@clawpump/claw-agent 0.1.5 → 0.1.6

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 (1212) hide show
  1. package/agent/.dockerignore +67 -0
  2. package/agent/.envrc +1 -1
  3. package/agent/.gitattributes +8 -0
  4. package/agent/AGENTS.md +216 -4
  5. package/agent/CONTRIBUTING.md +46 -8
  6. package/agent/Dockerfile +78 -35
  7. package/agent/MANIFEST.in +2 -0
  8. package/agent/README.md +12 -5
  9. package/agent/README.ur-pk.md +261 -0
  10. package/agent/README.zh-CN.md +11 -8
  11. package/agent/SECURITY.md +5 -4
  12. package/agent/acp_adapter/provenance.py +127 -0
  13. package/agent/acp_adapter/server.py +112 -5
  14. package/agent/acp_adapter/session.py +1 -6
  15. package/agent/acp_registry/agent.json +2 -2
  16. package/agent/agent/account_usage.py +313 -1
  17. package/agent/agent/agent_init.py +140 -37
  18. package/agent/agent/agent_runtime_helpers.py +342 -83
  19. package/agent/agent/anthropic_adapter.py +320 -33
  20. package/agent/agent/auxiliary_client.py +525 -105
  21. package/agent/agent/background_review.py +157 -19
  22. package/agent/agent/bedrock_adapter.py +71 -6
  23. package/agent/agent/billing_view.py +295 -0
  24. package/agent/agent/chat_completion_helpers.py +229 -4
  25. package/agent/agent/codex_responses_adapter.py +86 -10
  26. package/agent/agent/codex_runtime.py +153 -1
  27. package/agent/agent/coding_context.py +738 -0
  28. package/agent/agent/context_compressor.py +392 -44
  29. package/agent/agent/context_references.py +34 -1
  30. package/agent/agent/conversation_compression.py +159 -22
  31. package/agent/agent/conversation_loop.py +643 -908
  32. package/agent/agent/copilot_acp_client.py +4 -11
  33. package/agent/agent/credential_pool.py +5 -3
  34. package/agent/agent/credits_tracker.py +794 -0
  35. package/agent/agent/curator.py +91 -18
  36. package/agent/agent/curator_backup.py +26 -10
  37. package/agent/agent/display.py +42 -1
  38. package/agent/agent/error_classifier.py +52 -3
  39. package/agent/agent/errors.py +3 -0
  40. package/agent/agent/file_safety.py +0 -17
  41. package/agent/agent/gemini_native_adapter.py +31 -1
  42. package/agent/agent/i18n.py +48 -4
  43. package/agent/agent/image_gen_provider.py +74 -5
  44. package/agent/agent/image_routing.py +29 -0
  45. package/agent/agent/insights.py +8 -17
  46. package/agent/agent/lsp/install.py +3 -0
  47. package/agent/agent/memory_manager.py +326 -31
  48. package/agent/agent/message_content.py +50 -0
  49. package/agent/agent/model_metadata.py +214 -3
  50. package/agent/agent/moonshot_schema.py +8 -1
  51. package/agent/agent/onboarding.py +60 -0
  52. package/agent/agent/prompt_builder.py +327 -37
  53. package/agent/agent/redact.py +1 -0
  54. package/agent/agent/runtime_cwd.py +34 -5
  55. package/agent/agent/secret_scope.py +205 -0
  56. package/agent/agent/secret_sources/bitwarden.py +34 -2
  57. package/agent/agent/skill_commands.py +90 -1
  58. package/agent/agent/skill_preprocessing.py +1 -0
  59. package/agent/agent/skill_utils.py +209 -36
  60. package/agent/agent/ssl_guard.py +94 -0
  61. package/agent/agent/system_prompt.py +133 -5
  62. package/agent/agent/tool_executor.py +496 -70
  63. package/agent/agent/transports/anthropic.py +83 -21
  64. package/agent/agent/transports/chat_completions.py +94 -5
  65. package/agent/agent/transports/codex.py +67 -2
  66. package/agent/agent/transports/codex_app_server.py +1 -0
  67. package/agent/agent/transports/codex_app_server_session.py +30 -0
  68. package/agent/agent/transports/types.py +12 -0
  69. package/agent/agent/turn_context.py +408 -0
  70. package/agent/agent/turn_finalizer.py +428 -0
  71. package/agent/agent/turn_retry_state.py +68 -0
  72. package/agent/agent/usage_pricing.py +3 -0
  73. package/agent/apps/bootstrap-installer/package.json +6 -5
  74. package/agent/apps/bootstrap-installer/src/routes/failure.tsx +12 -5
  75. package/agent/apps/bootstrap-installer/src/routes/progress.tsx +1 -3
  76. package/agent/apps/bootstrap-installer/src/store.ts +3 -2
  77. package/agent/apps/bootstrap-installer/src-tauri/src/bootstrap.rs +172 -7
  78. package/agent/apps/bootstrap-installer/src-tauri/src/events.rs +14 -1
  79. package/agent/apps/bootstrap-installer/src-tauri/src/paths.rs +29 -0
  80. package/agent/apps/bootstrap-installer/src-tauri/src/powershell.rs +93 -3
  81. package/agent/apps/bootstrap-installer/src-tauri/src/update.rs +695 -39
  82. package/agent/apps/bootstrap-installer/tsconfig.json +3 -4
  83. package/agent/apps/desktop/DESIGN.md +167 -0
  84. package/agent/apps/desktop/README.md +20 -16
  85. package/agent/apps/desktop/assets/icon.icns +0 -0
  86. package/agent/apps/desktop/assets/icon.ico +0 -0
  87. package/agent/apps/desktop/assets/icon.png +0 -0
  88. package/agent/apps/desktop/electron/backend-env.cjs +112 -0
  89. package/agent/apps/desktop/electron/backend-env.test.cjs +111 -0
  90. package/agent/apps/desktop/electron/backend-probes.test.cjs +3 -1
  91. package/agent/apps/desktop/electron/backend-ready.cjs +66 -0
  92. package/agent/apps/desktop/electron/bootstrap-platform.cjs +52 -0
  93. package/agent/apps/desktop/electron/bootstrap-platform.test.cjs +59 -1
  94. package/agent/apps/desktop/electron/bootstrap-runner.cjs +176 -38
  95. package/agent/apps/desktop/electron/bootstrap-runner.test.cjs +112 -1
  96. package/agent/apps/desktop/electron/connection-config.cjs +288 -0
  97. package/agent/apps/desktop/electron/connection-config.test.cjs +396 -0
  98. package/agent/apps/desktop/electron/dashboard-token.cjs +99 -0
  99. package/agent/apps/desktop/electron/dashboard-token.test.cjs +142 -0
  100. package/agent/apps/desktop/electron/desktop-uninstall.cjs +232 -0
  101. package/agent/apps/desktop/electron/desktop-uninstall.test.cjs +246 -0
  102. package/agent/apps/desktop/electron/entitlements.mac.inherit.plist +2 -0
  103. package/agent/apps/desktop/electron/fs-read-dir.cjs +109 -0
  104. package/agent/apps/desktop/electron/fs-read-dir.test.cjs +364 -0
  105. package/agent/apps/desktop/electron/gateway-ws-probe.cjs +188 -0
  106. package/agent/apps/desktop/electron/gateway-ws-probe.test.cjs +122 -0
  107. package/agent/apps/desktop/electron/git-root.cjs +54 -0
  108. package/agent/apps/desktop/electron/git-root.test.cjs +40 -0
  109. package/agent/apps/desktop/electron/git-worktrees.cjs +174 -0
  110. package/agent/apps/desktop/electron/hardening.cjs +123 -28
  111. package/agent/apps/desktop/electron/hardening.test.cjs +163 -0
  112. package/agent/apps/desktop/electron/main.cjs +3121 -331
  113. package/agent/apps/desktop/electron/oauth-net-request.cjs +20 -0
  114. package/agent/apps/desktop/electron/oauth-net-request.test.cjs +34 -0
  115. package/agent/apps/desktop/electron/preload.cjs +52 -2
  116. package/agent/apps/desktop/electron/session-windows.cjs +124 -0
  117. package/agent/apps/desktop/electron/session-windows.test.cjs +199 -0
  118. package/agent/apps/desktop/electron/update-rebuild.cjs +29 -0
  119. package/agent/apps/desktop/electron/update-rebuild.test.cjs +55 -0
  120. package/agent/apps/desktop/electron/update-remote.cjs +56 -0
  121. package/agent/apps/desktop/electron/update-remote.test.cjs +78 -0
  122. package/agent/apps/desktop/electron/vscode-marketplace.cjs +331 -0
  123. package/agent/apps/desktop/electron/vscode-marketplace.test.cjs +113 -0
  124. package/agent/apps/desktop/electron/windows-child-process.test.cjs +57 -0
  125. package/agent/apps/desktop/electron/windows-user-env.cjs +76 -0
  126. package/agent/apps/desktop/electron/windows-user-env.test.cjs +90 -0
  127. package/agent/apps/desktop/electron/workspace-cwd.cjs +38 -0
  128. package/agent/apps/desktop/electron/workspace-cwd.test.cjs +45 -0
  129. package/agent/apps/desktop/eslint.config.mjs +0 -3
  130. package/agent/apps/desktop/index.html +27 -2
  131. package/agent/apps/desktop/package.json +31 -11
  132. package/agent/apps/desktop/pr-assets/session-source-folders.png +0 -0
  133. package/agent/apps/desktop/public/apple-touch-icon.png +0 -0
  134. package/agent/apps/desktop/public/nous-girl.jpg +0 -0
  135. package/agent/apps/desktop/scripts/assert-dist-built.cjs +70 -0
  136. package/agent/apps/desktop/scripts/assert-dist-built.test.cjs +84 -0
  137. package/agent/apps/desktop/scripts/before-pack.cjs +78 -0
  138. package/agent/apps/desktop/scripts/before-pack.test.cjs +53 -0
  139. package/agent/apps/desktop/scripts/diag-scroll-reset.mjs +229 -0
  140. package/agent/apps/desktop/scripts/patch-electron-builder-mac-binary.cjs +64 -0
  141. package/agent/apps/desktop/scripts/run-electron-builder.cjs +57 -0
  142. package/agent/apps/desktop/src/app/agents/index.tsx +53 -45
  143. package/agent/apps/desktop/src/app/artifacts/index.tsx +102 -83
  144. package/agent/apps/desktop/src/app/chat/chat-drop-overlay.tsx +29 -8
  145. package/agent/apps/desktop/src/app/chat/chat-swap-overlay.tsx +47 -0
  146. package/agent/apps/desktop/src/app/chat/composer/attachments.tsx +81 -45
  147. package/agent/apps/desktop/src/app/chat/composer/completion-drawer.tsx +13 -24
  148. package/agent/apps/desktop/src/app/chat/composer/context-menu.tsx +138 -88
  149. package/agent/apps/desktop/src/app/chat/composer/controls.tsx +138 -90
  150. package/agent/apps/desktop/src/app/chat/composer/enter-submit-dom-race.test.tsx +218 -0
  151. package/agent/apps/desktop/src/app/chat/composer/focus.ts +32 -0
  152. package/agent/apps/desktop/src/app/chat/composer/help-hint.tsx +38 -25
  153. package/agent/apps/desktop/src/app/chat/composer/hooks/use-live-completion-adapter.ts +7 -0
  154. package/agent/apps/desktop/src/app/chat/composer/hooks/use-mic-recorder.ts +22 -12
  155. package/agent/apps/desktop/src/app/chat/composer/hooks/use-slash-completions.ts +142 -14
  156. package/agent/apps/desktop/src/app/chat/composer/hooks/use-voice-conversation.ts +14 -11
  157. package/agent/apps/desktop/src/app/chat/composer/hooks/use-voice-recorder.ts +9 -6
  158. package/agent/apps/desktop/src/app/chat/composer/ime-composition-dom-repro.test.tsx +108 -0
  159. package/agent/apps/desktop/src/app/chat/composer/index.tsx +930 -180
  160. package/agent/apps/desktop/src/app/chat/composer/inline-refs.ts +136 -32
  161. package/agent/apps/desktop/src/app/chat/composer/model-pill.tsx +86 -0
  162. package/agent/apps/desktop/src/app/chat/composer/queue-panel.tsx +54 -75
  163. package/agent/apps/desktop/src/app/chat/composer/rich-editor.test.ts +117 -1
  164. package/agent/apps/desktop/src/app/chat/composer/rich-editor.ts +117 -6
  165. package/agent/apps/desktop/src/app/chat/composer/slash-nav-dom-repro.test.tsx +186 -0
  166. package/agent/apps/desktop/src/app/chat/composer/status-stack/index.tsx +202 -0
  167. package/agent/apps/desktop/src/app/chat/composer/status-stack/status-row.tsx +155 -0
  168. package/agent/apps/desktop/src/app/chat/composer/text-utils.test.ts +104 -0
  169. package/agent/apps/desktop/src/app/chat/composer/text-utils.ts +37 -9
  170. package/agent/apps/desktop/src/app/chat/composer/trigger-popover.test.tsx +50 -0
  171. package/agent/apps/desktop/src/app/chat/composer/trigger-popover.tsx +105 -40
  172. package/agent/apps/desktop/src/app/chat/composer/types.ts +5 -0
  173. package/agent/apps/desktop/src/app/chat/composer/url-dialog.tsx +11 -15
  174. package/agent/apps/desktop/src/app/chat/composer/voice-activity.tsx +8 -4
  175. package/agent/apps/desktop/src/app/chat/hooks/use-composer-actions.test.ts +57 -0
  176. package/agent/apps/desktop/src/app/chat/hooks/use-composer-actions.ts +70 -16
  177. package/agent/apps/desktop/src/app/chat/hooks/use-file-drop-zone.ts +52 -16
  178. package/agent/apps/desktop/src/app/chat/index.tsx +234 -81
  179. package/agent/apps/desktop/src/app/chat/perf-probe.tsx +69 -21
  180. package/agent/apps/desktop/src/app/chat/right-rail/preview-console.tsx +44 -40
  181. package/agent/apps/desktop/src/app/chat/right-rail/preview-file.tsx +71 -25
  182. package/agent/apps/desktop/src/app/chat/right-rail/preview-pane.test.tsx +40 -1
  183. package/agent/apps/desktop/src/app/chat/right-rail/preview-pane.tsx +55 -53
  184. package/agent/apps/desktop/src/app/chat/right-rail/preview.tsx +35 -17
  185. package/agent/apps/desktop/src/app/chat/scroll-to-bottom-button.test.tsx +67 -0
  186. package/agent/apps/desktop/src/app/chat/scroll-to-bottom-button.tsx +74 -0
  187. package/agent/apps/desktop/src/app/chat/sidebar/cron-jobs-section.tsx +356 -0
  188. package/agent/apps/desktop/src/app/chat/sidebar/index.tsx +1189 -364
  189. package/agent/apps/desktop/src/app/chat/sidebar/load-more-row.tsx +30 -0
  190. package/agent/apps/desktop/src/app/chat/sidebar/order.test.ts +21 -0
  191. package/agent/apps/desktop/src/app/chat/sidebar/order.ts +17 -0
  192. package/agent/apps/desktop/src/app/chat/sidebar/profile-switcher.tsx +524 -0
  193. package/agent/apps/desktop/src/app/chat/sidebar/session-actions-menu.tsx +80 -45
  194. package/agent/apps/desktop/src/app/chat/sidebar/session-row.tsx +120 -25
  195. package/agent/apps/desktop/src/app/chat/sidebar/virtual-session-list.tsx +7 -13
  196. package/agent/apps/desktop/src/app/chat/sidebar/workspace-groups.test.ts +149 -0
  197. package/agent/apps/desktop/src/app/chat/sidebar/workspace-groups.ts +326 -0
  198. package/agent/apps/desktop/src/app/chat/thread-loading.ts +7 -2
  199. package/agent/apps/desktop/src/app/command-center/index.tsx +320 -581
  200. package/agent/apps/desktop/src/app/command-palette/index.tsx +681 -0
  201. package/agent/apps/desktop/src/app/command-palette/marketplace-theme-page.tsx +157 -0
  202. package/agent/apps/desktop/src/app/cron/index.tsx +392 -324
  203. package/agent/apps/desktop/src/app/cron/job-state.ts +29 -0
  204. package/agent/apps/desktop/src/app/desktop-controller.tsx +618 -123
  205. package/agent/apps/desktop/src/app/floating-hud.ts +22 -0
  206. package/agent/apps/desktop/src/app/gateway/hooks/use-gateway-boot.test.tsx +265 -0
  207. package/agent/apps/desktop/src/app/gateway/hooks/use-gateway-boot.ts +260 -14
  208. package/agent/apps/desktop/src/app/gateway/hooks/use-gateway-request.ts +48 -4
  209. package/agent/apps/desktop/src/app/hooks/use-keybinds.ts +270 -0
  210. package/agent/apps/desktop/src/app/hooks/use-refresh-hotkey.ts +45 -0
  211. package/agent/apps/desktop/src/app/layout-constants.ts +19 -0
  212. package/agent/apps/desktop/src/app/messaging/index.tsx +136 -241
  213. package/agent/apps/desktop/src/app/messaging/platform-icon.tsx +95 -0
  214. package/agent/apps/desktop/src/app/model-visibility-overlay.tsx +31 -0
  215. package/agent/apps/desktop/src/app/overlays/overlay-search-input.tsx +18 -62
  216. package/agent/apps/desktop/src/app/overlays/overlay-split-layout.tsx +59 -7
  217. package/agent/apps/desktop/src/app/overlays/overlay-view.tsx +9 -5
  218. package/agent/apps/desktop/src/app/page-search-shell.tsx +42 -20
  219. package/agent/apps/desktop/src/app/profiles/create-profile-dialog.tsx +165 -0
  220. package/agent/apps/desktop/src/app/profiles/delete-profile-dialog.tsx +65 -0
  221. package/agent/apps/desktop/src/app/profiles/index.tsx +174 -199
  222. package/agent/apps/desktop/src/app/profiles/rename-profile-dialog.tsx +125 -0
  223. package/agent/apps/desktop/src/app/right-sidebar/files/dnd-manager.ts +27 -0
  224. package/agent/apps/desktop/src/app/right-sidebar/files/ipc.test.ts +100 -0
  225. package/agent/apps/desktop/src/app/right-sidebar/files/ipc.ts +12 -18
  226. package/agent/apps/desktop/src/app/right-sidebar/files/remote-picker.tsx +177 -0
  227. package/agent/apps/desktop/src/app/right-sidebar/files/tree.tsx +35 -21
  228. package/agent/apps/desktop/src/app/right-sidebar/files/use-project-tree.test.ts +75 -3
  229. package/agent/apps/desktop/src/app/right-sidebar/files/use-project-tree.ts +152 -5
  230. package/agent/apps/desktop/src/app/right-sidebar/index.test.tsx +75 -0
  231. package/agent/apps/desktop/src/app/right-sidebar/index.tsx +166 -129
  232. package/agent/apps/desktop/src/app/right-sidebar/store.ts +19 -4
  233. package/agent/apps/desktop/src/app/right-sidebar/terminal/buffer.ts +65 -0
  234. package/agent/apps/desktop/src/app/right-sidebar/terminal/index.tsx +29 -34
  235. package/agent/apps/desktop/src/app/right-sidebar/terminal/persistent.tsx +18 -6
  236. package/agent/apps/desktop/src/app/right-sidebar/terminal/selection.ts +93 -32
  237. package/agent/apps/desktop/src/app/right-sidebar/terminal/use-terminal-session.ts +381 -119
  238. package/agent/apps/desktop/src/app/routes.ts +9 -0
  239. package/agent/apps/desktop/src/app/session/hooks/use-cwd-actions.ts +17 -7
  240. package/agent/apps/desktop/src/app/session/hooks/use-message-stream.ts +365 -47
  241. package/agent/apps/desktop/src/app/session/hooks/use-model-controls.test.tsx +198 -0
  242. package/agent/apps/desktop/src/app/session/hooks/use-model-controls.ts +70 -34
  243. package/agent/apps/desktop/src/app/session/hooks/use-prompt-actions.test.tsx +1061 -0
  244. package/agent/apps/desktop/src/app/session/hooks/use-prompt-actions.ts +1143 -165
  245. package/agent/apps/desktop/src/app/session/hooks/use-route-resume.test.tsx +341 -2
  246. package/agent/apps/desktop/src/app/session/hooks/use-route-resume.ts +176 -5
  247. package/agent/apps/desktop/src/app/session/hooks/use-session-actions.test.tsx +259 -0
  248. package/agent/apps/desktop/src/app/session/hooks/use-session-actions.ts +452 -149
  249. package/agent/apps/desktop/src/app/session/hooks/use-session-state-cache.test.tsx +327 -0
  250. package/agent/apps/desktop/src/app/session/hooks/use-session-state-cache.ts +133 -4
  251. package/agent/apps/desktop/src/app/session-picker-overlay.tsx +32 -0
  252. package/agent/apps/desktop/src/app/session-switcher.tsx +107 -0
  253. package/agent/apps/desktop/src/app/settings/about-settings.tsx +45 -36
  254. package/agent/apps/desktop/src/app/settings/appearance-settings.tsx +243 -162
  255. package/agent/apps/desktop/src/app/settings/config-settings.tsx +86 -66
  256. package/agent/apps/desktop/src/app/settings/constants.ts +459 -122
  257. package/agent/apps/desktop/src/app/settings/credential-key-ui.tsx +373 -0
  258. package/agent/apps/desktop/src/app/settings/env-credentials.tsx +198 -0
  259. package/agent/apps/desktop/src/app/settings/env-var-actions-menu.tsx +136 -0
  260. package/agent/apps/desktop/src/app/settings/field-copy.ts +56 -0
  261. package/agent/apps/desktop/src/app/settings/gateway-settings.tsx +385 -72
  262. package/agent/apps/desktop/src/app/settings/helpers.test.ts +156 -1
  263. package/agent/apps/desktop/src/app/settings/helpers.ts +30 -2
  264. package/agent/apps/desktop/src/app/settings/index.tsx +118 -84
  265. package/agent/apps/desktop/src/app/settings/keys-settings.tsx +62 -419
  266. package/agent/apps/desktop/src/app/settings/mcp-settings.tsx +65 -60
  267. package/agent/apps/desktop/src/app/settings/model-settings.test.tsx +129 -5
  268. package/agent/apps/desktop/src/app/settings/model-settings.tsx +370 -65
  269. package/agent/apps/desktop/src/app/settings/notifications-settings.tsx +150 -0
  270. package/agent/apps/desktop/src/app/settings/primitives.tsx +5 -11
  271. package/agent/apps/desktop/src/app/settings/provider-config-panel.test.tsx +142 -0
  272. package/agent/apps/desktop/src/app/settings/provider-config-panel.tsx +182 -0
  273. package/agent/apps/desktop/src/app/settings/providers-settings.test.tsx +171 -0
  274. package/agent/apps/desktop/src/app/settings/providers-settings.tsx +471 -0
  275. package/agent/apps/desktop/src/app/settings/sessions-settings.tsx +183 -71
  276. package/agent/apps/desktop/src/app/settings/toolset-config-panel.test.tsx +135 -1
  277. package/agent/apps/desktop/src/app/settings/toolset-config-panel.tsx +180 -57
  278. package/agent/apps/desktop/src/app/settings/types.ts +9 -6
  279. package/agent/apps/desktop/src/app/settings/uninstall-section.tsx +185 -0
  280. package/agent/apps/desktop/src/app/settings/use-deep-link-highlight.ts +60 -0
  281. package/agent/apps/desktop/src/app/shell/app-shell.tsx +59 -13
  282. package/agent/apps/desktop/src/app/shell/gateway-menu-panel.tsx +37 -32
  283. package/agent/apps/desktop/src/app/shell/hooks/use-overlay-routing.ts +6 -3
  284. package/agent/apps/desktop/src/app/shell/hooks/use-statusbar-items.tsx +212 -53
  285. package/agent/apps/desktop/src/app/shell/keybind-panel.tsx +215 -0
  286. package/agent/apps/desktop/src/app/shell/model-edit-submenu.test.tsx +84 -0
  287. package/agent/apps/desktop/src/app/shell/model-edit-submenu.tsx +244 -0
  288. package/agent/apps/desktop/src/app/shell/model-menu-panel.tsx +392 -0
  289. package/agent/apps/desktop/src/app/shell/statusbar-controls.tsx +23 -33
  290. package/agent/apps/desktop/src/app/shell/titlebar-controls.tsx +79 -95
  291. package/agent/apps/desktop/src/app/shell/titlebar.ts +8 -2
  292. package/agent/apps/desktop/src/app/skills/index.test.tsx +11 -0
  293. package/agent/apps/desktop/src/app/skills/index.tsx +79 -64
  294. package/agent/apps/desktop/src/app/types.ts +85 -0
  295. package/agent/apps/desktop/src/app/updates-overlay.tsx +110 -105
  296. package/agent/apps/desktop/src/components/assistant-ui/ansi-text.tsx +34 -0
  297. package/agent/apps/desktop/src/components/assistant-ui/block-direction.test.tsx +129 -0
  298. package/agent/apps/desktop/src/components/assistant-ui/clarify-tool.tsx +102 -81
  299. package/agent/apps/desktop/src/components/assistant-ui/directive-text.tsx +92 -15
  300. package/agent/apps/desktop/src/components/assistant-ui/markdown-text.test.ts +38 -0
  301. package/agent/apps/desktop/src/components/assistant-ui/markdown-text.tsx +304 -45
  302. package/agent/apps/desktop/src/components/assistant-ui/message-render-boundary.test.tsx +80 -0
  303. package/agent/apps/desktop/src/components/assistant-ui/message-render-boundary.tsx +48 -0
  304. package/agent/apps/desktop/src/components/assistant-ui/streaming.test.tsx +142 -90
  305. package/agent/apps/desktop/src/components/assistant-ui/thread-list.tsx +337 -0
  306. package/agent/apps/desktop/src/components/assistant-ui/thread.tsx +667 -190
  307. package/agent/apps/desktop/src/components/assistant-ui/tool-approval-group.test.tsx +299 -0
  308. package/agent/apps/desktop/src/components/assistant-ui/tool-approval.test.tsx +133 -0
  309. package/agent/apps/desktop/src/components/assistant-ui/tool-approval.tsx +239 -0
  310. package/agent/apps/desktop/src/components/assistant-ui/tool-fallback-model.test.ts +31 -0
  311. package/agent/apps/desktop/src/components/assistant-ui/tool-fallback-model.ts +152 -134
  312. package/agent/apps/desktop/src/components/assistant-ui/tool-fallback.tsx +142 -150
  313. package/agent/apps/desktop/src/components/assistant-ui/tooltip-icon-button.tsx +14 -12
  314. package/agent/apps/desktop/src/components/assistant-ui/user-message-edit.test.tsx +141 -0
  315. package/agent/apps/desktop/src/components/assistant-ui/user-message-text.tsx +152 -0
  316. package/agent/apps/desktop/src/components/boot-failure-overlay.tsx +150 -33
  317. package/agent/apps/desktop/src/components/boot-failure-reauth.test.ts +100 -0
  318. package/agent/apps/desktop/src/components/boot-failure-reauth.ts +81 -0
  319. package/agent/apps/desktop/src/components/brand-mark.tsx +19 -0
  320. package/agent/apps/desktop/src/components/chat/code-card.tsx +1 -1
  321. package/agent/apps/desktop/src/components/chat/composer-dock.ts +31 -0
  322. package/agent/apps/desktop/src/components/chat/diff-lines.tsx +1 -1
  323. package/agent/apps/desktop/src/components/chat/disclosure-row.tsx +13 -3
  324. package/agent/apps/desktop/src/components/chat/expandable-block.tsx +52 -0
  325. package/agent/apps/desktop/src/components/chat/generated-image-result.tsx +174 -0
  326. package/agent/apps/desktop/src/components/chat/image-generation-placeholder.tsx +70 -37
  327. package/agent/apps/desktop/src/components/chat/intro.tsx +8 -7
  328. package/agent/apps/desktop/src/components/chat/preview-attachment.tsx +4 -2
  329. package/agent/apps/desktop/src/components/chat/shiki-highlighter.test.ts +37 -0
  330. package/agent/apps/desktop/src/components/chat/shiki-highlighter.tsx +96 -22
  331. package/agent/apps/desktop/src/components/chat/status-row.tsx +70 -0
  332. package/agent/apps/desktop/src/components/chat/status-section.tsx +42 -0
  333. package/agent/apps/desktop/src/components/chat/terminal-output.tsx +54 -0
  334. package/agent/apps/desktop/src/components/chat/zoomable-image.tsx +70 -109
  335. package/agent/apps/desktop/src/components/desktop-install-overlay.tsx +154 -84
  336. package/agent/apps/desktop/src/components/desktop-onboarding-overlay.test.tsx +38 -8
  337. package/agent/apps/desktop/src/components/desktop-onboarding-overlay.tsx +789 -233
  338. package/agent/apps/desktop/src/components/error-boundary.tsx +77 -0
  339. package/agent/apps/desktop/src/components/gateway-connecting-overlay.test.tsx +144 -0
  340. package/agent/apps/desktop/src/components/gateway-connecting-overlay.tsx +7 -1
  341. package/agent/apps/desktop/src/components/haptics-provider.tsx +24 -0
  342. package/agent/apps/desktop/src/components/language-switcher.test.tsx +53 -0
  343. package/agent/apps/desktop/src/components/language-switcher.tsx +175 -0
  344. package/agent/apps/desktop/src/components/model-picker.tsx +42 -40
  345. package/agent/apps/desktop/src/components/model-visibility-dialog.tsx +166 -0
  346. package/agent/apps/desktop/src/components/notifications.tsx +48 -27
  347. package/agent/apps/desktop/src/components/pane-shell/index.ts +1 -1
  348. package/agent/apps/desktop/src/components/pane-shell/pane-shell.tsx +146 -9
  349. package/agent/apps/desktop/src/components/prompt-overlays.tsx +234 -0
  350. package/agent/apps/desktop/src/components/session-picker.tsx +108 -0
  351. package/agent/apps/desktop/src/components/ui/action-status.tsx +25 -0
  352. package/agent/apps/desktop/src/components/ui/badge.tsx +35 -0
  353. package/agent/apps/desktop/src/components/ui/button.tsx +37 -13
  354. package/agent/apps/desktop/src/components/ui/confirm-dialog.tsx +109 -0
  355. package/agent/apps/desktop/src/components/ui/control.ts +25 -0
  356. package/agent/apps/desktop/src/components/ui/copy-button.test.tsx +36 -0
  357. package/agent/apps/desktop/src/components/ui/copy-button.tsx +38 -27
  358. package/agent/apps/desktop/src/components/ui/dialog.tsx +39 -11
  359. package/agent/apps/desktop/src/components/ui/dropdown-menu.tsx +98 -24
  360. package/agent/apps/desktop/src/components/ui/error-state.tsx +50 -0
  361. package/agent/apps/desktop/src/components/ui/fade-text.tsx +9 -2
  362. package/agent/apps/desktop/src/components/ui/{braille-spinner.tsx → glyph-spinner.tsx} +15 -13
  363. package/agent/apps/desktop/src/components/ui/input.tsx +5 -2
  364. package/agent/apps/desktop/src/components/ui/kbd.tsx +83 -12
  365. package/agent/apps/desktop/src/components/ui/log-view.tsx +19 -0
  366. package/agent/apps/desktop/src/components/ui/pagination.tsx +12 -5
  367. package/agent/apps/desktop/src/components/ui/popover.tsx +44 -0
  368. package/agent/apps/desktop/src/components/ui/search-field.tsx +80 -0
  369. package/agent/apps/desktop/src/components/ui/segmented-control.tsx +51 -0
  370. package/agent/apps/desktop/src/components/ui/select.tsx +10 -3
  371. package/agent/apps/desktop/src/components/ui/sheet.tsx +8 -2
  372. package/agent/apps/desktop/src/components/ui/sidebar.tsx +18 -25
  373. package/agent/apps/desktop/src/components/ui/switch.tsx +38 -15
  374. package/agent/apps/desktop/src/components/ui/textarea.tsx +4 -11
  375. package/agent/apps/desktop/src/components/ui/tool-icon.tsx +65 -0
  376. package/agent/apps/desktop/src/components/ui/tooltip.tsx +31 -4
  377. package/agent/apps/desktop/src/fonts/JetBrainsMono-Bold.woff2 +0 -0
  378. package/agent/apps/desktop/src/fonts/JetBrainsMono-Italic.woff2 +0 -0
  379. package/agent/apps/desktop/src/fonts/JetBrainsMono-Regular.woff2 +0 -0
  380. package/agent/apps/desktop/src/global.d.ts +181 -4
  381. package/agent/apps/desktop/src/hermes.test.ts +60 -0
  382. package/agent/apps/desktop/src/hermes.ts +190 -13
  383. package/agent/apps/desktop/src/hooks/use-image-download.ts +85 -0
  384. package/agent/apps/desktop/src/hooks/use-resize-observer.ts +13 -4
  385. package/agent/apps/desktop/src/hooks/use-worktree-info.ts +68 -0
  386. package/agent/apps/desktop/src/i18n/catalog.ts +12 -0
  387. package/agent/apps/desktop/src/i18n/context.test.tsx +232 -0
  388. package/agent/apps/desktop/src/i18n/context.tsx +183 -0
  389. package/agent/apps/desktop/src/i18n/define-locale.ts +41 -0
  390. package/agent/apps/desktop/src/i18n/en.ts +1921 -0
  391. package/agent/apps/desktop/src/i18n/index.ts +20 -0
  392. package/agent/apps/desktop/src/i18n/ja.ts +2053 -0
  393. package/agent/apps/desktop/src/i18n/languages.test.ts +43 -0
  394. package/agent/apps/desktop/src/i18n/languages.ts +86 -0
  395. package/agent/apps/desktop/src/i18n/runtime.test.ts +75 -0
  396. package/agent/apps/desktop/src/i18n/runtime.ts +53 -0
  397. package/agent/apps/desktop/src/i18n/types.ts +1559 -0
  398. package/agent/apps/desktop/src/i18n/zh-hant.ts +1992 -0
  399. package/agent/apps/desktop/src/i18n/zh.ts +2099 -0
  400. package/agent/apps/desktop/src/lib/ansi.test.ts +123 -0
  401. package/agent/apps/desktop/src/lib/ansi.ts +186 -0
  402. package/agent/apps/desktop/src/lib/chat-messages.test.ts +79 -0
  403. package/agent/apps/desktop/src/lib/chat-messages.ts +68 -29
  404. package/agent/apps/desktop/src/lib/chat-runtime.test.ts +65 -1
  405. package/agent/apps/desktop/src/lib/chat-runtime.ts +39 -3
  406. package/agent/apps/desktop/src/lib/completion-sound.ts +519 -0
  407. package/agent/apps/desktop/src/lib/desktop-fs.test.ts +116 -0
  408. package/agent/apps/desktop/src/lib/desktop-fs.ts +113 -0
  409. package/agent/apps/desktop/src/lib/desktop-slash-commands.test.ts +89 -6
  410. package/agent/apps/desktop/src/lib/desktop-slash-commands.ts +270 -131
  411. package/agent/apps/desktop/src/lib/external-link.test.tsx +27 -0
  412. package/agent/apps/desktop/src/lib/external-link.tsx +9 -2
  413. package/agent/apps/desktop/src/lib/gateway-events.test.ts +27 -0
  414. package/agent/apps/desktop/src/lib/gateway-events.ts +16 -0
  415. package/agent/apps/desktop/src/lib/gateway-ws-url.test.ts +78 -0
  416. package/agent/apps/desktop/src/lib/gateway-ws-url.ts +91 -0
  417. package/agent/apps/desktop/src/lib/generated-images.test.ts +97 -0
  418. package/agent/apps/desktop/src/lib/generated-images.ts +116 -0
  419. package/agent/apps/desktop/src/lib/haptics.ts +17 -0
  420. package/agent/apps/desktop/src/lib/icons.ts +10 -2
  421. package/agent/apps/desktop/src/lib/keybinds/actions.ts +137 -0
  422. package/agent/apps/desktop/src/lib/keybinds/combo.test.ts +86 -0
  423. package/agent/apps/desktop/src/lib/keybinds/combo.ts +195 -0
  424. package/agent/apps/desktop/src/lib/local-preview.ts +23 -2
  425. package/agent/apps/desktop/src/lib/markdown-preprocess.ts +20 -7
  426. package/agent/apps/desktop/src/lib/media.remote.test.ts +90 -0
  427. package/agent/apps/desktop/src/lib/media.ts +40 -1
  428. package/agent/apps/desktop/src/lib/model-status-label.test.ts +59 -0
  429. package/agent/apps/desktop/src/lib/model-status-label.ts +122 -0
  430. package/agent/apps/desktop/src/lib/mutable-ref.ts +6 -0
  431. package/agent/apps/desktop/src/lib/profile-color.ts +58 -0
  432. package/agent/apps/desktop/src/lib/query-client.ts +13 -0
  433. package/agent/apps/desktop/src/lib/remend-tail.test.ts +105 -0
  434. package/agent/apps/desktop/src/lib/remend-tail.ts +108 -0
  435. package/agent/apps/desktop/src/lib/session-export.ts +6 -3
  436. package/agent/apps/desktop/src/lib/session-ids.test.ts +44 -0
  437. package/agent/apps/desktop/src/lib/session-ids.ts +26 -0
  438. package/agent/apps/desktop/src/lib/session-search.test.ts +66 -0
  439. package/agent/apps/desktop/src/lib/session-search.ts +21 -0
  440. package/agent/apps/desktop/src/lib/session-source.ts +126 -0
  441. package/agent/apps/desktop/src/lib/storage.test.ts +25 -0
  442. package/agent/apps/desktop/src/lib/storage.ts +35 -1
  443. package/agent/apps/desktop/src/lib/todos.test.ts +46 -1
  444. package/agent/apps/desktop/src/lib/todos.ts +37 -0
  445. package/agent/apps/desktop/src/lib/tool-result-summary.ts +5 -1
  446. package/agent/apps/desktop/src/lib/update-copy.test.ts +38 -0
  447. package/agent/apps/desktop/src/lib/update-copy.ts +44 -0
  448. package/agent/apps/desktop/src/lib/use-enter-animation.ts +2 -2
  449. package/agent/apps/desktop/src/lib/yolo-session.ts +50 -0
  450. package/agent/apps/desktop/src/main.tsx +19 -19
  451. package/agent/apps/desktop/src/store/boot.ts +4 -3
  452. package/agent/apps/desktop/src/store/clarify.test.ts +81 -0
  453. package/agent/apps/desktop/src/store/clarify.ts +50 -13
  454. package/agent/apps/desktop/src/store/command-palette.ts +20 -0
  455. package/agent/apps/desktop/src/store/compaction.test.ts +53 -0
  456. package/agent/apps/desktop/src/store/compaction.ts +38 -0
  457. package/agent/apps/desktop/src/store/completion-sound.ts +32 -0
  458. package/agent/apps/desktop/src/store/composer-input-history.test.ts +147 -0
  459. package/agent/apps/desktop/src/store/composer-input-history.ts +158 -0
  460. package/agent/apps/desktop/src/store/composer-queue.test.ts +68 -0
  461. package/agent/apps/desktop/src/store/composer-queue.ts +76 -0
  462. package/agent/apps/desktop/src/store/composer-status.test.ts +99 -0
  463. package/agent/apps/desktop/src/store/composer-status.ts +277 -0
  464. package/agent/apps/desktop/src/store/composer.test.ts +106 -0
  465. package/agent/apps/desktop/src/store/composer.ts +116 -0
  466. package/agent/apps/desktop/src/store/cron.ts +19 -0
  467. package/agent/apps/desktop/src/store/gateway.ts +280 -6
  468. package/agent/apps/desktop/src/store/keybinds.ts +143 -0
  469. package/agent/apps/desktop/src/store/layout.ts +107 -9
  470. package/agent/apps/desktop/src/store/model-presets.test.ts +51 -0
  471. package/agent/apps/desktop/src/store/model-presets.ts +86 -0
  472. package/agent/apps/desktop/src/store/model-visibility.test.ts +99 -0
  473. package/agent/apps/desktop/src/store/model-visibility.ts +161 -0
  474. package/agent/apps/desktop/src/store/native-notifications.test.ts +192 -0
  475. package/agent/apps/desktop/src/store/native-notifications.ts +203 -0
  476. package/agent/apps/desktop/src/store/notifications.ts +10 -7
  477. package/agent/apps/desktop/src/store/onboarding.test.ts +271 -1
  478. package/agent/apps/desktop/src/store/onboarding.ts +268 -38
  479. package/agent/apps/desktop/src/store/preview.ts +10 -1
  480. package/agent/apps/desktop/src/store/profile.test.ts +89 -0
  481. package/agent/apps/desktop/src/store/profile.ts +395 -0
  482. package/agent/apps/desktop/src/store/prompts.test.ts +127 -0
  483. package/agent/apps/desktop/src/store/prompts.ts +117 -0
  484. package/agent/apps/desktop/src/store/session-switcher.test.ts +115 -0
  485. package/agent/apps/desktop/src/store/session-switcher.ts +128 -0
  486. package/agent/apps/desktop/src/store/session-sync.ts +25 -0
  487. package/agent/apps/desktop/src/store/session.test.ts +268 -2
  488. package/agent/apps/desktop/src/store/session.ts +392 -18
  489. package/agent/apps/desktop/src/store/subagents.ts +3 -0
  490. package/agent/apps/desktop/src/store/system-actions.ts +48 -0
  491. package/agent/apps/desktop/src/store/thread-scroll.ts +58 -5
  492. package/agent/apps/desktop/src/store/todos.test.ts +47 -0
  493. package/agent/apps/desktop/src/store/todos.ts +64 -0
  494. package/agent/apps/desktop/src/store/tool-dismiss.ts +45 -0
  495. package/agent/apps/desktop/src/store/translucency.ts +38 -0
  496. package/agent/apps/desktop/src/store/updates.test.ts +187 -2
  497. package/agent/apps/desktop/src/store/updates.ts +268 -18
  498. package/agent/apps/desktop/src/store/windows.test.ts +143 -0
  499. package/agent/apps/desktop/src/store/windows.ts +115 -0
  500. package/agent/apps/desktop/src/styles.css +510 -119
  501. package/agent/apps/desktop/src/themes/color.ts +142 -0
  502. package/agent/apps/desktop/src/themes/context.tsx +128 -75
  503. package/agent/apps/desktop/src/themes/install.test.ts +119 -0
  504. package/agent/apps/desktop/src/themes/install.ts +95 -0
  505. package/agent/apps/desktop/src/themes/presets.test.ts +33 -0
  506. package/agent/apps/desktop/src/themes/presets.ts +13 -4
  507. package/agent/apps/desktop/src/themes/profile-theme.test.ts +41 -0
  508. package/agent/apps/desktop/src/themes/types.ts +35 -0
  509. package/agent/apps/desktop/src/themes/user-themes.test.ts +63 -0
  510. package/agent/apps/desktop/src/themes/user-themes.ts +122 -0
  511. package/agent/apps/desktop/src/themes/vscode.test.ts +171 -0
  512. package/agent/apps/desktop/src/themes/vscode.ts +343 -0
  513. package/agent/apps/desktop/src/types/hermes.ts +138 -1
  514. package/agent/apps/desktop/tsconfig.json +2 -2
  515. package/agent/apps/desktop/vite.config.ts +18 -0
  516. package/agent/apps/shared/package.json +1 -1
  517. package/agent/apps/shared/src/json-rpc-gateway.ts +63 -2
  518. package/agent/apps/shared/tsconfig.json +2 -2
  519. package/agent/cli-config.yaml.example +78 -1
  520. package/agent/cli.py +2177 -3162
  521. package/agent/cron/blueprint_catalog.py +713 -0
  522. package/agent/cron/jobs.py +226 -110
  523. package/agent/cron/scheduler.py +468 -193
  524. package/agent/cron/scheduler_provider.py +177 -0
  525. package/agent/cron/scripts/__init__.py +1 -0
  526. package/agent/cron/scripts/classify_items.py +226 -0
  527. package/agent/cron/suggestion_catalog.py +154 -0
  528. package/agent/cron/suggestions.py +257 -0
  529. package/agent/docs/chronos-managed-cron-contract.md +196 -0
  530. package/agent/docs/design/profile-builder.md +146 -0
  531. package/agent/docs/middleware/README.md +260 -0
  532. package/agent/docs/observability/README.md +316 -0
  533. package/agent/docs/plans/2026-06-09-003-fix-telegram-stream-overflow-continuations-plan.md +240 -0
  534. package/agent/docs/rca-ssl-cacert-post-git-pull.md +54 -0
  535. package/agent/docs/relay-connector-contract.md +285 -0
  536. package/agent/gateway/authz_mixin.py +536 -0
  537. package/agent/gateway/channel_directory.py +65 -3
  538. package/agent/gateway/config.py +222 -12
  539. package/agent/gateway/display_config.py +10 -0
  540. package/agent/gateway/hooks.py +17 -0
  541. package/agent/gateway/kanban_watchers.py +1146 -0
  542. package/agent/gateway/message_timestamps.py +166 -0
  543. package/agent/gateway/platforms/ADDING_A_PLATFORM.md +29 -0
  544. package/agent/gateway/platforms/api_server.py +216 -38
  545. package/agent/gateway/platforms/base.py +210 -58
  546. package/agent/gateway/platforms/email.py +122 -12
  547. package/agent/gateway/platforms/feishu.py +80 -11
  548. package/agent/gateway/platforms/feishu_meeting_invite.py +212 -0
  549. package/agent/gateway/platforms/matrix.py +1498 -297
  550. package/agent/gateway/platforms/qqbot/adapter.py +6 -0
  551. package/agent/gateway/platforms/signal.py +8 -0
  552. package/agent/gateway/platforms/slack.py +308 -12
  553. package/agent/gateway/platforms/telegram.py +831 -24
  554. package/agent/gateway/platforms/webhook.py +109 -21
  555. package/agent/gateway/platforms/weixin.py +113 -2
  556. package/agent/gateway/platforms/whatsapp.py +94 -288
  557. package/agent/gateway/platforms/whatsapp_cloud.py +1956 -0
  558. package/agent/gateway/platforms/whatsapp_common.py +367 -0
  559. package/agent/gateway/platforms/yuanbao.py +608 -191
  560. package/agent/gateway/platforms/yuanbao_proto.py +232 -23
  561. package/agent/gateway/relay/__init__.py +375 -0
  562. package/agent/gateway/relay/adapter.py +222 -0
  563. package/agent/gateway/relay/auth.py +168 -0
  564. package/agent/gateway/relay/descriptor.py +118 -0
  565. package/agent/gateway/relay/transport.py +101 -0
  566. package/agent/gateway/relay/ws_transport.py +327 -0
  567. package/agent/gateway/response_filters.py +53 -0
  568. package/agent/gateway/rich_sent_store.py +80 -0
  569. package/agent/gateway/run.py +2940 -5001
  570. package/agent/gateway/session.py +109 -8
  571. package/agent/gateway/session_context.py +22 -4
  572. package/agent/gateway/slash_commands.py +3854 -0
  573. package/agent/gateway/status.py +141 -21
  574. package/agent/gateway/stream_consumer.py +288 -31
  575. package/agent/hermes-already-has-routines.md +1 -1
  576. package/agent/hermes_cli/__init__.py +62 -17
  577. package/agent/hermes_cli/_parser.py +30 -0
  578. package/agent/hermes_cli/_subprocess_compat.py +61 -0
  579. package/agent/hermes_cli/active_sessions.py +320 -0
  580. package/agent/hermes_cli/auth.py +707 -59
  581. package/agent/hermes_cli/auth_commands.py +39 -22
  582. package/agent/hermes_cli/backup.py +109 -7
  583. package/agent/hermes_cli/banner.py +88 -0
  584. package/agent/hermes_cli/blueprint_cmd.py +318 -0
  585. package/agent/hermes_cli/cli_agent_setup_mixin.py +684 -0
  586. package/agent/hermes_cli/cli_commands_mixin.py +2293 -0
  587. package/agent/hermes_cli/commands.py +215 -91
  588. package/agent/hermes_cli/config.py +967 -130
  589. package/agent/hermes_cli/container_boot.py +76 -11
  590. package/agent/hermes_cli/cron.py +5 -11
  591. package/agent/hermes_cli/curator.py +21 -0
  592. package/agent/hermes_cli/dashboard_auth/__init__.py +2 -0
  593. package/agent/hermes_cli/dashboard_auth/base.py +62 -0
  594. package/agent/hermes_cli/dashboard_auth/cookies.py +32 -19
  595. package/agent/hermes_cli/dashboard_auth/login_page.py +156 -6
  596. package/agent/hermes_cli/dashboard_auth/middleware.py +28 -4
  597. package/agent/hermes_cli/dashboard_auth/prefix.py +46 -2
  598. package/agent/hermes_cli/dashboard_auth/public_paths.py +6 -0
  599. package/agent/hermes_cli/dashboard_auth/routes.py +158 -2
  600. package/agent/hermes_cli/dashboard_auth/ws_tickets.py +85 -11
  601. package/agent/hermes_cli/dashboard_register.py +427 -0
  602. package/agent/hermes_cli/debug.py +155 -50
  603. package/agent/hermes_cli/doctor.py +255 -14
  604. package/agent/hermes_cli/dump.py +60 -6
  605. package/agent/hermes_cli/env_loader.py +33 -0
  606. package/agent/hermes_cli/gateway.py +755 -103
  607. package/agent/hermes_cli/gateway_enroll.py +250 -0
  608. package/agent/hermes_cli/gateway_windows.py +254 -11
  609. package/agent/hermes_cli/gui_uninstall.py +285 -0
  610. package/agent/hermes_cli/inventory.py +105 -4
  611. package/agent/hermes_cli/kanban.py +58 -71
  612. package/agent/hermes_cli/kanban_db.py +391 -14
  613. package/agent/hermes_cli/kanban_decompose.py +2 -2
  614. package/agent/hermes_cli/kanban_specify.py +3 -1
  615. package/agent/hermes_cli/logs.py +2 -0
  616. package/agent/hermes_cli/main.py +2889 -5287
  617. package/agent/hermes_cli/managed_scope.py +214 -0
  618. package/agent/hermes_cli/managed_uv.py +254 -0
  619. package/agent/hermes_cli/mcp_catalog.py +6 -3
  620. package/agent/hermes_cli/mcp_config.py +145 -21
  621. package/agent/hermes_cli/mcp_security.py +96 -0
  622. package/agent/hermes_cli/mcp_startup.py +32 -3
  623. package/agent/hermes_cli/memory_providers.py +149 -0
  624. package/agent/hermes_cli/memory_setup.py +97 -42
  625. package/agent/hermes_cli/middleware.py +313 -0
  626. package/agent/hermes_cli/model_catalog.py +31 -0
  627. package/agent/hermes_cli/model_cost_guard.py +134 -0
  628. package/agent/hermes_cli/model_normalize.py +2 -1
  629. package/agent/hermes_cli/model_setup_flows.py +2759 -0
  630. package/agent/hermes_cli/model_switch.py +242 -27
  631. package/agent/hermes_cli/models.py +284 -44
  632. package/agent/hermes_cli/nous_account.py +33 -6
  633. package/agent/hermes_cli/nous_billing.py +406 -0
  634. package/agent/hermes_cli/nous_subscription.py +202 -5
  635. package/agent/hermes_cli/platforms.py +1 -0
  636. package/agent/hermes_cli/plugins.py +218 -18
  637. package/agent/hermes_cli/plugins_cmd.py +249 -105
  638. package/agent/hermes_cli/portal_cli.py +56 -16
  639. package/agent/hermes_cli/profile_distribution.py +6 -1
  640. package/agent/hermes_cli/profiles.py +283 -32
  641. package/agent/hermes_cli/provider_catalog.py +170 -0
  642. package/agent/hermes_cli/providers.py +4 -1
  643. package/agent/hermes_cli/pty_bridge.py +53 -4
  644. package/agent/hermes_cli/runtime_provider.py +216 -34
  645. package/agent/hermes_cli/secret_prompt.py +4 -4
  646. package/agent/hermes_cli/secrets_cli.py +24 -0
  647. package/agent/hermes_cli/send_cmd.py +28 -2
  648. package/agent/hermes_cli/service_manager.py +166 -19
  649. package/agent/hermes_cli/session_listing.py +97 -0
  650. package/agent/hermes_cli/setup.py +158 -94
  651. package/agent/hermes_cli/setup_whatsapp_cloud.py +541 -0
  652. package/agent/hermes_cli/skills_config.py +8 -2
  653. package/agent/hermes_cli/skills_hub.py +149 -7
  654. package/agent/hermes_cli/status.py +2 -2
  655. package/agent/hermes_cli/subcommands/__init__.py +18 -0
  656. package/agent/hermes_cli/subcommands/_shared.py +29 -0
  657. package/agent/hermes_cli/subcommands/acp.py +52 -0
  658. package/agent/hermes_cli/subcommands/auth.py +109 -0
  659. package/agent/hermes_cli/subcommands/backup.py +38 -0
  660. package/agent/hermes_cli/subcommands/claw.py +92 -0
  661. package/agent/hermes_cli/subcommands/config.py +49 -0
  662. package/agent/hermes_cli/subcommands/cron.py +163 -0
  663. package/agent/hermes_cli/subcommands/dashboard.py +143 -0
  664. package/agent/hermes_cli/subcommands/debug.py +77 -0
  665. package/agent/hermes_cli/subcommands/doctor.py +35 -0
  666. package/agent/hermes_cli/subcommands/dump.py +28 -0
  667. package/agent/hermes_cli/subcommands/gateway.py +332 -0
  668. package/agent/hermes_cli/subcommands/gui.py +63 -0
  669. package/agent/hermes_cli/subcommands/hooks.py +77 -0
  670. package/agent/hermes_cli/subcommands/import_cmd.py +31 -0
  671. package/agent/hermes_cli/subcommands/insights.py +25 -0
  672. package/agent/hermes_cli/subcommands/login.py +78 -0
  673. package/agent/hermes_cli/subcommands/logout.py +28 -0
  674. package/agent/hermes_cli/subcommands/logs.py +78 -0
  675. package/agent/hermes_cli/subcommands/mcp.py +108 -0
  676. package/agent/hermes_cli/subcommands/memory.py +53 -0
  677. package/agent/hermes_cli/subcommands/model.py +72 -0
  678. package/agent/hermes_cli/subcommands/pairing.py +36 -0
  679. package/agent/hermes_cli/subcommands/plugins.py +94 -0
  680. package/agent/hermes_cli/subcommands/postinstall.py +23 -0
  681. package/agent/hermes_cli/subcommands/profile.py +203 -0
  682. package/agent/hermes_cli/subcommands/prompt_size.py +36 -0
  683. package/agent/hermes_cli/subcommands/security.py +62 -0
  684. package/agent/hermes_cli/subcommands/setup.py +58 -0
  685. package/agent/hermes_cli/subcommands/skills.py +298 -0
  686. package/agent/hermes_cli/subcommands/slack.py +60 -0
  687. package/agent/hermes_cli/subcommands/status.py +28 -0
  688. package/agent/hermes_cli/subcommands/tools.py +95 -0
  689. package/agent/hermes_cli/subcommands/uninstall.py +41 -0
  690. package/agent/hermes_cli/subcommands/update.py +70 -0
  691. package/agent/hermes_cli/subcommands/version.py +18 -0
  692. package/agent/hermes_cli/subcommands/webhook.py +76 -0
  693. package/agent/hermes_cli/subcommands/whatsapp.py +22 -0
  694. package/agent/hermes_cli/suggestions_cmd.py +153 -0
  695. package/agent/hermes_cli/telegram_managed_bot.py +358 -0
  696. package/agent/hermes_cli/tips.py +3 -4
  697. package/agent/hermes_cli/tools_config.py +155 -28
  698. package/agent/hermes_cli/uninstall.py +231 -35
  699. package/agent/hermes_cli/web_server.py +6188 -975
  700. package/agent/hermes_cli/win_pty_bridge.py +179 -0
  701. package/agent/hermes_cli/write_approval_commands.py +209 -0
  702. package/agent/hermes_constants.py +164 -33
  703. package/agent/hermes_logging.py +74 -2
  704. package/agent/hermes_state.py +919 -106
  705. package/agent/hermes_time.py +20 -0
  706. package/agent/locales/af.yaml +23 -0
  707. package/agent/locales/de.yaml +23 -0
  708. package/agent/locales/en.yaml +20 -0
  709. package/agent/locales/es.yaml +23 -0
  710. package/agent/locales/fr.yaml +23 -0
  711. package/agent/locales/ga.yaml +23 -0
  712. package/agent/locales/hu.yaml +23 -0
  713. package/agent/locales/it.yaml +23 -0
  714. package/agent/locales/ja.yaml +23 -0
  715. package/agent/locales/ko.yaml +23 -0
  716. package/agent/locales/pt.yaml +23 -0
  717. package/agent/locales/ru.yaml +23 -0
  718. package/agent/locales/tr.yaml +23 -0
  719. package/agent/locales/uk.yaml +23 -0
  720. package/agent/locales/zh-hant.yaml +23 -0
  721. package/agent/locales/zh.yaml +23 -0
  722. package/agent/model_tools.py +204 -40
  723. package/agent/optional-mcps/clawpump/manifest.yaml +4 -2
  724. package/agent/optional-mcps/clawpump-stdio/manifest.yaml +2 -0
  725. package/agent/optional-mcps/unreal-engine/manifest.yaml +54 -0
  726. package/agent/optional-skills/blockchain/hyperliquid/SKILL.md +2 -2
  727. package/agent/optional-skills/blockchain/hyperliquid/scripts/hyperliquid_client.py +1 -1
  728. package/agent/optional-skills/creative/kanban-video-orchestrator/SKILL.md +1 -1
  729. package/agent/optional-skills/creative/kanban-video-orchestrator/assets/setup.sh.tmpl +4 -3
  730. package/agent/optional-skills/creative/kanban-video-orchestrator/references/kanban-setup.md +6 -4
  731. package/agent/optional-skills/creative/kanban-video-orchestrator/references/tool-matrix.md +2 -2
  732. package/agent/{skills/software-development → optional-skills/devops}/hermes-s6-container-supervision/SKILL.md +2 -0
  733. package/agent/optional-skills/devops/watchers/SKILL.md +1 -1
  734. package/agent/optional-skills/devops/watchers/scripts/watch_github.py +2 -1
  735. package/agent/optional-skills/payments/mpp-agent/SKILL.md +124 -0
  736. package/agent/optional-skills/payments/stripe-link-cli/SKILL.md +184 -0
  737. package/agent/optional-skills/payments/stripe-projects/SKILL.md +120 -0
  738. package/agent/optional-skills/productivity/canvas/SKILL.md +1 -1
  739. package/agent/optional-skills/productivity/canvas/scripts/canvas_api.py +4 -1
  740. package/agent/optional-skills/productivity/shop/SKILL.md +224 -0
  741. package/agent/optional-skills/productivity/shop/references/catalog-mcp.md +236 -0
  742. package/agent/optional-skills/productivity/shop/references/direct-api.md +278 -0
  743. package/agent/optional-skills/productivity/shop/references/legal.md +3 -0
  744. package/agent/optional-skills/productivity/shop/references/safety.md +36 -0
  745. package/agent/optional-skills/productivity/shopify/SKILL.md +1 -1
  746. package/agent/optional-skills/productivity/siyuan/SKILL.md +1 -1
  747. package/agent/optional-skills/productivity/telephony/SKILL.md +4 -4
  748. package/agent/optional-skills/productivity/telephony/scripts/telephony.py +15 -15
  749. package/agent/optional-skills/security/1password/SKILL.md +1 -1
  750. package/agent/{skills/red-teaming → optional-skills/security}/godmode/SKILL.md +3 -4
  751. package/agent/{skills/red-teaming → optional-skills/security}/godmode/scripts/auto_jailbreak.py +3 -1
  752. package/agent/optional-skills/software-development/rest-graphql-debug/SKILL.md +1 -1
  753. package/agent/{skills → optional-skills}/software-development/subagent-driven-development/SKILL.md +5 -5
  754. package/agent/package-lock.json +4082 -7907
  755. package/agent/package.json +18 -3
  756. package/agent/plugins/browser/firecrawl/provider.py +4 -1
  757. package/agent/plugins/cron/__init__.py +344 -0
  758. package/agent/plugins/cron/chronos/__init__.py +241 -0
  759. package/agent/plugins/cron/chronos/_nas_client.py +123 -0
  760. package/agent/plugins/cron/chronos/plugin.yaml +9 -0
  761. package/agent/plugins/cron/chronos/verify.py +103 -0
  762. package/agent/plugins/dashboard_auth/basic/__init__.py +491 -0
  763. package/agent/plugins/dashboard_auth/basic/plugin.yaml +7 -0
  764. package/agent/plugins/dashboard_auth/nous/__init__.py +12 -14
  765. package/agent/plugins/dashboard_auth/self_hosted/__init__.py +736 -0
  766. package/agent/plugins/dashboard_auth/self_hosted/plugin.yaml +8 -0
  767. package/agent/plugins/disk-cleanup/disk_cleanup.py +100 -20
  768. package/agent/plugins/google_meet/audio_bridge.py +4 -0
  769. package/agent/plugins/google_meet/meet_bot.py +7 -1
  770. package/agent/plugins/hermes-achievements/dashboard/dist/index.js +9 -15
  771. package/agent/plugins/image_gen/fal/__init__.py +35 -6
  772. package/agent/plugins/image_gen/krea/__init__.py +56 -13
  773. package/agent/plugins/image_gen/openai/__init__.py +122 -24
  774. package/agent/plugins/image_gen/openai-codex/__init__.py +28 -2
  775. package/agent/plugins/image_gen/xai/__init__.py +92 -12
  776. package/agent/plugins/kanban/dashboard/dist/index.js +63 -48
  777. package/agent/plugins/kanban/dashboard/plugin_api.py +39 -35
  778. package/agent/plugins/memory/__init__.py +48 -5
  779. package/agent/plugins/memory/byterover/__init__.py +1 -0
  780. package/agent/plugins/memory/hindsight/README.md +1 -1
  781. package/agent/plugins/memory/hindsight/__init__.py +138 -24
  782. package/agent/plugins/memory/hindsight/plugin.yaml +1 -1
  783. package/agent/plugins/memory/honcho/README.md +13 -10
  784. package/agent/plugins/memory/honcho/cli.py +247 -122
  785. package/agent/plugins/memory/honcho/client.py +112 -102
  786. package/agent/plugins/memory/openviking/README.md +12 -1
  787. package/agent/plugins/memory/openviking/__init__.py +2281 -107
  788. package/agent/plugins/memory/openviking/plugin.yaml +1 -2
  789. package/agent/plugins/memory/supermemory/README.md +22 -10
  790. package/agent/plugins/memory/supermemory/__init__.py +142 -37
  791. package/agent/plugins/memory/supermemory/plugin.yaml +1 -1
  792. package/agent/plugins/model-providers/anthropic/__init__.py +1 -0
  793. package/agent/plugins/model-providers/bedrock/__init__.py +1 -0
  794. package/agent/plugins/model-providers/copilot-acp/__init__.py +1 -0
  795. package/agent/plugins/model-providers/custom/__init__.py +8 -2
  796. package/agent/plugins/model-providers/kimi-coding/__init__.py +16 -7
  797. package/agent/plugins/model-providers/minimax/__init__.py +60 -8
  798. package/agent/plugins/model-providers/opencode-zen/__init__.py +12 -3
  799. package/agent/plugins/model-providers/openrouter/__init__.py +75 -4
  800. package/agent/plugins/model-providers/xiaomi/__init__.py +2 -0
  801. package/agent/plugins/model-providers/zai/__init__.py +1 -0
  802. package/agent/plugins/observability/langfuse/__init__.py +147 -14
  803. package/agent/plugins/observability/nemo_relay/README.md +559 -0
  804. package/agent/plugins/observability/nemo_relay/__init__.py +962 -0
  805. package/agent/plugins/observability/nemo_relay/plugin.yaml +20 -0
  806. package/agent/plugins/platforms/discord/adapter.py +932 -61
  807. package/agent/plugins/platforms/discord/voice_mixer.py +379 -0
  808. package/agent/plugins/platforms/google_chat/adapter.py +9 -3
  809. package/agent/plugins/platforms/google_chat/oauth.py +1 -1
  810. package/agent/plugins/platforms/homeassistant/__init__.py +3 -0
  811. package/agent/{gateway/platforms/homeassistant.py → plugins/platforms/homeassistant/adapter.py} +128 -0
  812. package/agent/plugins/platforms/homeassistant/plugin.yaml +22 -0
  813. package/agent/plugins/platforms/irc/adapter.py +4 -1
  814. package/agent/plugins/platforms/line/adapter.py +16 -1
  815. package/agent/plugins/platforms/mattermost/adapter.py +100 -24
  816. package/agent/plugins/platforms/photon/README.md +179 -0
  817. package/agent/plugins/platforms/photon/__init__.py +4 -0
  818. package/agent/plugins/platforms/photon/adapter.py +1586 -0
  819. package/agent/plugins/platforms/photon/auth.py +1046 -0
  820. package/agent/plugins/platforms/photon/cli.py +439 -0
  821. package/agent/plugins/platforms/photon/plugin.yaml +88 -0
  822. package/agent/plugins/platforms/photon/sidecar/README.md +52 -0
  823. package/agent/plugins/platforms/photon/sidecar/index.mjs +720 -0
  824. package/agent/plugins/platforms/photon/sidecar/package-lock.json +1730 -0
  825. package/agent/plugins/platforms/photon/sidecar/package.json +25 -0
  826. package/agent/plugins/platforms/photon/sidecar/patch-spectrum-mixed-attachments.mjs +155 -0
  827. package/agent/plugins/platforms/raft/__init__.py +3 -0
  828. package/agent/plugins/platforms/raft/adapter.py +774 -0
  829. package/agent/plugins/platforms/raft/plugin.yaml +19 -0
  830. package/agent/plugins/platforms/simplex/adapter.py +777 -220
  831. package/agent/plugins/platforms/simplex/plugin.yaml +21 -2
  832. package/agent/plugins/platforms/teams/adapter.py +175 -5
  833. package/agent/plugins/plugin_utils.py +135 -0
  834. package/agent/plugins/video_gen/fal/__init__.py +10 -3
  835. package/agent/plugins/web/searxng/provider.py +15 -2
  836. package/agent/plugins/web/xai/provider.py +2 -2
  837. package/agent/providers/base.py +22 -3
  838. package/agent/pyproject.toml +115 -21
  839. package/agent/run_agent.py +733 -39
  840. package/agent/scripts/build_skills_index.py +51 -19
  841. package/agent/scripts/check_subprocess_stdin.py +177 -0
  842. package/agent/scripts/contributor_audit.py +2 -0
  843. package/agent/scripts/docker_config_migrate.py +67 -0
  844. package/agent/scripts/install.cmd +3 -3
  845. package/agent/scripts/install.ps1 +580 -154
  846. package/agent/scripts/install.sh +402 -185
  847. package/agent/scripts/lib/node-bootstrap.sh +39 -4
  848. package/agent/scripts/release.py +183 -0
  849. package/agent/scripts/run_tests.sh +1 -0
  850. package/agent/scripts/run_tests_parallel.py +18 -23
  851. package/agent/scripts/whatsapp-bridge/bridge.js +25 -4
  852. package/agent/setup.py +59 -0
  853. package/agent/skills/autonomous-ai-agents/codex/SKILL.md +19 -0
  854. package/agent/skills/autonomous-ai-agents/hermes-agent/SKILL.md +10 -3
  855. package/agent/skills/{mcp/native-mcp/SKILL.md → autonomous-ai-agents/hermes-agent/references/native-mcp.md} +0 -13
  856. package/agent/skills/{devops/webhook-subscriptions/SKILL.md → autonomous-ai-agents/hermes-agent/references/webhooks.md} +1 -11
  857. package/agent/skills/clawpump/SKILL.md +3 -1
  858. package/agent/skills/devops/kanban-orchestrator/SKILL.md +1 -0
  859. package/agent/skills/devops/kanban-worker/SKILL.md +1 -0
  860. package/agent/skills/github/github-auth/SKILL.md +2 -2
  861. package/agent/skills/github/github-auth/scripts/gh-env.sh +2 -2
  862. package/agent/skills/github/github-code-review/SKILL.md +2 -2
  863. package/agent/skills/github/github-issues/SKILL.md +2 -2
  864. package/agent/skills/github/github-pr-workflow/SKILL.md +2 -2
  865. package/agent/skills/github/github-repo-management/SKILL.md +2 -2
  866. package/agent/skills/media/gif-search/SKILL.md +1 -1
  867. package/agent/skills/media/youtube-content/SKILL.md +10 -7
  868. package/agent/skills/media/youtube-content/scripts/fetch_transcript.py +3 -3
  869. package/agent/skills/note-taking/obsidian/SKILL.md +1 -1
  870. package/agent/skills/productivity/airtable/SKILL.md +2 -2
  871. package/agent/skills/productivity/google-workspace/scripts/setup.py +33 -7
  872. package/agent/skills/productivity/notion/SKILL.md +2 -2
  873. package/agent/skills/productivity/teams-meeting-pipeline/SKILL.md +1 -1
  874. package/agent/skills/research/llm-wiki/SKILL.md +1 -1
  875. package/agent/skills/social-media/xurl/SKILL.md +9 -0
  876. package/agent/skills/software-development/hermes-agent-skill-authoring/SKILL.md +1 -1
  877. package/agent/skills/software-development/plan/SKILL.md +285 -5
  878. package/agent/skills/software-development/requesting-code-review/SKILL.md +2 -2
  879. package/agent/skills/software-development/simplify-code/SKILL.md +212 -0
  880. package/agent/skills/software-development/spike/SKILL.md +2 -2
  881. package/agent/skills/software-development/systematic-debugging/SKILL.md +1 -1
  882. package/agent/skills/software-development/test-driven-development/SKILL.md +1 -1
  883. package/agent/tools/approval.py +302 -4
  884. package/agent/tools/async_delegation.py +386 -0
  885. package/agent/tools/blueprints.py +325 -0
  886. package/agent/tools/browser_cdp_tool.py +3 -3
  887. package/agent/tools/browser_tool.py +34 -6
  888. package/agent/tools/checkpoint_manager.py +31 -1
  889. package/agent/tools/clarify_tool.py +55 -5
  890. package/agent/tools/code_execution_tool.py +31 -14
  891. package/agent/tools/computer_use/cua_backend.py +81 -3
  892. package/agent/tools/computer_use/tool.py +79 -5
  893. package/agent/tools/computer_use/vision_routing.py +55 -3
  894. package/agent/tools/credential_files.py +31 -12
  895. package/agent/tools/cronjob_tools.py +30 -20
  896. package/agent/tools/delegate_tool.py +356 -31
  897. package/agent/tools/env_probe.py +1 -0
  898. package/agent/tools/environments/docker.py +163 -8
  899. package/agent/tools/environments/file_sync.py +2 -1
  900. package/agent/tools/environments/local.py +74 -23
  901. package/agent/tools/environments/singularity.py +4 -1
  902. package/agent/tools/environments/ssh.py +78 -11
  903. package/agent/tools/file_operations.py +277 -41
  904. package/agent/tools/file_tools.py +166 -28
  905. package/agent/tools/image_generation_tool.py +515 -29
  906. package/agent/tools/kanban_tools.py +99 -0
  907. package/agent/tools/lazy_deps.py +33 -2
  908. package/agent/tools/mcp_oauth.py +5 -5
  909. package/agent/tools/mcp_oauth_manager.py +7 -5
  910. package/agent/tools/mcp_tool.py +840 -33
  911. package/agent/tools/memory_tool.py +335 -38
  912. package/agent/tools/osv_check.py +15 -1
  913. package/agent/tools/process_registry.py +155 -11
  914. package/agent/tools/read_extract.py +248 -0
  915. package/agent/tools/read_terminal_tool.py +93 -0
  916. package/agent/tools/schema_sanitizer.py +38 -0
  917. package/agent/tools/send_message_tool.py +163 -49
  918. package/agent/tools/session_search_tool.py +189 -7
  919. package/agent/tools/skill_manager_tool.py +202 -3
  920. package/agent/tools/skill_usage.py +52 -4
  921. package/agent/tools/skills_hub.py +184 -44
  922. package/agent/tools/skills_sync.py +232 -5
  923. package/agent/tools/skills_tool.py +125 -11
  924. package/agent/tools/terminal_tool.py +148 -26
  925. package/agent/tools/tirith_security.py +2 -0
  926. package/agent/tools/todo_tool.py +32 -1
  927. package/agent/tools/transcription_tools.py +13 -5
  928. package/agent/tools/tts_tool.py +332 -38
  929. package/agent/tools/url_safety.py +52 -1
  930. package/agent/tools/vision_tools.py +124 -39
  931. package/agent/tools/voice_mode.py +4 -3
  932. package/agent/tools/web_tools.py +45 -15
  933. package/agent/tools/write_approval.py +493 -0
  934. package/agent/toolsets.py +34 -10
  935. package/agent/trajectory_compressor.py +81 -10
  936. package/agent/tui_gateway/entry.py +43 -6
  937. package/agent/tui_gateway/server.py +3335 -330
  938. package/agent/tui_gateway/slash_worker.py +61 -0
  939. package/agent/tui_gateway/ws.py +67 -9
  940. package/agent/ui-tui/eslint.config.mjs +0 -4
  941. package/agent/ui-tui/package.json +6 -6
  942. package/agent/ui-tui/packages/hermes-ink/package.json +1 -1
  943. package/agent/ui-tui/packages/hermes-ink/src/ink/app-mouse.test.ts +34 -1
  944. package/agent/ui-tui/packages/hermes-ink/src/ink/app-rawmode-mouse.test.ts +91 -0
  945. package/agent/ui-tui/packages/hermes-ink/src/ink/components/App.tsx +35 -2
  946. package/agent/ui-tui/packages/hermes-ink/src/ink/events/input-event.ts +4 -11
  947. package/agent/ui-tui/packages/hermes-ink/src/ink/parse-keypress.test.ts +23 -57
  948. package/agent/ui-tui/packages/hermes-ink/src/ink/parse-keypress.ts +11 -135
  949. package/agent/ui-tui/packages/hermes-ink/src/ink/termio/tokenize.test.ts +185 -0
  950. package/agent/ui-tui/packages/hermes-ink/src/ink/termio/tokenize.ts +37 -3
  951. package/agent/ui-tui/packages/hermes-ink/src/utils/execFileNoThrow.ts +5 -5
  952. package/agent/ui-tui/src/__tests__/appChromeStatusRule.test.tsx +217 -0
  953. package/agent/ui-tui/src/__tests__/appChromeStatusRuleDevCredits.test.tsx +73 -0
  954. package/agent/ui-tui/src/__tests__/approvalAction.test.ts +11 -0
  955. package/agent/ui-tui/src/__tests__/billingCommand.test.ts +301 -0
  956. package/agent/ui-tui/src/__tests__/blockLayout.test.ts +122 -0
  957. package/agent/ui-tui/src/__tests__/brandingMcpCount.test.ts +111 -0
  958. package/agent/ui-tui/src/__tests__/completionApply.test.ts +51 -0
  959. package/agent/ui-tui/src/__tests__/createGatewayEventHandler.test.ts +487 -2
  960. package/agent/ui-tui/src/__tests__/createSlashHandler.test.ts +54 -0
  961. package/agent/ui-tui/src/__tests__/creditsCommand.test.ts +144 -0
  962. package/agent/ui-tui/src/__tests__/gatewayClient.test.ts +120 -99
  963. package/agent/ui-tui/src/__tests__/gracefulExit.test.ts +11 -0
  964. package/agent/ui-tui/src/__tests__/memoryMonitor.test.ts +102 -0
  965. package/agent/ui-tui/src/__tests__/paths.test.ts +41 -1
  966. package/agent/ui-tui/src/__tests__/terminalModes.test.ts +22 -0
  967. package/agent/ui-tui/src/__tests__/text.test.ts +23 -0
  968. package/agent/ui-tui/src/__tests__/textInputFastEcho.test.ts +37 -0
  969. package/agent/ui-tui/src/__tests__/turnControllerNotice.test.ts +43 -0
  970. package/agent/ui-tui/src/__tests__/useInputHandlers.test.ts +38 -1
  971. package/agent/ui-tui/src/__tests__/virtualHeights.test.ts +8 -0
  972. package/agent/ui-tui/src/app/createGatewayEventHandler.ts +102 -7
  973. package/agent/ui-tui/src/app/interfaces.ts +64 -1
  974. package/agent/ui-tui/src/app/overlayStore.ts +18 -2
  975. package/agent/ui-tui/src/app/slash/commands/billing.ts +332 -0
  976. package/agent/ui-tui/src/app/slash/commands/core.ts +31 -2
  977. package/agent/ui-tui/src/app/slash/commands/credits.ts +57 -0
  978. package/agent/ui-tui/src/app/slash/commands/ops.ts +28 -0
  979. package/agent/ui-tui/src/app/slash/commands/session.ts +32 -4
  980. package/agent/ui-tui/src/app/slash/registry.ts +4 -0
  981. package/agent/ui-tui/src/app/turnController.ts +145 -2
  982. package/agent/ui-tui/src/app/uiStore.ts +2 -0
  983. package/agent/ui-tui/src/app/useInputHandlers.ts +42 -4
  984. package/agent/ui-tui/src/app/useMainApp.ts +54 -8
  985. package/agent/ui-tui/src/app/useSessionLifecycle.ts +40 -31
  986. package/agent/ui-tui/src/app/useSubmission.ts +23 -31
  987. package/agent/ui-tui/src/components/appChrome.tsx +112 -5
  988. package/agent/ui-tui/src/components/appLayout.tsx +9 -0
  989. package/agent/ui-tui/src/components/appOverlays.tsx +25 -1
  990. package/agent/ui-tui/src/components/billingOverlay.tsx +684 -0
  991. package/agent/ui-tui/src/components/branding.tsx +15 -3
  992. package/agent/ui-tui/src/components/messageLine.tsx +25 -3
  993. package/agent/ui-tui/src/components/pluginsHub.tsx +238 -0
  994. package/agent/ui-tui/src/components/prompts.tsx +31 -17
  995. package/agent/ui-tui/src/components/streamingAssistant.tsx +63 -55
  996. package/agent/ui-tui/src/components/textInput.tsx +16 -0
  997. package/agent/ui-tui/src/config/env.ts +12 -0
  998. package/agent/ui-tui/src/config/limits.ts +13 -0
  999. package/agent/ui-tui/src/domain/blockLayout.ts +146 -0
  1000. package/agent/ui-tui/src/domain/paths.ts +24 -0
  1001. package/agent/ui-tui/src/domain/slash.ts +40 -0
  1002. package/agent/ui-tui/src/entry.tsx +35 -4
  1003. package/agent/ui-tui/src/gatewayClient.ts +22 -10
  1004. package/agent/ui-tui/src/gatewayTypes.ts +130 -1
  1005. package/agent/ui-tui/src/lib/gracefulExit.ts +24 -4
  1006. package/agent/ui-tui/src/lib/memory.test.ts +162 -0
  1007. package/agent/ui-tui/src/lib/memory.ts +60 -1
  1008. package/agent/ui-tui/src/lib/memoryMonitor.ts +79 -4
  1009. package/agent/ui-tui/src/lib/osc52.ts +1 -1
  1010. package/agent/ui-tui/src/lib/text.test.ts +32 -1
  1011. package/agent/ui-tui/src/lib/text.ts +29 -2
  1012. package/agent/ui-tui/src/lib/virtualHeights.ts +13 -0
  1013. package/agent/ui-tui/src/types.ts +5 -0
  1014. package/agent/ui-tui/tsconfig.build.json +0 -1
  1015. package/agent/ui-tui/tsconfig.json +2 -1
  1016. package/agent/utils.py +66 -2
  1017. package/agent/uv.lock +300 -684
  1018. package/agent/web/index.html +2 -2
  1019. package/agent/web/package.json +11 -6
  1020. package/agent/web/public/claw-bg.webp +0 -0
  1021. package/agent/web/public/claw-logo.webp +0 -0
  1022. package/agent/web/src/App.tsx +138 -48
  1023. package/agent/web/src/components/AutomationBlueprints.tsx +225 -0
  1024. package/agent/web/src/components/Backdrop.tsx +15 -0
  1025. package/agent/web/src/components/ChatSessionList.tsx +260 -0
  1026. package/agent/web/src/components/ChatSidebar.tsx +262 -78
  1027. package/agent/web/src/components/ConfirmDialog.tsx +122 -0
  1028. package/agent/web/src/components/ModelPickerDialog.tsx +111 -16
  1029. package/agent/web/src/components/ModelReloadConfirm.tsx +40 -0
  1030. package/agent/web/src/components/ProfileScopeBanner.tsx +30 -0
  1031. package/agent/web/src/components/ProfileSwitcher.tsx +67 -0
  1032. package/agent/web/src/components/ReasoningPicker.tsx +167 -0
  1033. package/agent/web/src/components/SkillEditorDialog.tsx +215 -0
  1034. package/agent/web/src/components/ThemeSwitcher.tsx +119 -4
  1035. package/agent/web/src/components/ToolsetConfigDrawer.tsx +457 -0
  1036. package/agent/web/src/contexts/PageHeaderProvider.tsx +7 -4
  1037. package/agent/web/src/contexts/ProfileProvider.tsx +137 -0
  1038. package/agent/web/src/contexts/SystemActions.tsx +6 -8
  1039. package/agent/web/src/contexts/profile-context.ts +19 -0
  1040. package/agent/web/src/contexts/useProfileScope.ts +6 -0
  1041. package/agent/web/src/i18n/af.ts +5 -4
  1042. package/agent/web/src/i18n/de.ts +5 -4
  1043. package/agent/web/src/i18n/en.ts +58 -4
  1044. package/agent/web/src/i18n/es.ts +5 -3
  1045. package/agent/web/src/i18n/fr.ts +5 -3
  1046. package/agent/web/src/i18n/ga.ts +5 -4
  1047. package/agent/web/src/i18n/hu.ts +5 -4
  1048. package/agent/web/src/i18n/it.ts +5 -4
  1049. package/agent/web/src/i18n/ja.ts +5 -4
  1050. package/agent/web/src/i18n/ko.ts +5 -4
  1051. package/agent/web/src/i18n/pt.ts +5 -3
  1052. package/agent/web/src/i18n/ru.ts +5 -4
  1053. package/agent/web/src/i18n/tr.ts +5 -4
  1054. package/agent/web/src/i18n/types.ts +59 -1
  1055. package/agent/web/src/i18n/uk.ts +5 -3
  1056. package/agent/web/src/i18n/zh-hant.ts +5 -4
  1057. package/agent/web/src/i18n/zh.ts +5 -4
  1058. package/agent/web/src/index.css +2 -2
  1059. package/agent/web/src/lib/api.ts +819 -52
  1060. package/agent/web/src/lib/dashboard-flags.ts +16 -7
  1061. package/agent/web/src/lib/reasoning-effort.test.ts +48 -0
  1062. package/agent/web/src/lib/reasoning-effort.ts +36 -0
  1063. package/agent/web/src/lib/session-refresh.test.ts +21 -0
  1064. package/agent/web/src/lib/session-refresh.ts +26 -0
  1065. package/agent/web/src/pages/ChannelsPage.tsx +529 -68
  1066. package/agent/web/src/pages/ChatPage.tsx +249 -56
  1067. package/agent/web/src/pages/ConfigPage.tsx +11 -1
  1068. package/agent/web/src/pages/CronPage.tsx +219 -31
  1069. package/agent/web/src/pages/EnvPage.tsx +25 -6
  1070. package/agent/web/src/pages/FilesPage.tsx +525 -0
  1071. package/agent/web/src/pages/McpPage.tsx +80 -3
  1072. package/agent/web/src/pages/ModelsPage.tsx +97 -12
  1073. package/agent/web/src/pages/PluginsPage.tsx +1 -1
  1074. package/agent/web/src/pages/ProfileBuilderPage.tsx +611 -0
  1075. package/agent/web/src/pages/ProfilesPage.tsx +1038 -172
  1076. package/agent/web/src/pages/SessionsPage.tsx +144 -13
  1077. package/agent/web/src/pages/SkillsPage.tsx +851 -70
  1078. package/agent/web/src/pages/SystemPage.tsx +340 -4
  1079. package/agent/web/src/pages/WalletPage.tsx +401 -0
  1080. package/agent/web/src/pages/WebhooksPage.tsx +145 -15
  1081. package/agent/web/src/pages/X402Page.tsx +207 -0
  1082. package/agent/web/src/plugins/registry.ts +28 -11
  1083. package/agent/web/src/plugins/sdk.d.ts +160 -0
  1084. package/agent/web/src/themes/context.tsx +112 -5
  1085. package/agent/web/src/themes/fonts.ts +167 -0
  1086. package/agent/web/src/themes/index.ts +7 -0
  1087. package/agent/web/tsconfig.app.json +0 -1
  1088. package/agent/web/vite.config.ts +1 -8
  1089. package/agent/web/vitest.config.ts +16 -0
  1090. package/package.json +1 -1
  1091. package/agent/apps/desktop/package-lock.json +0 -18363
  1092. package/agent/apps/desktop/src/app/chat/composer/skin-slash-popover.tsx +0 -56
  1093. package/agent/apps/desktop/src/components/assistant-ui/thread-virtualizer.tsx +0 -382
  1094. package/agent/apps/desktop/src/components/assistant-ui/todo-tool.tsx +0 -109
  1095. package/agent/apps/desktop/src/components/chat/generated-image-context.tsx +0 -19
  1096. package/agent/optional-skills/productivity/shop-app/SKILL.md +0 -340
  1097. package/agent/skills/autonomous-ai-agents/kanban-codex-lane/SKILL.md +0 -277
  1098. package/agent/skills/autonomous-ai-agents/kanban-codex-lane/templates/pmb-codex-lane-prompt.md +0 -57
  1099. package/agent/skills/diagramming/DESCRIPTION.md +0 -3
  1100. package/agent/skills/domain/DESCRIPTION.md +0 -24
  1101. package/agent/skills/gifs/DESCRIPTION.md +0 -3
  1102. package/agent/skills/inference-sh/DESCRIPTION.md +0 -19
  1103. package/agent/skills/mcp/DESCRIPTION.md +0 -3
  1104. package/agent/skills/media/spotify/SKILL.md +0 -135
  1105. package/agent/skills/mlops/training/DESCRIPTION.md +0 -3
  1106. package/agent/skills/mlops/vector-databases/DESCRIPTION.md +0 -3
  1107. package/agent/skills/productivity/linear/SKILL.md +0 -380
  1108. package/agent/skills/productivity/linear/scripts/linear_api.py +0 -445
  1109. package/agent/skills/software-development/debugging-hermes-tui-commands/SKILL.md +0 -152
  1110. package/agent/skills/software-development/writing-plans/SKILL.md +0 -297
  1111. package/agent/ui-tui/package-lock.json +0 -7449
  1112. package/agent/ui-tui/packages/hermes-ink/package-lock.json +0 -1289
  1113. package/agent/web/package-lock.json +0 -8887
  1114. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/PORT_NOTES.md +0 -0
  1115. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/SKILL.md +0 -0
  1116. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/prompts/system.md +0 -0
  1117. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/palettes/macaron.md +0 -0
  1118. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/palettes/mono-ink.md +0 -0
  1119. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/palettes/neon.md +0 -0
  1120. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/palettes/warm.md +0 -0
  1121. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/prompt-construction.md +0 -0
  1122. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/style-presets.md +0 -0
  1123. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/blueprint.md +0 -0
  1124. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/chalkboard.md +0 -0
  1125. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/editorial.md +0 -0
  1126. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/elegant.md +0 -0
  1127. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/fantasy-animation.md +0 -0
  1128. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/flat-doodle.md +0 -0
  1129. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/flat.md +0 -0
  1130. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/ink-notes.md +0 -0
  1131. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/intuition-machine.md +0 -0
  1132. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/minimal.md +0 -0
  1133. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/nature.md +0 -0
  1134. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/notion.md +0 -0
  1135. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/pixel-art.md +0 -0
  1136. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/playful.md +0 -0
  1137. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/retro.md +0 -0
  1138. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/scientific.md +0 -0
  1139. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/screen-print.md +0 -0
  1140. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/sketch-notes.md +0 -0
  1141. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/sketch.md +0 -0
  1142. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/vector-illustration.md +0 -0
  1143. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/vintage.md +0 -0
  1144. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/warm.md +0 -0
  1145. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/watercolor.md +0 -0
  1146. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles.md +0 -0
  1147. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/usage.md +0 -0
  1148. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/workflow.md +0 -0
  1149. /package/agent/{skills → optional-skills}/creative/baoyu-comic/PORT_NOTES.md +0 -0
  1150. /package/agent/{skills → optional-skills}/creative/baoyu-comic/SKILL.md +0 -0
  1151. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/analysis-framework.md +0 -0
  1152. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/chalk.md +0 -0
  1153. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/ink-brush.md +0 -0
  1154. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/ligne-claire.md +0 -0
  1155. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/manga.md +0 -0
  1156. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/minimalist.md +0 -0
  1157. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/realistic.md +0 -0
  1158. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/auto-selection.md +0 -0
  1159. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/base-prompt.md +0 -0
  1160. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/character-template.md +0 -0
  1161. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/cinematic.md +0 -0
  1162. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/dense.md +0 -0
  1163. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/four-panel.md +0 -0
  1164. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/mixed.md +0 -0
  1165. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/splash.md +0 -0
  1166. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/standard.md +0 -0
  1167. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/webtoon.md +0 -0
  1168. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/ohmsha-guide.md +0 -0
  1169. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/partial-workflows.md +0 -0
  1170. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/presets/concept-story.md +0 -0
  1171. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/presets/four-panel.md +0 -0
  1172. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/presets/ohmsha.md +0 -0
  1173. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/presets/shoujo.md +0 -0
  1174. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/presets/wuxia.md +0 -0
  1175. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/storyboard-template.md +0 -0
  1176. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/action.md +0 -0
  1177. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/dramatic.md +0 -0
  1178. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/energetic.md +0 -0
  1179. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/neutral.md +0 -0
  1180. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/romantic.md +0 -0
  1181. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/vintage.md +0 -0
  1182. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/warm.md +0 -0
  1183. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/workflow.md +0 -0
  1184. /package/agent/{skills → optional-skills}/creative/creative-ideation/SKILL.md +0 -0
  1185. /package/agent/{skills → optional-skills}/creative/creative-ideation/references/full-prompt-library.md +0 -0
  1186. /package/agent/{skills → optional-skills}/creative/pixel-art/ATTRIBUTION.md +0 -0
  1187. /package/agent/{skills → optional-skills}/creative/pixel-art/SKILL.md +0 -0
  1188. /package/agent/{skills → optional-skills}/creative/pixel-art/references/palettes.md +0 -0
  1189. /package/agent/{skills → optional-skills}/creative/pixel-art/scripts/__init__.py +0 -0
  1190. /package/agent/{skills → optional-skills}/creative/pixel-art/scripts/palettes.py +0 -0
  1191. /package/agent/{skills → optional-skills}/creative/pixel-art/scripts/pixel_art.py +0 -0
  1192. /package/agent/{skills → optional-skills}/creative/pixel-art/scripts/pixel_art_video.py +0 -0
  1193. /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/SKILL.md +0 -0
  1194. /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/references/analysis-modules.md +0 -0
  1195. /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/references/methods-guide.md +0 -0
  1196. /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/templates/abliteration-config.yaml +0 -0
  1197. /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/templates/analysis-study.yaml +0 -0
  1198. /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/templates/batch-abliteration.yaml +0 -0
  1199. /package/agent/{skills → optional-skills}/mlops/research/DESCRIPTION.md +0 -0
  1200. /package/agent/{skills → optional-skills}/mlops/research/dspy/SKILL.md +0 -0
  1201. /package/agent/{skills → optional-skills}/mlops/research/dspy/references/examples.md +0 -0
  1202. /package/agent/{skills → optional-skills}/mlops/research/dspy/references/modules.md +0 -0
  1203. /package/agent/{skills → optional-skills}/mlops/research/dspy/references/optimizers.md +0 -0
  1204. /package/agent/{skills/red-teaming → optional-skills/security}/godmode/references/jailbreak-templates.md +0 -0
  1205. /package/agent/{skills/red-teaming → optional-skills/security}/godmode/references/refusal-detection.md +0 -0
  1206. /package/agent/{skills/red-teaming → optional-skills/security}/godmode/scripts/godmode_race.py +0 -0
  1207. /package/agent/{skills/red-teaming → optional-skills/security}/godmode/scripts/load_godmode.py +0 -0
  1208. /package/agent/{skills/red-teaming → optional-skills/security}/godmode/scripts/parseltongue.py +0 -0
  1209. /package/agent/{skills/red-teaming → optional-skills/security}/godmode/templates/prefill-subtle.json +0 -0
  1210. /package/agent/{skills/red-teaming → optional-skills/security}/godmode/templates/prefill.json +0 -0
  1211. /package/agent/{skills → optional-skills}/software-development/subagent-driven-development/references/context-budget-discipline.md +0 -0
  1212. /package/agent/{skills → optional-skills}/software-development/subagent-driven-development/references/gates-taxonomy.md +0 -0
@@ -13,30 +13,51 @@ import {
13
13
  useState
14
14
  } from 'react'
15
15
 
16
- import { hermesDirectiveFormatter } from '@/components/assistant-ui/directive-text'
16
+ import { hermesDirectiveFormatter, type SlashChipKind } from '@/components/assistant-ui/directive-text'
17
+ import { composerFill, composerSurfaceGlass } from '@/components/chat/composer-dock'
17
18
  import { Button } from '@/components/ui/button'
18
19
  import { useMediaQuery } from '@/hooks/use-media-query'
19
20
  import { useResizeObserver } from '@/hooks/use-resize-observer'
21
+ import { useI18n } from '@/i18n'
20
22
  import { chatMessageText } from '@/lib/chat-messages'
23
+ import { SLASH_COMMAND_RE } from '@/lib/chat-runtime'
24
+ import { desktopSlashCommandTakesArgs } from '@/lib/desktop-slash-commands'
21
25
  import { DATA_IMAGE_URL_RE } from '@/lib/embedded-images'
22
26
  import { triggerHaptic } from '@/lib/haptics'
23
27
  import { cn } from '@/lib/utils'
24
28
  import {
25
29
  $composerAttachments,
26
30
  clearComposerAttachments,
27
- type ComposerAttachment
31
+ clearSessionDraft,
32
+ type ComposerAttachment,
33
+ stashSessionDraft,
34
+ takeSessionDraft
28
35
  } from '@/store/composer'
36
+ import {
37
+ browseBackward,
38
+ browseForward,
39
+ deriveUserHistory,
40
+ isBrowsingHistory,
41
+ resetBrowseState
42
+ } from '@/store/composer-input-history'
29
43
  import {
30
44
  $queuedPromptsBySession,
31
45
  enqueueQueuedPrompt,
46
+ MAX_AUTO_DRAIN_ATTEMPTS,
47
+ migrateQueuedPrompts,
48
+ promoteQueuedPrompt,
32
49
  type QueuedPromptEntry,
33
50
  removeQueuedPrompt,
51
+ shouldAutoDrain,
34
52
  updateQueuedPrompt
35
53
  } from '@/store/composer-queue'
36
- import { $messages } from '@/store/session'
54
+ import { $statusItemsBySession } from '@/store/composer-status'
55
+ import { notify } from '@/store/notifications'
56
+ import { $gatewayState, $messages, setSessionPickerOpen } from '@/store/session'
37
57
  import { $threadScrolledUp } from '@/store/thread-scroll'
58
+ import { useTheme } from '@/themes'
38
59
 
39
- import { extractDroppedFiles, HERMES_PATHS_MIME } from '../hooks/use-composer-actions'
60
+ import { extractDroppedFiles, HERMES_PATHS_MIME, partitionDroppedFiles } from '../hooks/use-composer-actions'
40
61
 
41
62
  import { AttachmentList } from './attachments'
42
63
  import { ContextMenu } from './context-menu'
@@ -47,6 +68,7 @@ import {
47
68
  focusComposerInput,
48
69
  markActiveComposer,
49
70
  onComposerFocusRequest,
71
+ onComposerInsertRefsRequest,
50
72
  onComposerInsertRequest
51
73
  } from './focus'
52
74
  import { HelpHint } from './help-hint'
@@ -54,16 +76,25 @@ import { useAtCompletions } from './hooks/use-at-completions'
54
76
  import { useSlashCompletions } from './hooks/use-slash-completions'
55
77
  import { useVoiceConversation } from './hooks/use-voice-conversation'
56
78
  import { useVoiceRecorder } from './hooks/use-voice-recorder'
57
- import { dragHasAttachments, droppedFileInlineRef, insertInlineRefsIntoEditor } from './inline-refs'
79
+ import {
80
+ dragHasAttachments,
81
+ droppedFileInlineRefs,
82
+ type InlineRefInput,
83
+ insertInlineRefsIntoEditor
84
+ } from './inline-refs'
58
85
  import { QueuePanel } from './queue-panel'
59
86
  import {
60
87
  composerPlainText,
88
+ deleteSelectionInEditor,
89
+ insertPlainTextAtCaret,
90
+ normalizeComposerEditorDom,
61
91
  placeCaretEnd,
62
92
  refChipElement,
63
93
  renderComposerContents,
64
- RICH_INPUT_SLOT
94
+ RICH_INPUT_SLOT,
95
+ slashChipElement
65
96
  } from './rich-editor'
66
- import { SkinSlashPopover } from './skin-slash-popover'
97
+ import { ComposerStatusStack } from './status-stack'
67
98
  import { detectTrigger, extractClipboardImageBlobs, textBeforeCaret, type TriggerState } from './text-utils'
68
99
  import { ComposerTriggerPopover } from './trigger-popover'
69
100
  import type { ChatBarProps } from './types'
@@ -72,9 +103,46 @@ import { VoiceActivity, VoicePlaybackActivity } from './voice-activity'
72
103
 
73
104
  const COMPOSER_STACK_BREAKPOINT_PX = 320
74
105
 
106
+ // A single editor line is ~28px (--composer-input-min-height 1.625rem + 0.5rem
107
+ // vertical padding). Anything taller means the text wrapped to a second line,
108
+ // which is when the composer should expand to the stacked layout.
109
+ const COMPOSER_SINGLE_LINE_MAX_PX = 36
110
+
75
111
  const COMPOSER_FADE_BACKGROUND =
76
112
  'linear-gradient(to bottom, transparent, color-mix(in srgb, var(--dt-background) 10%, transparent))'
77
113
 
114
+ const pickPlaceholder = (pool: readonly string[]) => pool[Math.floor(Math.random() * pool.length)]
115
+
116
+ /** Completion items can carry an `action` (set in use-slash-completions) that
117
+ * runs a side effect on pick instead of inserting a chip — e.g. the session
118
+ * picker's "Browse all…" entry opens the overlay. Table-driven so new action
119
+ * items are a registry row, not a composer branch. */
120
+ const COMPLETION_ACTIONS: Record<string, () => void> = {
121
+ 'session-picker': () => setSessionPickerOpen(true)
122
+ }
123
+
124
+ /** Map a picked `/` completion to its pill accent. Driven by the completion
125
+ * group set in use-slash-completions (Skills / Themes / Commands|Options). */
126
+ function slashChipKindForItem(item: Unstable_TriggerItem): SlashChipKind {
127
+ const group = (item.metadata as { group?: unknown } | undefined)?.group
128
+
129
+ if (group === 'Skills') {
130
+ return 'skill'
131
+ }
132
+
133
+ if (group === 'Themes') {
134
+ return 'theme'
135
+ }
136
+
137
+ return 'command'
138
+ }
139
+
140
+ /** A `/` query is at its arg stage once it's past the command name. */
141
+ const slashArgStage = (query: string) => query.includes(' ')
142
+
143
+ /** The `/command` token of a slash query (`personality x` → `/personality`). */
144
+ const slashCommandToken = (query: string) => `/${query.split(/\s+/, 1)[0]?.toLowerCase() ?? ''}`
145
+
78
146
  interface QueueEditState {
79
147
  attachments: ComposerAttachment[]
80
148
  draft: string
@@ -84,6 +152,10 @@ interface QueueEditState {
84
152
 
85
153
  const cloneAttachments = (attachments: ComposerAttachment[]) => attachments.map(a => ({ ...a }))
86
154
 
155
+ // Quiet period after the last keystroke before persisting the draft;
156
+ // unmount/pagehide flushes bypass it.
157
+ const DRAFT_PERSIST_DEBOUNCE_MS = 400
158
+
87
159
  export function ChatBar({
88
160
  busy,
89
161
  cwd,
@@ -103,6 +175,7 @@ export function ChatBar({
103
175
  onPickFolders,
104
176
  onPickImages,
105
177
  onRemoveAttachment,
178
+ onSteer,
106
179
  onSubmit,
107
180
  onTranscribeAudio
108
181
  }: ChatBarProps) {
@@ -110,6 +183,7 @@ export function ChatBar({
110
183
  const draft = useAuiState(s => s.composer.text)
111
184
  const attachments = useStore($composerAttachments)
112
185
  const queuedPromptsBySession = useStore($queuedPromptsBySession)
186
+ const statusItemsBySession = useStore($statusItemsBySession)
113
187
  const scrolledUp = useStore($threadScrolledUp)
114
188
  const activeQueueSessionKey = queueSessionKey || sessionId || null
115
189
 
@@ -118,12 +192,29 @@ export function ChatBar({
118
192
  [activeQueueSessionKey, queuedPromptsBySession]
119
193
  )
120
194
 
195
+ // Status items (subagents, background processes) are keyed by the RUNTIME
196
+ // session id — gateway events and process.list both speak that id. Only the
197
+ // queue uses the stored-session fallback key (prompts can queue pre-resume).
198
+ const statusSessionId = sessionId ?? null
199
+
200
+ const statusStackVisible = useMemo(
201
+ () =>
202
+ queuedPrompts.length > 0 || (statusSessionId ? (statusItemsBySession[statusSessionId]?.length ?? 0) > 0 : false),
203
+ [queuedPrompts.length, statusItemsBySession, statusSessionId]
204
+ )
205
+
121
206
  const composerRef = useRef<HTMLFormElement | null>(null)
122
207
  const composerSurfaceRef = useRef<HTMLDivElement | null>(null)
123
208
  const editorRef = useRef<HTMLDivElement | null>(null)
124
209
  const draftRef = useRef(draft)
125
- const previousBusyRef = useRef(busy)
210
+ const pendingDraftPersistRef = useRef<{ scope: string | null; text: string } | null>(null)
211
+ const activeQueueSessionKeyRef = useRef(activeQueueSessionKey)
212
+ activeQueueSessionKeyRef.current = activeQueueSessionKey
213
+ const prevQueueKeyRef = useRef(activeQueueSessionKey)
126
214
  const drainingQueueRef = useRef(false)
215
+ // Per-entry auto-drain failure counts; bounds retries so a persistent 404
216
+ // can't spin-loop. Cleared on success; reset naturally on remount/reconnect.
217
+ const drainFailuresRef = useRef(new Map<string, number>())
127
218
  const urlInputRef = useRef<HTMLInputElement | null>(null)
128
219
 
129
220
  const [urlOpen, setUrlOpen] = useState(false)
@@ -134,22 +225,78 @@ export function ChatBar({
134
225
  const [dragActive, setDragActive] = useState(false)
135
226
  const [queueEdit, setQueueEdit] = useState<QueueEditState | null>(null)
136
227
  const [focusRequestId, setFocusRequestId] = useState(0)
228
+ const queueEditRef = useRef(queueEdit)
229
+ queueEditRef.current = queueEdit
137
230
  const dragDepthRef = useRef(0)
231
+ const composingRef = useRef(false) // true during IME composition (CJK input)
138
232
  const lastSpokenIdRef = useRef<string | null>(null)
139
233
 
140
234
  const narrow = useMediaQuery('(max-width: 30rem)')
141
235
 
236
+ const { availableThemes, themeName } = useTheme()
142
237
  const at = useAtCompletions({ gateway: gateway ?? null, sessionId: sessionId ?? null, cwd: cwd ?? null })
143
- const slash = useSlashCompletions({ gateway: gateway ?? null })
238
+ const slash = useSlashCompletions({ activeSkin: themeName, gateway: gateway ?? null, skinThemes: availableThemes })
144
239
 
145
240
  const stacked = expanded || narrow || tight
146
- const hasComposerPayload = draft.trim().length > 0 || attachments.length > 0
241
+ const trimmedDraft = draft.trim()
242
+ const hasComposerPayload = trimmedDraft.length > 0 || attachments.length > 0
147
243
  const canSubmit = busy || hasComposerPayload
148
244
  const editingQueuedPrompt = queueEdit ? (queuedPrompts.find(entry => entry.id === queueEdit.entryId) ?? null) : null
149
245
  const busyAction = busy && hasComposerPayload ? 'queue' : 'stop'
246
+
247
+ // Steer only makes sense mid-turn, text-only (the gateway can't carry images
248
+ // into a tool result) and never for a slash command (those execute inline).
249
+ const canSteer =
250
+ busy && !!onSteer && attachments.length === 0 && trimmedDraft.length > 0 && !SLASH_COMMAND_RE.test(trimmedDraft)
251
+
150
252
  const showHelpHint = draft === '?'
151
253
 
152
- const placeholder = disabled ? 'Starting Hermes...' : 'Send follow-up'
254
+ const { t } = useI18n()
255
+ const gatewayState = useStore($gatewayState)
256
+ const newSessionPlaceholders = t.composer.newSessionPlaceholders
257
+ const followUpPlaceholders = t.composer.followUpPlaceholders
258
+ const reconnecting = gatewayState === 'closed' || gatewayState === 'error'
259
+ const inputDisabled = disabled && !reconnecting
260
+
261
+ // Resting placeholder: a starter for brand-new sessions, a continuation for
262
+ // existing ones. Picked once and only re-rolled when we genuinely move to a
263
+ // *different* conversation. Critically, the first id assignment of a freshly
264
+ // started session (null → id, on the first send) is treated as the same
265
+ // conversation so the placeholder doesn't visibly flip mid-stream.
266
+ const [restingPlaceholder, setRestingPlaceholder] = useState(() =>
267
+ pickPlaceholder(sessionId ? followUpPlaceholders : newSessionPlaceholders)
268
+ )
269
+
270
+ const prevSessionIdRef = useRef(sessionId)
271
+
272
+ useEffect(() => {
273
+ const prev = prevSessionIdRef.current
274
+ prevSessionIdRef.current = sessionId
275
+
276
+ if (prev === sessionId) {
277
+ return
278
+ }
279
+
280
+ // null → id: the new session we're already in just got persisted. Keep the
281
+ // starter we showed instead of swapping to a follow-up under the user.
282
+ if (prev == null && sessionId) {
283
+ return
284
+ }
285
+
286
+ resetBrowseState(prev)
287
+ setRestingPlaceholder(pickPlaceholder(sessionId ? followUpPlaceholders : newSessionPlaceholders))
288
+ }, [followUpPlaceholders, newSessionPlaceholders, sessionId])
289
+
290
+ // When the transport is disabled it's because the gateway isn't open.
291
+ // Distinguish a cold start ("Starting Hermes...") from a dropped connection
292
+ // we're trying to restore. During reconnect, keep the textbox editable so a
293
+ // flaky network doesn't block drafting; only submit/backend actions stay
294
+ // disabled until the gateway is open again.
295
+ const placeholder = disabled
296
+ ? reconnecting
297
+ ? t.composer.placeholderReconnecting
298
+ : t.composer.placeholderStarting
299
+ : restingPlaceholder
153
300
 
154
301
  const focusInput = useCallback(() => {
155
302
  focusComposerInput(editorRef.current)
@@ -188,13 +335,13 @@ export function ChatBar({
188
335
  )
189
336
 
190
337
  useEffect(() => {
191
- if (!disabled) {
338
+ if (!inputDisabled) {
192
339
  focusInput()
193
340
  }
194
- }, [disabled, focusInput, focusKey, focusRequestId])
341
+ }, [focusInput, focusKey, focusRequestId, inputDisabled])
195
342
 
196
343
  useEffect(() => {
197
- if (disabled) {
344
+ if (inputDisabled) {
198
345
  return undefined
199
346
  }
200
347
 
@@ -214,7 +361,7 @@ export function ChatBar({
214
361
  offFocus()
215
362
  offInsert()
216
363
  }
217
- }, [appendExternalText, disabled])
364
+ }, [appendExternalText, inputDisabled])
218
365
 
219
366
  // Keep draftRef in sync with the assistant-ui composer state for callers
220
367
  // that read the latest text outside the React render cycle. We don't push
@@ -240,14 +387,13 @@ export function ChatBar({
240
387
  }
241
388
  }, [urlOpen])
242
389
 
243
- // Track expansion via cheap heuristics (newline or length threshold) instead
244
- // of reading editor.scrollHeight on every keystroke. scrollHeight forces a
245
- // synchronous layout flush measured at 2.27 layouts per character typed
246
- // (see scripts/leak-typing.mjs). With ~30 chars before a typical wrap on
247
- // composer-default-width, this heuristic flips at roughly the right time
248
- // and the user only notices if they type far past the wrap boundary
249
- // without a newline; in that case the ResizeObserver below catches it via
250
- // a height delta and we still expand.
390
+ // Expansion (input on its own full-width row, controls below) is driven by
391
+ // the editor's *actual* rendered height via the ResizeObserver in
392
+ // syncComposerMetrics it only fires when the text genuinely wraps to a
393
+ // second line, so the layout flips exactly at the wrap point rather than at
394
+ // a guessed character count. We only handle the two cases the observer
395
+ // can't: an explicit newline (expand before layout settles) and an emptied
396
+ // draft (collapse back). We never read scrollHeight per keystroke.
251
397
  useEffect(() => {
252
398
  if (!draft) {
253
399
  setExpanded(false)
@@ -259,7 +405,7 @@ export function ChatBar({
259
405
  return
260
406
  }
261
407
 
262
- if (draft.includes('\n') || draft.length > 60) {
408
+ if (draft.includes('\n')) {
263
409
  setExpanded(true)
264
410
  }
265
411
  }, [draft, expanded])
@@ -295,6 +441,18 @@ export function ChatBar({
295
441
  }
296
442
  }
297
443
 
444
+ // Expand once the input has actually wrapped past a single line. The
445
+ // observer only fires on real size changes, so this reads scrollHeight at
446
+ // most once per wrap (not per keystroke). One line ≈ 28px (1.625rem
447
+ // min-height + padding); a second line clears ~36px. We only ever expand
448
+ // here — collapse is handled by the emptied-draft effect to avoid
449
+ // oscillating across the wrap boundary as the input switches widths.
450
+ const editor = editorRef.current
451
+
452
+ if (editor && editor.scrollHeight > COMPOSER_SINGLE_LINE_MAX_PX) {
453
+ setExpanded(true)
454
+ }
455
+
298
456
  if (height > 0) {
299
457
  const bucket = Math.round(height / 8) * 8
300
458
 
@@ -314,7 +472,7 @@ export function ChatBar({
314
472
  }
315
473
  }, [])
316
474
 
317
- useResizeObserver(syncComposerMetrics, composerRef, composerSurfaceRef)
475
+ useResizeObserver(syncComposerMetrics, composerRef, composerSurfaceRef, editorRef)
318
476
 
319
477
  useEffect(() => {
320
478
  return () => {
@@ -349,7 +507,7 @@ export function ChatBar({
349
507
  requestMainFocus()
350
508
  }
351
509
 
352
- const insertInlineRefs = (refs: string[]) => {
510
+ const insertInlineRefs = (refs: InlineRefInput[]) => {
353
511
  const editor = editorRef.current
354
512
 
355
513
  if (!editor) {
@@ -369,51 +527,30 @@ export function ChatBar({
369
527
  return true
370
528
  }
371
529
 
372
- const selectSkinSlashCommand = (command: string) => {
373
- draftRef.current = command
374
- aui.composer().setText(command)
375
- requestMainFocus()
376
- }
530
+ // Latest-closure ref so the (once-only) subscription always calls the current
531
+ // insertInlineRefs without re-subscribing every render.
532
+ const insertInlineRefsRef = useRef(insertInlineRefs)
533
+ insertInlineRefsRef.current = insertInlineRefs
377
534
 
378
- const handlePaste = (event: ClipboardEvent<HTMLDivElement>) => {
379
- const imageBlobs = extractClipboardImageBlobs(event.clipboardData)
380
-
381
- if (imageBlobs.length > 0) {
382
- event.preventDefault()
383
-
384
- if (onAttachImageBlob) {
385
- triggerHaptic('selection')
386
-
387
- for (const blob of imageBlobs) {
388
- void onAttachImageBlob(blob)
389
- }
535
+ useEffect(() => {
536
+ return onComposerInsertRefsRequest(({ refs, target }) => {
537
+ if (target === 'main') {
538
+ insertInlineRefsRef.current(refs)
390
539
  }
391
-
392
- return
393
- }
394
-
395
- const pastedText = event.clipboardData.getData('text')
396
-
397
- if (!pastedText) {
398
- return
399
- }
400
-
401
- if (DATA_IMAGE_URL_RE.test(pastedText.trim())) {
402
- event.preventDefault()
403
-
404
- return
405
- }
406
-
407
- event.preventDefault()
408
- document.execCommand('insertText', false, pastedText)
409
- const nextDraft = composerPlainText(event.currentTarget)
410
- draftRef.current = nextDraft
411
- aui.composer().setText(nextDraft)
412
- }
540
+ })
541
+ }, [])
413
542
 
414
543
  const [trigger, setTrigger] = useState<TriggerState | null>(null)
415
544
  const [triggerActive, setTriggerActive] = useState(0)
416
545
  const [triggerItems, setTriggerItems] = useState<readonly Unstable_TriggerItem[]>([])
546
+ // Set synchronously in keydown when the open trigger popover consumes a
547
+ // navigation/control key (Arrow/Enter/Tab/Escape). The subsequent keyup must
548
+ // NOT run refreshTrigger for that keypress: it never edits text, and for
549
+ // Escape the keydown has already set trigger=null, so a keyup refresh would
550
+ // re-detect the still-present `/` and instantly reopen the menu. A ref is
551
+ // used instead of reading `trigger` in keyup because by keyup time React has
552
+ // re-rendered and the handler closure sees the post-keydown state.
553
+ const triggerKeyConsumedRef = useRef(false)
417
554
 
418
555
  const refreshTrigger = useCallback(() => {
419
556
  const editor = editorRef.current
@@ -439,18 +576,32 @@ export function ChatBar({
439
576
  }
440
577
 
441
578
  const before = textBeforeCaret(editor)
442
- const detected = detectTrigger(before ?? composerPlainText(editor))
579
+ const found = detectTrigger(before ?? composerPlainText(editor))
443
580
 
444
- setTrigger(detected)
445
- setTriggerActive(0)
446
- }, [trigger])
581
+ // The arg-stage popover is only useful for commands with an options screen.
582
+ // For a no-arg command it would dead-end on "No matches", so drop it — the
583
+ // directive is already complete.
584
+ const detected =
585
+ found?.kind === '/' && slashArgStage(found.query) && !desktopSlashCommandTakesArgs(slashCommandToken(found.query))
586
+ ? null
587
+ : found
447
588
 
448
- const handleEditorInput = (event: FormEvent<HTMLDivElement>) => {
449
- const editor = event.currentTarget
589
+ setTrigger(detected)
450
590
 
451
- if (editor.childNodes.length === 1 && editor.firstChild?.nodeName === 'BR') {
452
- editor.replaceChildren()
591
+ // Only reset the highlight when the trigger actually changed (opened, or
592
+ // the query/kind differs). Re-detecting the *same* trigger — e.g. on a
593
+ // caret move (mouseup) or a stray refresh — must preserve the user's
594
+ // current selection instead of snapping back to the first item.
595
+ if (detected?.kind !== trigger?.kind || detected?.query !== trigger?.query) {
596
+ setTriggerActive(0)
453
597
  }
598
+ }, [trigger])
599
+
600
+ // Pull the live contentEditable text into draftRef + the AUI composer state
601
+ // (which drives `hasComposerPayload` → the send button). Shared by the input
602
+ // and compositionend paths so committed IME text reaches state through either.
603
+ const flushEditorToDraft = (editor: HTMLDivElement) => {
604
+ normalizeComposerEditorDom(editor)
454
605
 
455
606
  const nextDraft = composerPlainText(editor)
456
607
 
@@ -462,6 +613,57 @@ export function ChatBar({
462
613
  window.setTimeout(refreshTrigger, 0)
463
614
  }
464
615
 
616
+ const handleEditorInput = (event: FormEvent<HTMLDivElement>) => {
617
+ // During IME composition the DOM contains uncommitted preedit text
618
+ // mixed with real content. Skip state writes — compositionend flushes
619
+ // the finalized text (see onCompositionEnd).
620
+ if (composingRef.current) {
621
+ return
622
+ }
623
+
624
+ flushEditorToDraft(event.currentTarget)
625
+ }
626
+
627
+ const handlePaste = (event: ClipboardEvent<HTMLDivElement>) => {
628
+ const imageBlobs = extractClipboardImageBlobs(event.clipboardData)
629
+
630
+ if (imageBlobs.length > 0) {
631
+ event.preventDefault()
632
+
633
+ if (onAttachImageBlob) {
634
+ triggerHaptic('selection')
635
+
636
+ for (const blob of imageBlobs) {
637
+ void onAttachImageBlob(blob)
638
+ }
639
+ }
640
+
641
+ return
642
+ }
643
+
644
+ // Trim surrounding whitespace so a copy that dragged along leading/trailing
645
+ // blank lines (common when selecting from terminals, code blocks, web pages)
646
+ // doesn't dump multiline padding into the composer. Internal newlines are
647
+ // preserved — only the edges are cleaned up.
648
+ const pastedText = event.clipboardData.getData('text').trim()
649
+
650
+ if (!pastedText) {
651
+ event.preventDefault()
652
+
653
+ return
654
+ }
655
+
656
+ if (DATA_IMAGE_URL_RE.test(pastedText)) {
657
+ event.preventDefault()
658
+
659
+ return
660
+ }
661
+
662
+ event.preventDefault()
663
+ insertPlainTextAtCaret(event.currentTarget, pastedText)
664
+ flushEditorToDraft(event.currentTarget)
665
+ }
666
+
465
667
  const triggerAdapter: Unstable_TriggerAdapter | null =
466
668
  trigger?.kind === '@' ? at.adapter : trigger?.kind === '/' ? slash.adapter : null
467
669
 
@@ -477,6 +679,12 @@ export function ChatBar({
477
679
 
478
680
  const triggerLoading = trigger?.kind === '@' ? at.loading : trigger?.kind === '/' ? slash.loading : false
479
681
 
682
+ // Suppress the "No matches" empty state once a slash command is past its name:
683
+ // a no-arg command has nothing to offer, and a fully-typed arg commits on
684
+ // Space/Tab — neither should dead-end on a popover.
685
+ const argStageEmpty =
686
+ trigger?.kind === '/' && slashArgStage(trigger.query) && !triggerLoading && !triggerItems.length
687
+
480
688
  const closeTrigger = () => {
481
689
  setTrigger(null)
482
690
  setTriggerItems([])
@@ -487,6 +695,25 @@ export function ChatBar({
487
695
  setTriggerActive(idx => Math.min(idx, Math.max(0, triggerItems.length - 1)))
488
696
  }, [triggerItems.length])
489
697
 
698
+ // Commit the literally-typed `/command arg` as a directive chip — used when
699
+ // the completion list is empty because the arg is already fully typed (the
700
+ // backend completer drops exact matches). Reuses the chip path via a
701
+ // synthetic item whose serialized form is the verbatim text.
702
+ const commitTypedSlashDirective = () => {
703
+ if (trigger?.kind !== '/') {
704
+ return
705
+ }
706
+
707
+ const text = `/${trigger.query.trimEnd()}`
708
+
709
+ replaceTriggerWithChip({
710
+ id: text,
711
+ type: 'slash',
712
+ label: text.slice(1),
713
+ metadata: { command: slashCommandToken(trigger.query), display: text, meta: '', group: '', action: '', rawText: text }
714
+ })
715
+ }
716
+
490
717
  const replaceTriggerWithChip = (item: Unstable_TriggerItem) => {
491
718
  const editor = editorRef.current
492
719
 
@@ -494,16 +721,49 @@ export function ChatBar({
494
721
  return
495
722
  }
496
723
 
724
+ // Action items (e.g. "Browse all sessions…") run a side effect instead of
725
+ // inserting a chip: strip the typed trigger token, then fire the action.
726
+ const completionAction = (item.metadata as { action?: unknown } | undefined)?.action
727
+ const runAction = typeof completionAction === 'string' ? COMPLETION_ACTIONS[completionAction] : undefined
728
+
729
+ if (runAction) {
730
+ const current = composerPlainText(editor)
731
+ const prefix = current.slice(0, Math.max(0, current.length - trigger.tokenLength))
732
+
733
+ renderComposerContents(editor, prefix)
734
+ placeCaretEnd(editor)
735
+ draftRef.current = composerPlainText(editor)
736
+ aui.composer().setText(draftRef.current)
737
+ closeTrigger()
738
+ runAction()
739
+ requestMainFocus()
740
+
741
+ return
742
+ }
743
+
497
744
  const serialized = hermesDirectiveFormatter.serialize(item)
498
745
  const starter = serialized.endsWith(':')
746
+
747
+ // Picking a bare arg-taking command (e.g. `/personality`) shouldn't commit
748
+ // it — expand to its options step so the popover shows the inline list, just
749
+ // as typing `/personality ` by hand would. A serialized value with a space is
750
+ // already an arg pick (`/personality alice`), so it commits normally.
751
+ const command = (item.metadata as { command?: string } | undefined)?.command ?? ''
752
+
753
+ const expandsToArgs = trigger.kind === '/' && !serialized.includes(' ') && desktopSlashCommandTakesArgs(command)
754
+
499
755
  const text = starter || serialized.endsWith(' ') ? serialized : `${serialized} `
500
756
  const directive = !starter && serialized.match(/^@([^:]+):(.+)$/)
757
+ // No pill while expanding — the bare command stays plain text until an arg
758
+ // is picked, at which point a single pill is emitted for the full command.
759
+ const slashKind = !expandsToArgs && trigger.kind === '/' ? slashChipKindForItem(item) : null
760
+ const keepTriggerOpen = starter || expandsToArgs
501
761
 
502
762
  const finish = () => {
503
763
  draftRef.current = composerPlainText(editor)
504
764
  aui.composer().setText(draftRef.current)
505
765
  requestMainFocus()
506
- starter ? window.setTimeout(refreshTrigger, 0) : closeTrigger()
766
+ keepTriggerOpen ? window.setTimeout(refreshTrigger, 0) : closeTrigger()
507
767
  }
508
768
 
509
769
  const sel = window.getSelection()
@@ -513,7 +773,20 @@ export function ChatBar({
513
773
 
514
774
  if (!sel || !range || node?.nodeType !== Node.TEXT_NODE || offset < trigger.tokenLength) {
515
775
  const current = composerPlainText(editor)
516
- renderComposerContents(editor, `${current.slice(0, Math.max(0, current.length - trigger.tokenLength))}${text}`)
776
+ const prefix = current.slice(0, Math.max(0, current.length - trigger.tokenLength))
777
+
778
+ if (slashKind) {
779
+ // Two-step arg picks (e.g. `/handoff` pill already inserted, now picking
780
+ // the platform) land here because the caret sits past a contenteditable
781
+ // chip. Rebuild the prefix and re-emit a single pill for the full command.
782
+ renderComposerContents(editor, prefix)
783
+ editor.append(slashChipElement(serialized, slashKind), document.createTextNode(' '))
784
+ placeCaretEnd(editor)
785
+
786
+ return finish()
787
+ }
788
+
789
+ renderComposerContents(editor, `${prefix}${text}`)
517
790
  placeCaretEnd(editor)
518
791
 
519
792
  return finish()
@@ -524,8 +797,13 @@ export function ChatBar({
524
797
  replaceRange.setEnd(node, offset)
525
798
  replaceRange.deleteContents()
526
799
 
527
- if (directive) {
528
- const chip = refChipElement(directive[1], directive[2])
800
+ const chip = slashKind
801
+ ? slashChipElement(serialized, slashKind)
802
+ : directive
803
+ ? refChipElement(directive[1], directive[2])
804
+ : null
805
+
806
+ if (chip) {
529
807
  const space = document.createTextNode(' ')
530
808
  const fragment = document.createDocumentFragment()
531
809
  fragment.append(chip, space)
@@ -545,7 +823,30 @@ export function ChatBar({
545
823
  }
546
824
 
547
825
  const handleEditorKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
548
- if ((event.metaKey || event.ctrlKey) && !event.altKey && !event.shiftKey && event.key.toLowerCase() === 'k') {
826
+ // IME composition: Enter confirms composed text, not a message submission.
827
+ // We check both composingRef (set by compositionstart/compositionend, robust
828
+ // across browsers) and nativeEvent.isComposing (Chromium fallback). Without
829
+ // this guard, pressing Enter to finalise a Korean/Japanese/Chinese IME
830
+ // preedit fires submitDraft() and splits the message mid-word.
831
+ if (composingRef.current || event.nativeEvent.isComposing) {
832
+ return
833
+ }
834
+
835
+ // Non-collapsed Backspace/Delete: native selection-delete is ~O(n²) on large
836
+ // drafts (Ctrl+A → Delete froze ~1.3s). Collapsed carets fall through.
837
+ if (
838
+ (event.key === 'Backspace' || event.key === 'Delete') &&
839
+ deleteSelectionInEditor(event.currentTarget)
840
+ ) {
841
+ event.preventDefault()
842
+ flushEditorToDraft(event.currentTarget)
843
+
844
+ return
845
+ }
846
+
847
+ // Cmd/Ctrl+Shift+K drains the next queued message. Plain Cmd/Ctrl+K is
848
+ // reserved for the global command palette.
849
+ if ((event.metaKey || event.ctrlKey) && !event.altKey && event.shiftKey && event.key.toLowerCase() === 'k') {
549
850
  event.preventDefault()
550
851
 
551
852
  if (!busy) {
@@ -558,6 +859,7 @@ export function ChatBar({
558
859
  if (trigger && triggerItems.length > 0) {
559
860
  if (event.key === 'ArrowDown') {
560
861
  event.preventDefault()
862
+ triggerKeyConsumedRef.current = true
561
863
  setTriggerActive(idx => (idx + 1) % triggerItems.length)
562
864
 
563
865
  return
@@ -565,13 +867,23 @@ export function ChatBar({
565
867
 
566
868
  if (event.key === 'ArrowUp') {
567
869
  event.preventDefault()
870
+ triggerKeyConsumedRef.current = true
568
871
  setTriggerActive(idx => (idx - 1 + triggerItems.length) % triggerItems.length)
569
872
 
570
873
  return
571
874
  }
572
875
 
573
- if (event.key === 'Enter' || event.key === 'Tab') {
876
+ // Enter / Tab / Space all accept the highlighted item: a no-arg command
877
+ // commits its directive chip, an arg-taking command expands to its
878
+ // options step, and an arg option commits the full `/cmd arg` chip. Space
879
+ // is slash-only (an `@` mention takes a literal space) and gated to a
880
+ // non-empty query so a bare `/ ` still types a space.
881
+ const acceptOnSpace = event.key === ' ' && trigger.kind === '/' && Boolean(trigger.query.trim())
882
+ const accept = event.key === 'Enter' || event.key === 'Tab' || acceptOnSpace
883
+
884
+ if (accept) {
574
885
  event.preventDefault()
886
+ triggerKeyConsumedRef.current = true
575
887
  const item = triggerItems[triggerActive]
576
888
 
577
889
  if (item) {
@@ -583,26 +895,181 @@ export function ChatBar({
583
895
 
584
896
  if (event.key === 'Escape') {
585
897
  event.preventDefault()
898
+ triggerKeyConsumedRef.current = true
586
899
  closeTrigger()
587
900
 
588
901
  return
589
902
  }
590
903
  }
591
904
 
905
+ // Arg stage with nothing left to suggest — a fully-typed arg the backend
906
+ // completer no longer echoes (it drops the exact match), e.g.
907
+ // `/personality creative`. Space/Tab still commit what's typed as a single
908
+ // directive chip; Enter falls through to submit (send it as-is).
909
+ if (
910
+ trigger?.kind === '/' &&
911
+ !triggerItems.length &&
912
+ (event.key === ' ' || event.key === 'Tab') &&
913
+ slashArgStage(trigger.query) &&
914
+ trigger.query.trim()
915
+ ) {
916
+ event.preventDefault()
917
+ triggerKeyConsumedRef.current = true
918
+ commitTypedSlashDirective()
919
+
920
+ return
921
+ }
922
+
923
+ // ArrowUp/ArrowDown navigate, in priority order: the queue (edit entries in
924
+ // place) then sent-message history. The history ring is derived from live
925
+ // session messages each press — single source of truth, no mirror.
926
+ if (event.key === 'ArrowUp') {
927
+ const currentDraft = draftRef.current
928
+
929
+ // Editing a queued turn → walk to the older entry.
930
+ if (queueEdit && stepQueuedEdit(-1)) {
931
+ event.preventDefault()
932
+ triggerKeyConsumedRef.current = true
933
+
934
+ return
935
+ }
936
+
937
+ // Empty composer + a queued turn → open the newest queued entry for edit
938
+ // (the row's pencil), not a text recall. Enter saves it back to the queue.
939
+ if (!currentDraft.trim() && !queueEdit && queuedPrompts.length > 0) {
940
+ event.preventDefault()
941
+ triggerKeyConsumedRef.current = true
942
+ beginQueuedEdit(queuedPrompts[queuedPrompts.length - 1]!)
943
+
944
+ return
945
+ }
946
+
947
+ // Don't hijack a typed draft unless already browsing — they'd lose it.
948
+ if (currentDraft.trim() && !isBrowsingHistory(sessionId)) {
949
+ return
950
+ }
951
+
952
+ event.preventDefault()
953
+ triggerKeyConsumedRef.current = true
954
+
955
+ // $messages is read imperatively (not subscribed) so the composer
956
+ // doesn't re-render on every streaming delta flush.
957
+ const history = deriveUserHistory($messages.get(), chatMessageText)
958
+ const entry = browseBackward(sessionId, currentDraft, history)
959
+
960
+ if (entry !== null) {
961
+ loadIntoComposer(entry, $composerAttachments.get())
962
+ }
963
+
964
+ return
965
+ }
966
+
967
+ if (event.key === 'ArrowDown') {
968
+ // Editing a queued turn → walk to the newer entry (past the newest exits).
969
+ if (queueEdit) {
970
+ event.preventDefault()
971
+ triggerKeyConsumedRef.current = true
972
+ stepQueuedEdit(1)
973
+
974
+ return
975
+ }
976
+
977
+ // Browsing sent history → step toward the present, restoring the draft.
978
+ if (isBrowsingHistory(sessionId)) {
979
+ event.preventDefault()
980
+ triggerKeyConsumedRef.current = true
981
+
982
+ const history = deriveUserHistory($messages.get(), chatMessageText)
983
+ const result = browseForward(sessionId, history)
984
+
985
+ if (result !== null) {
986
+ loadIntoComposer(result.text, $composerAttachments.get())
987
+ }
988
+ }
989
+
990
+ return
991
+ }
992
+
993
+ // Cmd/Ctrl+Enter is reserved for steering the live run — never a send.
994
+ // Steer when there's a steerable draft, otherwise swallow it so it can't
995
+ // surprise-send. (Plain Enter still queues while busy / sends when idle.)
996
+ if (event.key === 'Enter' && (event.metaKey || event.ctrlKey) && !event.shiftKey) {
997
+ event.preventDefault()
998
+
999
+ if (canSteer) {
1000
+ steerDraft()
1001
+ }
1002
+
1003
+ return
1004
+ }
1005
+
592
1006
  if (event.key === 'Enter' && !event.shiftKey) {
593
1007
  event.preventDefault()
594
1008
 
595
- if (!busy && !hasComposerPayload && queuedPrompts.length > 0) {
1009
+ // Decide from the DOM, not React state. `hasComposerPayload` is derived
1010
+ // from the AUI composer state, which lags the latest keystroke by a
1011
+ // render, so on fast typing / IME the just-typed text isn't in state yet.
1012
+ // Without the live read, a real message typed while prompts are queued
1013
+ // would drain the queue instead of sending. submitDraft() re-syncs and
1014
+ // sends the live editor text.
1015
+ const editorText = editorRef.current ? composerPlainText(editorRef.current) : draftRef.current
1016
+ const hasLivePayload = editorText.trim().length > 0 || attachments.length > 0
1017
+
1018
+ if (disabled) {
1019
+ return
1020
+ }
1021
+
1022
+ if (!busy && !hasLivePayload && queuedPrompts.length > 0) {
596
1023
  void drainNextQueued()
597
1024
 
598
1025
  return
599
1026
  }
600
1027
 
1028
+ // Empty Enter while busy is a no-op — interrupting is explicit (Stop/Esc),
1029
+ // never a stray Enter after sending. With a payload, submitDraft queues it.
1030
+ // Gate on the live DOM payload (not the render-lagged composer state) so a
1031
+ // message typed fast / via IME while busy still reaches submitDraft() and
1032
+ // gets queued instead of being mistaken for an empty Enter.
1033
+ if (busy && !hasLivePayload) {
1034
+ return
1035
+ }
1036
+
601
1037
  submitDraft()
1038
+
1039
+ return
1040
+ }
1041
+
1042
+ if (event.key === 'Escape') {
1043
+ // Editing a queued turn → Esc cancels the edit, restoring the prior draft.
1044
+ if (queueEdit) {
1045
+ event.preventDefault()
1046
+ exitQueuedEdit('cancel')
1047
+
1048
+ return
1049
+ }
1050
+
1051
+ // Otherwise Esc interrupts the running turn (Stop-button parity).
1052
+ if (busy) {
1053
+ event.preventDefault()
1054
+ triggerHaptic('cancel')
1055
+ void Promise.resolve(onCancel())
1056
+ }
602
1057
  }
603
1058
  }
604
1059
 
605
1060
  const handleEditorKeyUp = () => {
1061
+ // If this keyup belongs to a key the open trigger popover already consumed
1062
+ // in keydown (Arrow/Enter/Tab/Escape), skip the refresh. Those keys never
1063
+ // edit text, and for Escape the keydown already closed the menu — a refresh
1064
+ // here would re-detect the still-present `/` and instantly reopen it. We
1065
+ // read a ref set during keydown rather than `trigger`, because by keyup
1066
+ // time React has re-rendered and `trigger` may already be null.
1067
+ if (triggerKeyConsumedRef.current) {
1068
+ triggerKeyConsumedRef.current = false
1069
+
1070
+ return
1071
+ }
1072
+
606
1073
  window.setTimeout(refreshTrigger, 0)
607
1074
  }
608
1075
 
@@ -660,24 +1127,25 @@ export function ChatBar({
660
1127
  return
661
1128
  }
662
1129
 
663
- if (Array.from(event.dataTransfer.types || []).includes(HERMES_PATHS_MIME)) {
664
- const refs = candidates
665
- .map(candidate => droppedFileInlineRef(candidate, cwd))
666
- .filter((ref): ref is string => Boolean(ref))
1130
+ // In-app drags (project tree / gutter) are workspace-relative paths the
1131
+ // gateway resolves directly, so they stay inline @file:/@line: refs. OS
1132
+ // drops are absolute local paths a remote gateway can't read (and images
1133
+ // need byte upload for vision), so route them through the upload pipeline.
1134
+ const { inAppRefs, osDrops } = partitionDroppedFiles(candidates)
1135
+ const refs = droppedFileInlineRefs(inAppRefs, cwd)
667
1136
 
668
- if (insertInlineRefs(refs)) {
669
- triggerHaptic('selection')
670
- }
671
-
672
- return
1137
+ if (refs.length && insertInlineRefs(refs)) {
1138
+ triggerHaptic('selection')
673
1139
  }
674
1140
 
675
- void Promise.resolve(onAttachDroppedItems(candidates)).then(attached => {
676
- if (attached) {
677
- triggerHaptic('selection')
678
- requestMainFocus()
679
- }
680
- })
1141
+ if (osDrops.length) {
1142
+ void Promise.resolve(onAttachDroppedItems(osDrops)).then(attached => {
1143
+ if (attached) {
1144
+ triggerHaptic('selection')
1145
+ requestMainFocus()
1146
+ }
1147
+ })
1148
+ }
681
1149
  }
682
1150
 
683
1151
  const handleInputDragOver = (event: ReactDragEvent<HTMLDivElement>) => {
@@ -697,11 +1165,7 @@ export function ChatBar({
697
1165
 
698
1166
  const candidates = extractDroppedFiles(event.dataTransfer)
699
1167
 
700
- const refs = candidates
701
- .map(candidate => droppedFileInlineRef(candidate, cwd))
702
- .filter((ref): ref is string => Boolean(ref))
703
-
704
- if (!refs.length) {
1168
+ if (!candidates.length) {
705
1169
  return
706
1170
  }
707
1171
 
@@ -709,9 +1173,27 @@ export function ChatBar({
709
1173
  event.stopPropagation()
710
1174
  resetDragState()
711
1175
 
712
- if (insertInlineRefs(refs)) {
1176
+ // Dropping straight onto the text box used to inline-ref *every* file —
1177
+ // including OS/Finder drops, whose absolute local path a remote gateway
1178
+ // can't read and whose image bytes never reached vision. Split by origin:
1179
+ // in-app drags stay inline refs; OS drops go through the upload pipeline.
1180
+ // (When no upload handler is wired, fall back to inline refs for all.)
1181
+ const attach = onAttachDroppedItems
1182
+ const { inAppRefs, osDrops } = partitionDroppedFiles(candidates)
1183
+ const refs = droppedFileInlineRefs(attach ? inAppRefs : candidates, cwd)
1184
+
1185
+ if (refs.length && insertInlineRefs(refs)) {
713
1186
  triggerHaptic('selection')
714
1187
  }
1188
+
1189
+ if (attach && osDrops.length) {
1190
+ void Promise.resolve(attach(osDrops)).then(attached => {
1191
+ if (attached) {
1192
+ triggerHaptic('selection')
1193
+ requestMainFocus()
1194
+ }
1195
+ })
1196
+ }
715
1197
  }
716
1198
 
717
1199
  const clearDraft = useCallback(() => {
@@ -736,6 +1218,66 @@ export function ChatBar({
736
1218
  }
737
1219
  }
738
1220
 
1221
+ const stashAt = (scope: string | null, text = draftRef.current, attachments = $composerAttachments.get()) =>
1222
+ stashSessionDraft(scope, text, attachments)
1223
+
1224
+ // Per-thread draft swap — the composer's only session coupling. Lifecycle
1225
+ // never clears composer state; this effect alone stashes on leave, restores
1226
+ // on enter. Keyed writes are idempotent, so no skip-sentinel.
1227
+ useEffect(() => {
1228
+ const { attachments, text } = takeSessionDraft(activeQueueSessionKey)
1229
+ loadIntoComposer(text, attachments)
1230
+
1231
+ return () => {
1232
+ const editing = queueEditRef.current
1233
+
1234
+ if (editing?.sessionKey === activeQueueSessionKey) {
1235
+ stashAt(activeQueueSessionKey, editing.draft, editing.attachments)
1236
+ } else if (!isBrowsingHistory(sessionId)) {
1237
+ stashAt(activeQueueSessionKey)
1238
+ }
1239
+ }
1240
+ }, [activeQueueSessionKey]) // eslint-disable-line react-hooks/exhaustive-deps
1241
+
1242
+ // Debounced stash into the active scope. Skipped while browsing history or
1243
+ // editing a queued prompt — recalled text must not clobber the real draft.
1244
+ useEffect(() => {
1245
+ if (isBrowsingHistory(sessionId) || queueEdit) {
1246
+ return
1247
+ }
1248
+
1249
+ pendingDraftPersistRef.current = { scope: activeQueueSessionKey, text: draft }
1250
+
1251
+ const handle = window.setTimeout(() => {
1252
+ pendingDraftPersistRef.current = null
1253
+ stashAt(activeQueueSessionKey, draft)
1254
+ }, DRAFT_PERSIST_DEBOUNCE_MS)
1255
+
1256
+ return () => window.clearTimeout(handle)
1257
+ }, [activeQueueSessionKey, draft, queueEdit, sessionId])
1258
+
1259
+ // pagehide is load-bearing: React skips effect cleanups on reload, so Cmd+R
1260
+ // inside the debounce window would drop trailing keystrokes without this.
1261
+ useEffect(() => {
1262
+ const flushPendingDraftPersist = () => {
1263
+ const pending = pendingDraftPersistRef.current
1264
+
1265
+ if (!pending) {
1266
+ return
1267
+ }
1268
+
1269
+ pendingDraftPersistRef.current = null
1270
+ stashAt(pending.scope, pending.text)
1271
+ }
1272
+
1273
+ window.addEventListener('pagehide', flushPendingDraftPersist)
1274
+
1275
+ return () => {
1276
+ window.removeEventListener('pagehide', flushPendingDraftPersist)
1277
+ flushPendingDraftPersist()
1278
+ }
1279
+ }, [])
1280
+
739
1281
  const beginQueuedEdit = (entry: QueuedPromptEntry) => {
740
1282
  if (!activeQueueSessionKey || queueEdit) {
741
1283
  return
@@ -752,6 +1294,42 @@ export function ChatBar({
752
1294
  focusInput()
753
1295
  }
754
1296
 
1297
+ // Walk queued entries while editing (ArrowUp = older, ArrowDown = newer),
1298
+ // saving the in-progress edit on each step. Stepping newer past the last
1299
+ // entry exits edit mode and restores the pre-edit draft.
1300
+ const stepQueuedEdit = (direction: -1 | 1) => {
1301
+ if (!queueEdit) {
1302
+ return false
1303
+ }
1304
+
1305
+ const index = queuedPrompts.findIndex(e => e.id === queueEdit.entryId)
1306
+ const target = index + direction
1307
+
1308
+ if (index < 0 || target < 0) {
1309
+ return index >= 0 // at the oldest: swallow; missing entry: let it fall through
1310
+ }
1311
+
1312
+ const saved = updateQueuedPrompt(queueEdit.sessionKey, queueEdit.entryId, {
1313
+ attachments: cloneAttachments($composerAttachments.get()),
1314
+ text: draftRef.current
1315
+ })
1316
+
1317
+ const next = queuedPrompts[target]
1318
+
1319
+ if (next) {
1320
+ setQueueEdit({ ...queueEdit, entryId: next.id })
1321
+ loadIntoComposer(next.text, next.attachments)
1322
+ } else {
1323
+ setQueueEdit(null)
1324
+ loadIntoComposer(queueEdit.draft, queueEdit.attachments)
1325
+ }
1326
+
1327
+ triggerHaptic(saved ? 'success' : 'selection')
1328
+ focusInput()
1329
+
1330
+ return true
1331
+ }
1332
+
755
1333
  const exitQueuedEdit = (action: 'cancel' | 'save'): boolean => {
756
1334
  if (!queueEdit) {
757
1335
  return false
@@ -794,6 +1372,26 @@ export function ChatBar({
794
1372
  return true
795
1373
  }, [activeQueueSessionKey, attachments, clearDraft, draft])
796
1374
 
1375
+ // Steer the live turn (nudge without interrupting). Clears the draft up front
1376
+ // for snappy feedback; if the gateway rejects (no live tool window) the words
1377
+ // are re-queued so nothing is lost — same safety net as a plain queue.
1378
+ const steerDraft = useCallback(() => {
1379
+ if (!onSteer || !canSteer) {
1380
+ return
1381
+ }
1382
+
1383
+ const text = draftRef.current.trim()
1384
+
1385
+ triggerHaptic('submit')
1386
+ clearDraft()
1387
+
1388
+ void Promise.resolve(onSteer(text)).then(accepted => {
1389
+ if (!accepted && activeQueueSessionKey) {
1390
+ enqueueQueuedPrompt(activeQueueSessionKey, { text, attachments: [] })
1391
+ }
1392
+ })
1393
+ }, [activeQueueSessionKey, canSteer, clearDraft, onSteer])
1394
+
797
1395
  // All queue drain paths share one lock + send-then-remove sequence.
798
1396
  // `pickEntry` lets each caller choose head, by-id, or skip-edited.
799
1397
  const runDrain = useCallback(
@@ -819,86 +1417,207 @@ export function ChatBar({
819
1417
  return false
820
1418
  }
821
1419
 
1420
+ drainFailuresRef.current.delete(entry.id)
822
1421
  removeQueuedPrompt(activeQueueSessionKey, entry.id)
1422
+ resetBrowseState(sessionId)
823
1423
 
824
1424
  return true
825
1425
  } finally {
826
1426
  drainingQueueRef.current = false
827
1427
  }
828
1428
  },
829
- [activeQueueSessionKey, onSubmit, queuedPrompts]
1429
+ [activeQueueSessionKey, onSubmit, queuedPrompts, sessionId]
830
1430
  )
831
1431
 
832
- const drainNextQueued = useCallback(
833
- () =>
834
- runDrain(entries => {
835
- const skip = queueEdit?.entryId
1432
+ const pickDrainHead = useCallback(
1433
+ (entries: QueuedPromptEntry[]) => {
1434
+ const skip = queueEditRef.current?.entryId
836
1435
 
837
- return skip ? entries.find(e => e.id !== skip) : entries[0]
838
- }),
839
- [queueEdit, runDrain]
1436
+ return skip ? entries.find(e => e.id !== skip) : entries[0]
1437
+ },
1438
+ [] // reads the edit id off a ref so the lock-holder always sees the latest
840
1439
  )
841
1440
 
1441
+ const drainNextQueued = useCallback(() => runDrain(pickDrainHead), [pickDrainHead, runDrain])
1442
+
842
1443
  const sendQueuedNow = useCallback(
843
- (id: string) => runDrain(entries => entries.find(e => e.id === id && id !== queueEdit?.entryId)),
844
- [queueEdit, runDrain]
1444
+ (id: string) => {
1445
+ if (!activeQueueSessionKey || id === queueEdit?.entryId) {
1446
+ return false
1447
+ }
1448
+
1449
+ if (busy) {
1450
+ // Promote to the head, then interrupt. The gateway always emits a
1451
+ // settle (message.complete + session.info running:false) when the
1452
+ // turn unwinds, and the busy→false auto-drain below sends this entry.
1453
+ promoteQueuedPrompt(activeQueueSessionKey, id)
1454
+ triggerHaptic('selection')
1455
+ void Promise.resolve(onCancel())
1456
+
1457
+ return true
1458
+ }
1459
+
1460
+ // A manual send clears the auto-drain backoff so a stuck entry the user
1461
+ // taps gets a fresh attempt (and re-enables auto-retry on success).
1462
+ drainFailuresRef.current.delete(id)
1463
+
1464
+ return runDrain(entries => entries.find(e => e.id === id))
1465
+ },
1466
+ [activeQueueSessionKey, busy, onCancel, queueEdit, runDrain]
845
1467
  )
846
1468
 
847
- const interruptAndSendNextQueued = useCallback(async () => {
848
- if (queuedPrompts.length === 0) {
849
- return false
1469
+ // Edge-independent auto-drain: send the head whenever the session is idle and
1470
+ // the queue is non-empty, bounding retries so a thrown/rejected onSubmit (e.g.
1471
+ // a stale-session 404) can't strand the entry permanently nor spin-loop. The
1472
+ // drain lock serializes sends; a remount/reconnect resets the failure counts.
1473
+ const autoDrainNext = useCallback(() => {
1474
+ if (busy || drainingQueueRef.current || !activeQueueSessionKey) {
1475
+ return
850
1476
  }
851
1477
 
852
- await Promise.resolve(onCancel())
1478
+ const entry = pickDrainHead(queuedPrompts)
853
1479
 
854
- return drainNextQueued()
855
- }, [drainNextQueued, onCancel, queuedPrompts.length])
1480
+ if (!entry || (drainFailuresRef.current.get(entry.id) ?? 0) >= MAX_AUTO_DRAIN_ATTEMPTS) {
1481
+ return
1482
+ }
856
1483
 
857
- // Auto-drain on busy false (turn settled).
1484
+ const onFail = () => {
1485
+ const fails = (drainFailuresRef.current.get(entry.id) ?? 0) + 1
1486
+ drainFailuresRef.current.set(entry.id, fails)
1487
+
1488
+ if (fails >= MAX_AUTO_DRAIN_ATTEMPTS) {
1489
+ notify({
1490
+ id: 'composer-queue-stuck',
1491
+ kind: 'error',
1492
+ title: t.composer.queueStuckTitle,
1493
+ message: t.composer.queueStuckBody
1494
+ })
1495
+ }
1496
+ }
1497
+
1498
+ void runDrain(() => entry)
1499
+ .then(sent => {
1500
+ if (!sent) {
1501
+ onFail()
1502
+ }
1503
+ })
1504
+ .catch(onFail)
1505
+ }, [activeQueueSessionKey, busy, pickDrainHead, queuedPrompts, runDrain, t])
1506
+
1507
+ // Re-key on a runtime session-id change. A stable stored id (queueSessionKey)
1508
+ // never churns, so a change there is a real session switch and must NOT
1509
+ // migrate; only the runtime-derived key (queueSessionKey falsy → key is
1510
+ // sessionId) churns on a backend bounce/resume of the same conversation.
858
1511
  useEffect(() => {
859
- const wasBusy = previousBusyRef.current
860
- previousBusyRef.current = busy
1512
+ const prev = prevQueueKeyRef.current
1513
+ prevQueueKeyRef.current = activeQueueSessionKey
861
1514
 
862
- if (busy || !wasBusy || queuedPrompts.length === 0) {
1515
+ if (queueSessionKey || !prev || !activeQueueSessionKey || prev === activeQueueSessionKey) {
863
1516
  return
864
1517
  }
865
1518
 
866
- void drainNextQueued()
867
- }, [busy, drainNextQueued, queuedPrompts.length])
1519
+ migrateQueuedPrompts(prev, activeQueueSessionKey)
1520
+ }, [activeQueueSessionKey, queueSessionKey])
868
1521
 
869
- // Clean up queue edit when its target disappears (session swap or external delete).
1522
+ // Queued turns flow whenever the session is idle on the busy→false settle
1523
+ // edge, on mount/reconnect, and after a re-key — so a swallowed edge can't
1524
+ // strand them. To cancel queued turns, the user deletes them from the panel.
1525
+ useEffect(() => {
1526
+ if (shouldAutoDrain({ isBusy: busy, queueLength: queuedPrompts.length })) {
1527
+ autoDrainNext()
1528
+ }
1529
+ }, [autoDrainNext, busy, queuedPrompts.length])
1530
+
1531
+ // Queue-edit cleanup: on session swap the scope effect already stashed the
1532
+ // edit snapshot; only restore into the composer when still on the same scope.
870
1533
  useEffect(() => {
871
1534
  if (!queueEdit) {
872
1535
  return
873
1536
  }
874
1537
 
875
- if (queueEdit.sessionKey === activeQueueSessionKey && editingQueuedPrompt) {
876
- return
1538
+ if (queueEdit.sessionKey === activeQueueSessionKey) {
1539
+ if (editingQueuedPrompt) {
1540
+ return
1541
+ }
1542
+
1543
+ loadIntoComposer(queueEdit.draft, queueEdit.attachments)
877
1544
  }
878
1545
 
879
- loadIntoComposer(queueEdit.draft, queueEdit.attachments)
880
1546
  setQueueEdit(null)
881
1547
  }, [activeQueueSessionKey, editingQueuedPrompt, queueEdit]) // eslint-disable-line react-hooks/exhaustive-deps
882
1548
 
1549
+ const dispatchSubmit = (text: string, attachments?: ComposerAttachment[]) => {
1550
+ const submittedScope = activeQueueSessionKeyRef.current
1551
+ const submittedAttachments = attachments ?? []
1552
+
1553
+ const restore = () => {
1554
+ loadIntoComposer(text, submittedAttachments)
1555
+ stashAt(activeQueueSessionKeyRef.current, text, submittedAttachments)
1556
+ }
1557
+
1558
+ void Promise.resolve(attachments ? onSubmit(text, { attachments }) : onSubmit(text))
1559
+ .then(accepted => void (accepted === false ? restore() : clearSessionDraft(submittedScope)))
1560
+ .catch(restore)
1561
+ }
1562
+
883
1563
  const submitDraft = () => {
1564
+ if (disabled) {
1565
+ return
1566
+ }
1567
+
1568
+ // Source the text from the DOM editor, not React state. The AUI composer
1569
+ // state (`draft`) and the derived `hasComposerPayload` lag the DOM by a
1570
+ // render, so on fast typing or IME composition the final keystroke(s) may
1571
+ // not have synced yet — reading state here drops the message (Enter looks
1572
+ // like it does nothing; typing a trailing space only "fixes" it because the
1573
+ // extra input event forces a state sync). draftRef is updated on every
1574
+ // input event; refresh it from the editor once more to also cover an
1575
+ // in-flight keystroke that hasn't fired its input event yet.
1576
+ const editor = editorRef.current
1577
+
1578
+ if (editor) {
1579
+ const domText = composerPlainText(editor)
1580
+
1581
+ if (domText !== draftRef.current) {
1582
+ draftRef.current = domText
1583
+ aui.composer().setText(domText)
1584
+ }
1585
+ }
1586
+
1587
+ const text = draftRef.current
1588
+ const payloadPresent = text.trim().length > 0 || attachments.length > 0
1589
+
884
1590
  if (queueEdit) {
885
1591
  exitQueuedEdit('save')
886
1592
  } else if (busy) {
887
- if (hasComposerPayload) {
1593
+ // Slash commands should execute immediately even while the agent is
1594
+ // busy — they're client-side operations (/yolo, /skin, /new, /help,
1595
+ // etc.) or self-contained gateway RPCs (/status, /compress). onSubmit
1596
+ // routes them to executeSlashCommand, which has its own per-command
1597
+ // busy guard for commands that genuinely need an idle session (skill
1598
+ // /send directives). Queuing them would make every slash command wait
1599
+ // for the current turn to finish, which is how the TUI never behaves.
1600
+ if (!attachments.length && SLASH_COMMAND_RE.test(text.trim())) {
1601
+ triggerHaptic('submit')
1602
+ clearDraft()
1603
+ dispatchSubmit(text)
1604
+ } else if (payloadPresent) {
888
1605
  queueCurrentDraft()
889
- } else if (queuedPrompts.length > 0) {
890
- void interruptAndSendNextQueued()
891
1606
  } else {
1607
+ // Stop button (the only way to reach here while busy with an empty
1608
+ // composer — empty Enter is short-circuited in the keydown handler).
892
1609
  triggerHaptic('cancel')
893
1610
  void Promise.resolve(onCancel())
894
1611
  }
895
- } else if (!hasComposerPayload && queuedPrompts.length > 0) {
1612
+ } else if (!payloadPresent && queuedPrompts.length > 0) {
896
1613
  void drainNextQueued()
897
- } else if (draft.trim() || attachments.length > 0) {
898
- const submitted = draft
1614
+ } else if (payloadPresent) {
1615
+ const submittedAttachments = cloneAttachments(attachments)
899
1616
  triggerHaptic('submit')
1617
+ resetBrowseState(sessionId)
900
1618
  clearDraft()
901
- void onSubmit(submitted)
1619
+ clearComposerAttachments()
1620
+ dispatchSubmit(text, submittedAttachments)
902
1621
  }
903
1622
 
904
1623
  focusInput()
@@ -965,6 +1684,7 @@ export function ChatBar({
965
1684
  }
966
1685
 
967
1686
  triggerHaptic('submit')
1687
+ resetBrowseState(sessionId)
968
1688
  clearDraft()
969
1689
  await onSubmit(text)
970
1690
  }
@@ -998,6 +1718,7 @@ export function ChatBar({
998
1718
  <ComposerControls
999
1719
  busy={busy}
1000
1720
  busyAction={busyAction}
1721
+ canSteer={canSteer}
1001
1722
  canSubmit={canSubmit}
1002
1723
  conversation={{
1003
1724
  active: voiceConversationActive,
@@ -1015,6 +1736,7 @@ export function ChatBar({
1015
1736
  disabled={disabled}
1016
1737
  hasComposerPayload={hasComposerPayload}
1017
1738
  onDictate={dictate}
1739
+ onSteer={steerDraft}
1018
1740
  state={state}
1019
1741
  voiceStatus={voiceStatus}
1020
1742
  />
@@ -1023,18 +1745,36 @@ export function ChatBar({
1023
1745
  const input = (
1024
1746
  <div className={cn('relative', stacked ? 'w-full' : 'min-w-(--composer-input-inline-min-width) flex-1')}>
1025
1747
  <div
1026
- aria-label="Message"
1748
+ aria-disabled={inputDisabled ? true : undefined}
1749
+ aria-label={t.composer.message}
1750
+ autoCapitalize="off"
1751
+ autoCorrect="off"
1027
1752
  className={cn(
1028
- 'min-h-(--composer-input-min-height) max-h-(--composer-input-max-height) overflow-y-auto bg-transparent pb-1 pr-1 pt-1 leading-normal text-foreground outline-none disabled:cursor-not-allowed',
1753
+ 'min-h-(--composer-input-min-height) max-h-(--composer-input-max-height) overflow-y-auto whitespace-pre-wrap break-words [overflow-wrap:anywhere] bg-transparent pb-1 pr-1 pt-1 leading-normal text-foreground outline-none disabled:cursor-not-allowed',
1029
1754
  'empty:before:content-[attr(data-placeholder)] empty:before:text-muted-foreground/60',
1030
1755
  '**:data-ref-text:cursor-default',
1031
1756
  stacked && 'pl-3',
1032
1757
  stacked ? 'w-full' : 'min-w-(--composer-input-inline-min-width) flex-1'
1033
1758
  )}
1034
- contentEditable={!disabled}
1759
+ contentEditable={!inputDisabled}
1035
1760
  data-placeholder={placeholder}
1036
1761
  data-slot={RICH_INPUT_SLOT}
1037
1762
  onBlur={() => window.setTimeout(closeTrigger, 80)}
1763
+ onCompositionEnd={event => {
1764
+ composingRef.current = false
1765
+
1766
+ // The input events fired *during* composition were skipped (they
1767
+ // carried uncommitted preedit text), and Chromium does NOT reliably
1768
+ // emit a trailing input event after compositionend on Windows IMEs.
1769
+ // Without flushing here, committed multi-character IME input (e.g.
1770
+ // Chinese "你好", Japanese, Korean) never reaches composer state, so
1771
+ // `hasComposerPayload` stays false and the send button stays hidden
1772
+ // until an unrelated edit forces a sync (#39614).
1773
+ flushEditorToDraft(event.currentTarget)
1774
+ }}
1775
+ onCompositionStart={() => {
1776
+ composingRef.current = true
1777
+ }}
1038
1778
  onDragOver={handleInputDragOver}
1039
1779
  onDrop={handleInputDrop}
1040
1780
  onFocus={() => markActiveComposer('main')}
@@ -1045,6 +1785,7 @@ export function ChatBar({
1045
1785
  onPaste={handlePaste}
1046
1786
  ref={editorRef}
1047
1787
  role="textbox"
1788
+ spellCheck={false}
1048
1789
  suppressContentEditableWarning
1049
1790
  />
1050
1791
  {/* assistant-ui requires ComposerPrimitive.Input somewhere in the tree
@@ -1062,8 +1803,16 @@ export function ChatBar({
1062
1803
 
1063
1804
  `asChild` swaps TextareaAutosize for a Radix Slot wrapping our
1064
1805
  plain <textarea>, which carries the binding but skips autosize. */}
1065
- <ComposerPrimitive.Input asChild tabIndex={-1} unstable_focusOnScrollToBottom={false}>
1066
- <textarea aria-hidden className="sr-only" tabIndex={-1} />
1806
+ <ComposerPrimitive.Input asChild submitMode="ctrlEnter" tabIndex={-1} unstable_focusOnScrollToBottom={false}>
1807
+ <textarea
1808
+ aria-hidden
1809
+ autoCapitalize="off"
1810
+ autoComplete="off"
1811
+ autoCorrect="off"
1812
+ className="sr-only"
1813
+ spellCheck={false}
1814
+ tabIndex={-1}
1815
+ />
1067
1816
  </ComposerPrimitive.Input>
1068
1817
  </div>
1069
1818
  )
@@ -1075,6 +1824,7 @@ export function ChatBar({
1075
1824
  className="group/composer absolute bottom-0 left-1/2 z-30 w-[min(var(--composer-width),calc(100%-2rem))] max-w-full -translate-x-1/2 rounded-2xl pt-2 pb-[var(--composer-shell-pad-block-end)]"
1076
1825
  data-drag-active={dragActive ? '' : undefined}
1077
1826
  data-slot="composer-root"
1827
+ data-status-stack={statusStackVisible ? '' : undefined}
1078
1828
  data-thread-scrolled-up={scrolledUp ? '' : undefined}
1079
1829
  onDragEnter={handleDragEnter}
1080
1830
  onDragLeave={handleDragLeave}
@@ -1082,12 +1832,17 @@ export function ChatBar({
1082
1832
  onDrop={handleDrop}
1083
1833
  onSubmit={e => {
1084
1834
  e.preventDefault()
1835
+
1836
+ if (composingRef.current) {
1837
+ return
1838
+ }
1839
+
1085
1840
  submitDraft()
1086
1841
  }}
1087
1842
  ref={composerRef}
1088
1843
  >
1089
1844
  {showHelpHint && <HelpHint />}
1090
- {trigger && (
1845
+ {trigger && !argStageEmpty && (
1091
1846
  <ComposerTriggerPopover
1092
1847
  activeIndex={triggerActive}
1093
1848
  items={triggerItems}
@@ -1097,23 +1852,30 @@ export function ChatBar({
1097
1852
  onPick={replaceTriggerWithChip}
1098
1853
  />
1099
1854
  )}
1100
- <SkinSlashPopover draft={draft} onSelect={selectSkinSlashCommand} />
1101
- {activeQueueSessionKey && queuedPrompts.length > 0 && (
1102
- <div className="relative z-6 mb-1 px-0.5">
1103
- <QueuePanel
1104
- busy={busy}
1105
- editingId={queueEdit?.entryId ?? null}
1106
- entries={queuedPrompts}
1107
- onDelete={id => {
1108
- if (removeQueuedPrompt(activeQueueSessionKey, id) && queueEdit?.entryId === id) {
1109
- exitQueuedEdit('cancel')
1110
- }
1111
- }}
1112
- onEdit={beginQueuedEdit}
1113
- onSendNow={id => void sendQueuedNow(id)}
1114
- />
1115
- </div>
1116
- )}
1855
+ {/* Session-scoped status stack (todos, subagents, background tasks,
1856
+ queue). Out of flow so it never inflates the composer's measured
1857
+ height; it overlays the chat instead of pushing it, and publishes
1858
+ its own --status-stack-measured-height so the thread's clearance
1859
+ accounts for it. Collapses to nothing when every status is empty. */}
1860
+ <ComposerStatusStack
1861
+ queue={
1862
+ activeQueueSessionKey && queuedPrompts.length > 0 ? (
1863
+ <QueuePanel
1864
+ busy={busy}
1865
+ editingId={queueEdit?.entryId ?? null}
1866
+ entries={queuedPrompts}
1867
+ onDelete={id => {
1868
+ if (removeQueuedPrompt(activeQueueSessionKey, id) && queueEdit?.entryId === id) {
1869
+ exitQueuedEdit('cancel')
1870
+ }
1871
+ }}
1872
+ onEdit={beginQueuedEdit}
1873
+ onSendNow={id => void sendQueuedNow(id)}
1874
+ />
1875
+ ) : null
1876
+ }
1877
+ sessionId={statusSessionId}
1878
+ />
1117
1879
  <div
1118
1880
  className="pointer-events-none absolute inset-0 rounded-[inherit]"
1119
1881
  style={{ background: COMPOSER_FADE_BACKGROUND }}
@@ -1121,11 +1883,9 @@ export function ChatBar({
1121
1883
  <div className="relative w-full rounded-[inherit]">
1122
1884
  <div
1123
1885
  className={cn(
1124
- 'relative z-4 isolate rounded-[inherit] border border-[color-mix(in_srgb,var(--dt-composer-ring)_calc(18%*var(--composer-ring-strength)),var(--dt-input))] shadow-composer transition-[border-color,box-shadow] duration-200 ease-out',
1886
+ 'group/composer-surface relative z-4 isolate rounded-[inherit] border border-[color-mix(in_srgb,var(--dt-composer-ring)_calc(18%*var(--composer-ring-strength)),var(--dt-input))] transition-[border-color] duration-200 ease-out focus-within:border-[color-mix(in_srgb,var(--dt-composer-ring)_calc(45%*var(--composer-ring-strength)),transparent)]',
1125
1887
  COMPOSER_DROP_FADE_CLASS,
1126
- 'group-focus-within/composer:border-[color-mix(in_srgb,var(--dt-composer-ring)_calc(45%*var(--composer-ring-strength)),transparent)] group-focus-within/composer:shadow-composer-focus',
1127
1888
  'group-has-data-[state=open]/composer:border-t-transparent',
1128
- 'group-has-data-[state=open]/composer:shadow-[0_0.0625rem_0_0.0625rem_color-mix(in_srgb,var(--dt-composer-ring)_calc(35%*var(--composer-ring-strength)),transparent),0_0.5rem_1.5rem_color-mix(in_srgb,var(--shadow-ink)_6%,transparent)]',
1129
1889
  dragActive && COMPOSER_DROP_ACTIVE_CLASS
1130
1890
  )}
1131
1891
  data-slot="composer-surface"
@@ -1135,20 +1895,14 @@ export function ChatBar({
1135
1895
  aria-hidden
1136
1896
  className={cn(
1137
1897
  'pointer-events-none absolute inset-0 -z-10 rounded-[inherit]',
1138
- 'bg-[color-mix(in_srgb,var(--dt-card)_72%,transparent)]',
1139
- 'backdrop-blur-[0.75rem] backdrop-saturate-[1.12]',
1140
- '[-webkit-backdrop-filter:blur(0.75rem)_saturate(1.12)]',
1141
- 'transition-[background-color] duration-150 ease-out',
1142
- 'group-data-[thread-scrolled-up]/composer:bg-[color-mix(in_srgb,var(--dt-card)_48%,transparent)]',
1143
- 'group-focus-within/composer:bg-[color-mix(in_srgb,var(--dt-card)_85%,transparent)]'
1898
+ composerFill,
1899
+ composerSurfaceGlass
1144
1900
  )}
1145
1901
  />
1146
1902
  <div
1147
1903
  className={cn(
1148
1904
  'relative z-1 flex min-h-0 w-full flex-col gap-(--composer-row-gap) overflow-hidden rounded-[inherit] px-(--composer-surface-pad-x) py-(--composer-surface-pad-y) transition-opacity duration-200 ease-out',
1149
- scrolledUp
1150
- ? 'opacity-30 group-hover/composer:opacity-100 group-focus-within/composer:opacity-100'
1151
- : 'opacity-100'
1905
+ scrolledUp ? 'opacity-30 group-hover/composer:opacity-100 group-focus-within/composer-surface:opacity-100' : 'opacity-100'
1152
1906
  )}
1153
1907
  data-slot="composer-fade"
1154
1908
  >
@@ -1157,7 +1911,7 @@ export function ChatBar({
1157
1911
  {queueEdit && editingQueuedPrompt && (
1158
1912
  <div className="flex items-center justify-between gap-2 rounded-lg border border-[color-mix(in_srgb,var(--dt-composer-ring)_32%,transparent)] bg-accent/18 px-2 py-1">
1159
1913
  <div className="min-w-0 text-[0.7rem] text-muted-foreground/88">
1160
- Editing queued turn in composer
1914
+ {t.composer.editingQueuedInComposer}
1161
1915
  </div>
1162
1916
  <div className="flex shrink-0 items-center gap-1">
1163
1917
  <Button
@@ -1166,14 +1920,14 @@ export function ChatBar({
1166
1920
  type="button"
1167
1921
  variant="ghost"
1168
1922
  >
1169
- Cancel
1923
+ {t.common.cancel}
1170
1924
  </Button>
1171
1925
  <Button
1172
1926
  className="h-6 rounded-md px-2 text-[0.68rem]"
1173
1927
  onClick={() => exitQueuedEdit('save')}
1174
1928
  type="button"
1175
1929
  >
1176
- Save
1930
+ {t.common.save}
1177
1931
  </Button>
1178
1932
  </div>
1179
1933
  </div>
@@ -1184,7 +1938,7 @@ export function ChatBar({
1184
1938
  'grid w-full',
1185
1939
  stacked
1186
1940
  ? 'grid-cols-[auto_1fr] gap-(--composer-row-gap) [grid-template-areas:"input_input"_"menu_controls"]'
1187
- : 'grid-cols-[auto_1fr_auto] items-end gap-(--composer-control-gap) [grid-template-areas:"menu_input_controls"]'
1941
+ : 'grid-cols-[auto_1fr_auto] items-center gap-(--composer-control-gap) [grid-template-areas:"menu_input_controls"]'
1188
1942
  )}
1189
1943
  >
1190
1944
  <div className="flex items-center [grid-area:menu]">{contextMenu}</div>
@@ -1218,17 +1972,13 @@ export function ChatBarFallback() {
1218
1972
  )}
1219
1973
  data-slot="composer-root"
1220
1974
  >
1221
- <div className="composer-fallback-surface relative isolate h-(--composer-fallback-height) w-full rounded-[inherit] border border-[color-mix(in_srgb,var(--dt-composer-ring)_calc(18%*var(--composer-ring-strength)),var(--dt-input))] shadow-composer">
1975
+ <div className="composer-fallback-surface relative isolate h-(--composer-fallback-height) w-full rounded-[inherit] border border-[color-mix(in_srgb,var(--dt-composer-ring)_calc(18%*var(--composer-ring-strength)),var(--dt-input))]">
1222
1976
  <div
1223
1977
  aria-hidden
1224
1978
  className={cn(
1225
1979
  'pointer-events-none absolute inset-0 -z-10 rounded-[inherit]',
1226
- 'bg-[color-mix(in_srgb,var(--dt-card)_72%,transparent)]',
1227
- 'backdrop-blur-[0.75rem] backdrop-saturate-[1.12]',
1228
- '[-webkit-backdrop-filter:blur(0.75rem)_saturate(1.12)]',
1229
- 'transition-[background-color] duration-150 ease-out',
1230
- 'group-data-[thread-scrolled-up]/composer:bg-[color-mix(in_srgb,var(--dt-card)_48%,transparent)]',
1231
- 'group-focus-within/composer:bg-[color-mix(in_srgb,var(--dt-card)_85%,transparent)]'
1980
+ composerFill,
1981
+ composerSurfaceGlass
1232
1982
  )}
1233
1983
  />
1234
1984
  </div>