@clawpump/claw-agent 0.1.4 → 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 (1214) 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 +2294 -3146
  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/clawpump_cli.py +3 -3
  586. package/agent/hermes_cli/cli_agent_setup_mixin.py +684 -0
  587. package/agent/hermes_cli/cli_commands_mixin.py +2293 -0
  588. package/agent/hermes_cli/commands.py +216 -91
  589. package/agent/hermes_cli/config.py +967 -130
  590. package/agent/hermes_cli/container_boot.py +76 -11
  591. package/agent/hermes_cli/cron.py +5 -11
  592. package/agent/hermes_cli/curator.py +21 -0
  593. package/agent/hermes_cli/dashboard_auth/__init__.py +2 -0
  594. package/agent/hermes_cli/dashboard_auth/base.py +62 -0
  595. package/agent/hermes_cli/dashboard_auth/cookies.py +32 -19
  596. package/agent/hermes_cli/dashboard_auth/login_page.py +156 -6
  597. package/agent/hermes_cli/dashboard_auth/middleware.py +28 -4
  598. package/agent/hermes_cli/dashboard_auth/prefix.py +46 -2
  599. package/agent/hermes_cli/dashboard_auth/public_paths.py +6 -0
  600. package/agent/hermes_cli/dashboard_auth/routes.py +158 -2
  601. package/agent/hermes_cli/dashboard_auth/ws_tickets.py +85 -11
  602. package/agent/hermes_cli/dashboard_register.py +427 -0
  603. package/agent/hermes_cli/debug.py +155 -50
  604. package/agent/hermes_cli/distribution.py +227 -0
  605. package/agent/hermes_cli/doctor.py +255 -14
  606. package/agent/hermes_cli/dump.py +60 -6
  607. package/agent/hermes_cli/env_loader.py +33 -0
  608. package/agent/hermes_cli/gateway.py +755 -103
  609. package/agent/hermes_cli/gateway_enroll.py +250 -0
  610. package/agent/hermes_cli/gateway_windows.py +254 -11
  611. package/agent/hermes_cli/gui_uninstall.py +285 -0
  612. package/agent/hermes_cli/inventory.py +105 -4
  613. package/agent/hermes_cli/kanban.py +58 -71
  614. package/agent/hermes_cli/kanban_db.py +391 -14
  615. package/agent/hermes_cli/kanban_decompose.py +2 -2
  616. package/agent/hermes_cli/kanban_specify.py +3 -1
  617. package/agent/hermes_cli/logs.py +2 -0
  618. package/agent/hermes_cli/main.py +2889 -5287
  619. package/agent/hermes_cli/managed_scope.py +214 -0
  620. package/agent/hermes_cli/managed_uv.py +254 -0
  621. package/agent/hermes_cli/mcp_catalog.py +6 -3
  622. package/agent/hermes_cli/mcp_config.py +145 -21
  623. package/agent/hermes_cli/mcp_security.py +96 -0
  624. package/agent/hermes_cli/mcp_startup.py +32 -3
  625. package/agent/hermes_cli/memory_providers.py +149 -0
  626. package/agent/hermes_cli/memory_setup.py +97 -42
  627. package/agent/hermes_cli/middleware.py +313 -0
  628. package/agent/hermes_cli/model_catalog.py +31 -0
  629. package/agent/hermes_cli/model_cost_guard.py +134 -0
  630. package/agent/hermes_cli/model_normalize.py +2 -1
  631. package/agent/hermes_cli/model_setup_flows.py +2759 -0
  632. package/agent/hermes_cli/model_switch.py +242 -27
  633. package/agent/hermes_cli/models.py +284 -44
  634. package/agent/hermes_cli/nous_account.py +33 -6
  635. package/agent/hermes_cli/nous_billing.py +406 -0
  636. package/agent/hermes_cli/nous_subscription.py +202 -5
  637. package/agent/hermes_cli/platforms.py +1 -0
  638. package/agent/hermes_cli/plugins.py +218 -18
  639. package/agent/hermes_cli/plugins_cmd.py +249 -105
  640. package/agent/hermes_cli/portal_cli.py +56 -16
  641. package/agent/hermes_cli/profile_distribution.py +6 -1
  642. package/agent/hermes_cli/profiles.py +283 -32
  643. package/agent/hermes_cli/provider_catalog.py +170 -0
  644. package/agent/hermes_cli/providers.py +4 -1
  645. package/agent/hermes_cli/pty_bridge.py +53 -4
  646. package/agent/hermes_cli/runtime_provider.py +216 -34
  647. package/agent/hermes_cli/secret_prompt.py +4 -4
  648. package/agent/hermes_cli/secrets_cli.py +24 -0
  649. package/agent/hermes_cli/send_cmd.py +28 -2
  650. package/agent/hermes_cli/service_manager.py +166 -19
  651. package/agent/hermes_cli/session_listing.py +97 -0
  652. package/agent/hermes_cli/setup.py +158 -94
  653. package/agent/hermes_cli/setup_whatsapp_cloud.py +541 -0
  654. package/agent/hermes_cli/skills_config.py +8 -2
  655. package/agent/hermes_cli/skills_hub.py +149 -7
  656. package/agent/hermes_cli/status.py +2 -2
  657. package/agent/hermes_cli/subcommands/__init__.py +18 -0
  658. package/agent/hermes_cli/subcommands/_shared.py +29 -0
  659. package/agent/hermes_cli/subcommands/acp.py +52 -0
  660. package/agent/hermes_cli/subcommands/auth.py +109 -0
  661. package/agent/hermes_cli/subcommands/backup.py +38 -0
  662. package/agent/hermes_cli/subcommands/claw.py +92 -0
  663. package/agent/hermes_cli/subcommands/config.py +49 -0
  664. package/agent/hermes_cli/subcommands/cron.py +163 -0
  665. package/agent/hermes_cli/subcommands/dashboard.py +143 -0
  666. package/agent/hermes_cli/subcommands/debug.py +77 -0
  667. package/agent/hermes_cli/subcommands/doctor.py +35 -0
  668. package/agent/hermes_cli/subcommands/dump.py +28 -0
  669. package/agent/hermes_cli/subcommands/gateway.py +332 -0
  670. package/agent/hermes_cli/subcommands/gui.py +63 -0
  671. package/agent/hermes_cli/subcommands/hooks.py +77 -0
  672. package/agent/hermes_cli/subcommands/import_cmd.py +31 -0
  673. package/agent/hermes_cli/subcommands/insights.py +25 -0
  674. package/agent/hermes_cli/subcommands/login.py +78 -0
  675. package/agent/hermes_cli/subcommands/logout.py +28 -0
  676. package/agent/hermes_cli/subcommands/logs.py +78 -0
  677. package/agent/hermes_cli/subcommands/mcp.py +108 -0
  678. package/agent/hermes_cli/subcommands/memory.py +53 -0
  679. package/agent/hermes_cli/subcommands/model.py +72 -0
  680. package/agent/hermes_cli/subcommands/pairing.py +36 -0
  681. package/agent/hermes_cli/subcommands/plugins.py +94 -0
  682. package/agent/hermes_cli/subcommands/postinstall.py +23 -0
  683. package/agent/hermes_cli/subcommands/profile.py +203 -0
  684. package/agent/hermes_cli/subcommands/prompt_size.py +36 -0
  685. package/agent/hermes_cli/subcommands/security.py +62 -0
  686. package/agent/hermes_cli/subcommands/setup.py +58 -0
  687. package/agent/hermes_cli/subcommands/skills.py +298 -0
  688. package/agent/hermes_cli/subcommands/slack.py +60 -0
  689. package/agent/hermes_cli/subcommands/status.py +28 -0
  690. package/agent/hermes_cli/subcommands/tools.py +95 -0
  691. package/agent/hermes_cli/subcommands/uninstall.py +41 -0
  692. package/agent/hermes_cli/subcommands/update.py +70 -0
  693. package/agent/hermes_cli/subcommands/version.py +18 -0
  694. package/agent/hermes_cli/subcommands/webhook.py +76 -0
  695. package/agent/hermes_cli/subcommands/whatsapp.py +22 -0
  696. package/agent/hermes_cli/suggestions_cmd.py +153 -0
  697. package/agent/hermes_cli/telegram_managed_bot.py +358 -0
  698. package/agent/hermes_cli/tips.py +3 -4
  699. package/agent/hermes_cli/tools_config.py +155 -28
  700. package/agent/hermes_cli/uninstall.py +231 -35
  701. package/agent/hermes_cli/web_server.py +6188 -975
  702. package/agent/hermes_cli/win_pty_bridge.py +179 -0
  703. package/agent/hermes_cli/write_approval_commands.py +209 -0
  704. package/agent/hermes_constants.py +164 -33
  705. package/agent/hermes_logging.py +74 -2
  706. package/agent/hermes_state.py +919 -106
  707. package/agent/hermes_time.py +20 -0
  708. package/agent/locales/af.yaml +23 -0
  709. package/agent/locales/de.yaml +23 -0
  710. package/agent/locales/en.yaml +20 -0
  711. package/agent/locales/es.yaml +23 -0
  712. package/agent/locales/fr.yaml +23 -0
  713. package/agent/locales/ga.yaml +23 -0
  714. package/agent/locales/hu.yaml +23 -0
  715. package/agent/locales/it.yaml +23 -0
  716. package/agent/locales/ja.yaml +23 -0
  717. package/agent/locales/ko.yaml +23 -0
  718. package/agent/locales/pt.yaml +23 -0
  719. package/agent/locales/ru.yaml +23 -0
  720. package/agent/locales/tr.yaml +23 -0
  721. package/agent/locales/uk.yaml +23 -0
  722. package/agent/locales/zh-hant.yaml +23 -0
  723. package/agent/locales/zh.yaml +23 -0
  724. package/agent/model_tools.py +204 -40
  725. package/agent/optional-mcps/clawpump/manifest.yaml +15 -5
  726. package/agent/optional-mcps/clawpump-stdio/manifest.yaml +14 -4
  727. package/agent/optional-mcps/unreal-engine/manifest.yaml +54 -0
  728. package/agent/optional-skills/blockchain/hyperliquid/SKILL.md +2 -2
  729. package/agent/optional-skills/blockchain/hyperliquid/scripts/hyperliquid_client.py +1 -1
  730. package/agent/optional-skills/creative/kanban-video-orchestrator/SKILL.md +1 -1
  731. package/agent/optional-skills/creative/kanban-video-orchestrator/assets/setup.sh.tmpl +4 -3
  732. package/agent/optional-skills/creative/kanban-video-orchestrator/references/kanban-setup.md +6 -4
  733. package/agent/optional-skills/creative/kanban-video-orchestrator/references/tool-matrix.md +2 -2
  734. package/agent/{skills/software-development → optional-skills/devops}/hermes-s6-container-supervision/SKILL.md +2 -0
  735. package/agent/optional-skills/devops/watchers/SKILL.md +1 -1
  736. package/agent/optional-skills/devops/watchers/scripts/watch_github.py +2 -1
  737. package/agent/optional-skills/payments/mpp-agent/SKILL.md +124 -0
  738. package/agent/optional-skills/payments/stripe-link-cli/SKILL.md +184 -0
  739. package/agent/optional-skills/payments/stripe-projects/SKILL.md +120 -0
  740. package/agent/optional-skills/productivity/canvas/SKILL.md +1 -1
  741. package/agent/optional-skills/productivity/canvas/scripts/canvas_api.py +4 -1
  742. package/agent/optional-skills/productivity/shop/SKILL.md +224 -0
  743. package/agent/optional-skills/productivity/shop/references/catalog-mcp.md +236 -0
  744. package/agent/optional-skills/productivity/shop/references/direct-api.md +278 -0
  745. package/agent/optional-skills/productivity/shop/references/legal.md +3 -0
  746. package/agent/optional-skills/productivity/shop/references/safety.md +36 -0
  747. package/agent/optional-skills/productivity/shopify/SKILL.md +1 -1
  748. package/agent/optional-skills/productivity/siyuan/SKILL.md +1 -1
  749. package/agent/optional-skills/productivity/telephony/SKILL.md +4 -4
  750. package/agent/optional-skills/productivity/telephony/scripts/telephony.py +15 -15
  751. package/agent/optional-skills/security/1password/SKILL.md +1 -1
  752. package/agent/{skills/red-teaming → optional-skills/security}/godmode/SKILL.md +3 -4
  753. package/agent/{skills/red-teaming → optional-skills/security}/godmode/scripts/auto_jailbreak.py +3 -1
  754. package/agent/optional-skills/software-development/rest-graphql-debug/SKILL.md +1 -1
  755. package/agent/{skills → optional-skills}/software-development/subagent-driven-development/SKILL.md +5 -5
  756. package/agent/package-lock.json +4082 -7907
  757. package/agent/package.json +18 -3
  758. package/agent/plugins/browser/firecrawl/provider.py +4 -1
  759. package/agent/plugins/cron/__init__.py +344 -0
  760. package/agent/plugins/cron/chronos/__init__.py +241 -0
  761. package/agent/plugins/cron/chronos/_nas_client.py +123 -0
  762. package/agent/plugins/cron/chronos/plugin.yaml +9 -0
  763. package/agent/plugins/cron/chronos/verify.py +103 -0
  764. package/agent/plugins/dashboard_auth/basic/__init__.py +491 -0
  765. package/agent/plugins/dashboard_auth/basic/plugin.yaml +7 -0
  766. package/agent/plugins/dashboard_auth/nous/__init__.py +12 -14
  767. package/agent/plugins/dashboard_auth/self_hosted/__init__.py +736 -0
  768. package/agent/plugins/dashboard_auth/self_hosted/plugin.yaml +8 -0
  769. package/agent/plugins/disk-cleanup/disk_cleanup.py +100 -20
  770. package/agent/plugins/google_meet/audio_bridge.py +4 -0
  771. package/agent/plugins/google_meet/meet_bot.py +7 -1
  772. package/agent/plugins/hermes-achievements/dashboard/dist/index.js +9 -15
  773. package/agent/plugins/image_gen/fal/__init__.py +35 -6
  774. package/agent/plugins/image_gen/krea/__init__.py +56 -13
  775. package/agent/plugins/image_gen/openai/__init__.py +122 -24
  776. package/agent/plugins/image_gen/openai-codex/__init__.py +28 -2
  777. package/agent/plugins/image_gen/xai/__init__.py +92 -12
  778. package/agent/plugins/kanban/dashboard/dist/index.js +63 -48
  779. package/agent/plugins/kanban/dashboard/plugin_api.py +39 -35
  780. package/agent/plugins/memory/__init__.py +48 -5
  781. package/agent/plugins/memory/byterover/__init__.py +1 -0
  782. package/agent/plugins/memory/hindsight/README.md +1 -1
  783. package/agent/plugins/memory/hindsight/__init__.py +138 -24
  784. package/agent/plugins/memory/hindsight/plugin.yaml +1 -1
  785. package/agent/plugins/memory/honcho/README.md +13 -10
  786. package/agent/plugins/memory/honcho/cli.py +247 -122
  787. package/agent/plugins/memory/honcho/client.py +112 -102
  788. package/agent/plugins/memory/openviking/README.md +12 -1
  789. package/agent/plugins/memory/openviking/__init__.py +2281 -107
  790. package/agent/plugins/memory/openviking/plugin.yaml +1 -2
  791. package/agent/plugins/memory/supermemory/README.md +22 -10
  792. package/agent/plugins/memory/supermemory/__init__.py +142 -37
  793. package/agent/plugins/memory/supermemory/plugin.yaml +1 -1
  794. package/agent/plugins/model-providers/anthropic/__init__.py +1 -0
  795. package/agent/plugins/model-providers/bedrock/__init__.py +1 -0
  796. package/agent/plugins/model-providers/copilot-acp/__init__.py +1 -0
  797. package/agent/plugins/model-providers/custom/__init__.py +8 -2
  798. package/agent/plugins/model-providers/kimi-coding/__init__.py +16 -7
  799. package/agent/plugins/model-providers/minimax/__init__.py +60 -8
  800. package/agent/plugins/model-providers/opencode-zen/__init__.py +12 -3
  801. package/agent/plugins/model-providers/openrouter/__init__.py +75 -4
  802. package/agent/plugins/model-providers/xiaomi/__init__.py +2 -0
  803. package/agent/plugins/model-providers/zai/__init__.py +1 -0
  804. package/agent/plugins/observability/langfuse/__init__.py +147 -14
  805. package/agent/plugins/observability/nemo_relay/README.md +559 -0
  806. package/agent/plugins/observability/nemo_relay/__init__.py +962 -0
  807. package/agent/plugins/observability/nemo_relay/plugin.yaml +20 -0
  808. package/agent/plugins/platforms/discord/adapter.py +932 -61
  809. package/agent/plugins/platforms/discord/voice_mixer.py +379 -0
  810. package/agent/plugins/platforms/google_chat/adapter.py +9 -3
  811. package/agent/plugins/platforms/google_chat/oauth.py +1 -1
  812. package/agent/plugins/platforms/homeassistant/__init__.py +3 -0
  813. package/agent/{gateway/platforms/homeassistant.py → plugins/platforms/homeassistant/adapter.py} +128 -0
  814. package/agent/plugins/platforms/homeassistant/plugin.yaml +22 -0
  815. package/agent/plugins/platforms/irc/adapter.py +4 -1
  816. package/agent/plugins/platforms/line/adapter.py +16 -1
  817. package/agent/plugins/platforms/mattermost/adapter.py +100 -24
  818. package/agent/plugins/platforms/photon/README.md +179 -0
  819. package/agent/plugins/platforms/photon/__init__.py +4 -0
  820. package/agent/plugins/platforms/photon/adapter.py +1586 -0
  821. package/agent/plugins/platforms/photon/auth.py +1046 -0
  822. package/agent/plugins/platforms/photon/cli.py +439 -0
  823. package/agent/plugins/platforms/photon/plugin.yaml +88 -0
  824. package/agent/plugins/platforms/photon/sidecar/README.md +52 -0
  825. package/agent/plugins/platforms/photon/sidecar/index.mjs +720 -0
  826. package/agent/plugins/platforms/photon/sidecar/package-lock.json +1730 -0
  827. package/agent/plugins/platforms/photon/sidecar/package.json +25 -0
  828. package/agent/plugins/platforms/photon/sidecar/patch-spectrum-mixed-attachments.mjs +155 -0
  829. package/agent/plugins/platforms/raft/__init__.py +3 -0
  830. package/agent/plugins/platforms/raft/adapter.py +774 -0
  831. package/agent/plugins/platforms/raft/plugin.yaml +19 -0
  832. package/agent/plugins/platforms/simplex/adapter.py +777 -220
  833. package/agent/plugins/platforms/simplex/plugin.yaml +21 -2
  834. package/agent/plugins/platforms/teams/adapter.py +175 -5
  835. package/agent/plugins/plugin_utils.py +135 -0
  836. package/agent/plugins/video_gen/fal/__init__.py +10 -3
  837. package/agent/plugins/web/searxng/provider.py +15 -2
  838. package/agent/plugins/web/xai/provider.py +2 -2
  839. package/agent/providers/base.py +22 -3
  840. package/agent/pyproject.toml +115 -21
  841. package/agent/run_agent.py +733 -39
  842. package/agent/scripts/build_skills_index.py +51 -19
  843. package/agent/scripts/check_subprocess_stdin.py +177 -0
  844. package/agent/scripts/contributor_audit.py +2 -0
  845. package/agent/scripts/docker_config_migrate.py +67 -0
  846. package/agent/scripts/install.cmd +3 -3
  847. package/agent/scripts/install.ps1 +580 -154
  848. package/agent/scripts/install.sh +402 -185
  849. package/agent/scripts/lib/node-bootstrap.sh +39 -4
  850. package/agent/scripts/release.py +183 -0
  851. package/agent/scripts/run_tests.sh +1 -0
  852. package/agent/scripts/run_tests_parallel.py +18 -23
  853. package/agent/scripts/whatsapp-bridge/bridge.js +25 -4
  854. package/agent/setup.py +59 -0
  855. package/agent/skills/autonomous-ai-agents/codex/SKILL.md +19 -0
  856. package/agent/skills/autonomous-ai-agents/hermes-agent/SKILL.md +10 -3
  857. package/agent/skills/{mcp/native-mcp/SKILL.md → autonomous-ai-agents/hermes-agent/references/native-mcp.md} +0 -13
  858. package/agent/skills/{devops/webhook-subscriptions/SKILL.md → autonomous-ai-agents/hermes-agent/references/webhooks.md} +1 -11
  859. package/agent/skills/clawpump/SKILL.md +53 -5
  860. package/agent/skills/devops/kanban-orchestrator/SKILL.md +1 -0
  861. package/agent/skills/devops/kanban-worker/SKILL.md +1 -0
  862. package/agent/skills/github/github-auth/SKILL.md +2 -2
  863. package/agent/skills/github/github-auth/scripts/gh-env.sh +2 -2
  864. package/agent/skills/github/github-code-review/SKILL.md +2 -2
  865. package/agent/skills/github/github-issues/SKILL.md +2 -2
  866. package/agent/skills/github/github-pr-workflow/SKILL.md +2 -2
  867. package/agent/skills/github/github-repo-management/SKILL.md +2 -2
  868. package/agent/skills/media/gif-search/SKILL.md +1 -1
  869. package/agent/skills/media/youtube-content/SKILL.md +10 -7
  870. package/agent/skills/media/youtube-content/scripts/fetch_transcript.py +3 -3
  871. package/agent/skills/note-taking/obsidian/SKILL.md +1 -1
  872. package/agent/skills/productivity/airtable/SKILL.md +2 -2
  873. package/agent/skills/productivity/google-workspace/scripts/setup.py +33 -7
  874. package/agent/skills/productivity/notion/SKILL.md +2 -2
  875. package/agent/skills/productivity/teams-meeting-pipeline/SKILL.md +1 -1
  876. package/agent/skills/research/llm-wiki/SKILL.md +1 -1
  877. package/agent/skills/social-media/xurl/SKILL.md +9 -0
  878. package/agent/skills/software-development/hermes-agent-skill-authoring/SKILL.md +1 -1
  879. package/agent/skills/software-development/plan/SKILL.md +285 -5
  880. package/agent/skills/software-development/requesting-code-review/SKILL.md +2 -2
  881. package/agent/skills/software-development/simplify-code/SKILL.md +212 -0
  882. package/agent/skills/software-development/spike/SKILL.md +2 -2
  883. package/agent/skills/software-development/systematic-debugging/SKILL.md +1 -1
  884. package/agent/skills/software-development/test-driven-development/SKILL.md +1 -1
  885. package/agent/tools/approval.py +302 -4
  886. package/agent/tools/async_delegation.py +386 -0
  887. package/agent/tools/blueprints.py +325 -0
  888. package/agent/tools/browser_cdp_tool.py +3 -3
  889. package/agent/tools/browser_tool.py +34 -6
  890. package/agent/tools/checkpoint_manager.py +31 -1
  891. package/agent/tools/clarify_tool.py +55 -5
  892. package/agent/tools/code_execution_tool.py +31 -14
  893. package/agent/tools/computer_use/cua_backend.py +81 -3
  894. package/agent/tools/computer_use/tool.py +79 -5
  895. package/agent/tools/computer_use/vision_routing.py +55 -3
  896. package/agent/tools/credential_files.py +31 -12
  897. package/agent/tools/cronjob_tools.py +30 -20
  898. package/agent/tools/delegate_tool.py +356 -31
  899. package/agent/tools/env_probe.py +1 -0
  900. package/agent/tools/environments/docker.py +163 -8
  901. package/agent/tools/environments/file_sync.py +2 -1
  902. package/agent/tools/environments/local.py +74 -23
  903. package/agent/tools/environments/singularity.py +4 -1
  904. package/agent/tools/environments/ssh.py +78 -11
  905. package/agent/tools/file_operations.py +277 -41
  906. package/agent/tools/file_tools.py +166 -28
  907. package/agent/tools/image_generation_tool.py +515 -29
  908. package/agent/tools/kanban_tools.py +99 -0
  909. package/agent/tools/lazy_deps.py +33 -2
  910. package/agent/tools/mcp_oauth.py +5 -5
  911. package/agent/tools/mcp_oauth_manager.py +7 -5
  912. package/agent/tools/mcp_tool.py +840 -33
  913. package/agent/tools/memory_tool.py +335 -38
  914. package/agent/tools/osv_check.py +15 -1
  915. package/agent/tools/process_registry.py +155 -11
  916. package/agent/tools/read_extract.py +248 -0
  917. package/agent/tools/read_terminal_tool.py +93 -0
  918. package/agent/tools/schema_sanitizer.py +38 -0
  919. package/agent/tools/send_message_tool.py +163 -49
  920. package/agent/tools/session_search_tool.py +189 -7
  921. package/agent/tools/skill_manager_tool.py +202 -3
  922. package/agent/tools/skill_usage.py +52 -4
  923. package/agent/tools/skills_hub.py +184 -44
  924. package/agent/tools/skills_sync.py +232 -5
  925. package/agent/tools/skills_tool.py +125 -11
  926. package/agent/tools/terminal_tool.py +148 -26
  927. package/agent/tools/tirith_security.py +2 -0
  928. package/agent/tools/todo_tool.py +32 -1
  929. package/agent/tools/transcription_tools.py +13 -5
  930. package/agent/tools/tts_tool.py +332 -38
  931. package/agent/tools/url_safety.py +52 -1
  932. package/agent/tools/vision_tools.py +124 -39
  933. package/agent/tools/voice_mode.py +4 -3
  934. package/agent/tools/web_tools.py +45 -15
  935. package/agent/tools/write_approval.py +493 -0
  936. package/agent/toolsets.py +34 -10
  937. package/agent/trajectory_compressor.py +81 -10
  938. package/agent/tui_gateway/entry.py +43 -6
  939. package/agent/tui_gateway/server.py +3335 -330
  940. package/agent/tui_gateway/slash_worker.py +61 -0
  941. package/agent/tui_gateway/ws.py +67 -9
  942. package/agent/ui-tui/eslint.config.mjs +0 -4
  943. package/agent/ui-tui/package.json +6 -6
  944. package/agent/ui-tui/packages/hermes-ink/package.json +1 -1
  945. package/agent/ui-tui/packages/hermes-ink/src/ink/app-mouse.test.ts +34 -1
  946. package/agent/ui-tui/packages/hermes-ink/src/ink/app-rawmode-mouse.test.ts +91 -0
  947. package/agent/ui-tui/packages/hermes-ink/src/ink/components/App.tsx +35 -2
  948. package/agent/ui-tui/packages/hermes-ink/src/ink/events/input-event.ts +4 -11
  949. package/agent/ui-tui/packages/hermes-ink/src/ink/parse-keypress.test.ts +23 -57
  950. package/agent/ui-tui/packages/hermes-ink/src/ink/parse-keypress.ts +11 -135
  951. package/agent/ui-tui/packages/hermes-ink/src/ink/termio/tokenize.test.ts +185 -0
  952. package/agent/ui-tui/packages/hermes-ink/src/ink/termio/tokenize.ts +37 -3
  953. package/agent/ui-tui/packages/hermes-ink/src/utils/execFileNoThrow.ts +5 -5
  954. package/agent/ui-tui/src/__tests__/appChromeStatusRule.test.tsx +217 -0
  955. package/agent/ui-tui/src/__tests__/appChromeStatusRuleDevCredits.test.tsx +73 -0
  956. package/agent/ui-tui/src/__tests__/approvalAction.test.ts +11 -0
  957. package/agent/ui-tui/src/__tests__/billingCommand.test.ts +301 -0
  958. package/agent/ui-tui/src/__tests__/blockLayout.test.ts +122 -0
  959. package/agent/ui-tui/src/__tests__/brandingMcpCount.test.ts +111 -0
  960. package/agent/ui-tui/src/__tests__/completionApply.test.ts +51 -0
  961. package/agent/ui-tui/src/__tests__/createGatewayEventHandler.test.ts +487 -2
  962. package/agent/ui-tui/src/__tests__/createSlashHandler.test.ts +54 -0
  963. package/agent/ui-tui/src/__tests__/creditsCommand.test.ts +144 -0
  964. package/agent/ui-tui/src/__tests__/gatewayClient.test.ts +120 -99
  965. package/agent/ui-tui/src/__tests__/gracefulExit.test.ts +11 -0
  966. package/agent/ui-tui/src/__tests__/memoryMonitor.test.ts +102 -0
  967. package/agent/ui-tui/src/__tests__/paths.test.ts +41 -1
  968. package/agent/ui-tui/src/__tests__/terminalModes.test.ts +22 -0
  969. package/agent/ui-tui/src/__tests__/text.test.ts +23 -0
  970. package/agent/ui-tui/src/__tests__/textInputFastEcho.test.ts +37 -0
  971. package/agent/ui-tui/src/__tests__/turnControllerNotice.test.ts +43 -0
  972. package/agent/ui-tui/src/__tests__/useInputHandlers.test.ts +38 -1
  973. package/agent/ui-tui/src/__tests__/virtualHeights.test.ts +8 -0
  974. package/agent/ui-tui/src/app/createGatewayEventHandler.ts +102 -7
  975. package/agent/ui-tui/src/app/interfaces.ts +64 -1
  976. package/agent/ui-tui/src/app/overlayStore.ts +18 -2
  977. package/agent/ui-tui/src/app/slash/commands/billing.ts +332 -0
  978. package/agent/ui-tui/src/app/slash/commands/core.ts +31 -2
  979. package/agent/ui-tui/src/app/slash/commands/credits.ts +57 -0
  980. package/agent/ui-tui/src/app/slash/commands/ops.ts +28 -0
  981. package/agent/ui-tui/src/app/slash/commands/session.ts +32 -4
  982. package/agent/ui-tui/src/app/slash/registry.ts +4 -0
  983. package/agent/ui-tui/src/app/turnController.ts +145 -2
  984. package/agent/ui-tui/src/app/uiStore.ts +2 -0
  985. package/agent/ui-tui/src/app/useInputHandlers.ts +42 -4
  986. package/agent/ui-tui/src/app/useMainApp.ts +54 -8
  987. package/agent/ui-tui/src/app/useSessionLifecycle.ts +40 -31
  988. package/agent/ui-tui/src/app/useSubmission.ts +23 -31
  989. package/agent/ui-tui/src/components/appChrome.tsx +112 -5
  990. package/agent/ui-tui/src/components/appLayout.tsx +9 -0
  991. package/agent/ui-tui/src/components/appOverlays.tsx +25 -1
  992. package/agent/ui-tui/src/components/billingOverlay.tsx +684 -0
  993. package/agent/ui-tui/src/components/branding.tsx +15 -3
  994. package/agent/ui-tui/src/components/messageLine.tsx +25 -3
  995. package/agent/ui-tui/src/components/pluginsHub.tsx +238 -0
  996. package/agent/ui-tui/src/components/prompts.tsx +31 -17
  997. package/agent/ui-tui/src/components/streamingAssistant.tsx +63 -55
  998. package/agent/ui-tui/src/components/textInput.tsx +16 -0
  999. package/agent/ui-tui/src/config/env.ts +12 -0
  1000. package/agent/ui-tui/src/config/limits.ts +13 -0
  1001. package/agent/ui-tui/src/domain/blockLayout.ts +146 -0
  1002. package/agent/ui-tui/src/domain/paths.ts +24 -0
  1003. package/agent/ui-tui/src/domain/slash.ts +40 -0
  1004. package/agent/ui-tui/src/entry.tsx +35 -4
  1005. package/agent/ui-tui/src/gatewayClient.ts +22 -10
  1006. package/agent/ui-tui/src/gatewayTypes.ts +130 -1
  1007. package/agent/ui-tui/src/lib/gracefulExit.ts +24 -4
  1008. package/agent/ui-tui/src/lib/memory.test.ts +162 -0
  1009. package/agent/ui-tui/src/lib/memory.ts +60 -1
  1010. package/agent/ui-tui/src/lib/memoryMonitor.ts +79 -4
  1011. package/agent/ui-tui/src/lib/osc52.ts +1 -1
  1012. package/agent/ui-tui/src/lib/text.test.ts +32 -1
  1013. package/agent/ui-tui/src/lib/text.ts +29 -2
  1014. package/agent/ui-tui/src/lib/virtualHeights.ts +13 -0
  1015. package/agent/ui-tui/src/types.ts +5 -0
  1016. package/agent/ui-tui/tsconfig.build.json +0 -1
  1017. package/agent/ui-tui/tsconfig.json +2 -1
  1018. package/agent/utils.py +66 -2
  1019. package/agent/uv.lock +308 -696
  1020. package/agent/web/index.html +2 -2
  1021. package/agent/web/package.json +11 -6
  1022. package/agent/web/public/claw-bg.webp +0 -0
  1023. package/agent/web/public/claw-logo.webp +0 -0
  1024. package/agent/web/src/App.tsx +138 -48
  1025. package/agent/web/src/components/AutomationBlueprints.tsx +225 -0
  1026. package/agent/web/src/components/Backdrop.tsx +15 -0
  1027. package/agent/web/src/components/ChatSessionList.tsx +260 -0
  1028. package/agent/web/src/components/ChatSidebar.tsx +262 -78
  1029. package/agent/web/src/components/ConfirmDialog.tsx +122 -0
  1030. package/agent/web/src/components/ModelPickerDialog.tsx +111 -16
  1031. package/agent/web/src/components/ModelReloadConfirm.tsx +40 -0
  1032. package/agent/web/src/components/ProfileScopeBanner.tsx +30 -0
  1033. package/agent/web/src/components/ProfileSwitcher.tsx +67 -0
  1034. package/agent/web/src/components/ReasoningPicker.tsx +167 -0
  1035. package/agent/web/src/components/SkillEditorDialog.tsx +215 -0
  1036. package/agent/web/src/components/ThemeSwitcher.tsx +119 -4
  1037. package/agent/web/src/components/ToolsetConfigDrawer.tsx +457 -0
  1038. package/agent/web/src/contexts/PageHeaderProvider.tsx +7 -4
  1039. package/agent/web/src/contexts/ProfileProvider.tsx +137 -0
  1040. package/agent/web/src/contexts/SystemActions.tsx +6 -8
  1041. package/agent/web/src/contexts/profile-context.ts +19 -0
  1042. package/agent/web/src/contexts/useProfileScope.ts +6 -0
  1043. package/agent/web/src/i18n/af.ts +5 -4
  1044. package/agent/web/src/i18n/de.ts +5 -4
  1045. package/agent/web/src/i18n/en.ts +58 -4
  1046. package/agent/web/src/i18n/es.ts +5 -3
  1047. package/agent/web/src/i18n/fr.ts +5 -3
  1048. package/agent/web/src/i18n/ga.ts +5 -4
  1049. package/agent/web/src/i18n/hu.ts +5 -4
  1050. package/agent/web/src/i18n/it.ts +5 -4
  1051. package/agent/web/src/i18n/ja.ts +5 -4
  1052. package/agent/web/src/i18n/ko.ts +5 -4
  1053. package/agent/web/src/i18n/pt.ts +5 -3
  1054. package/agent/web/src/i18n/ru.ts +5 -4
  1055. package/agent/web/src/i18n/tr.ts +5 -4
  1056. package/agent/web/src/i18n/types.ts +59 -1
  1057. package/agent/web/src/i18n/uk.ts +5 -3
  1058. package/agent/web/src/i18n/zh-hant.ts +5 -4
  1059. package/agent/web/src/i18n/zh.ts +5 -4
  1060. package/agent/web/src/index.css +2 -2
  1061. package/agent/web/src/lib/api.ts +819 -52
  1062. package/agent/web/src/lib/dashboard-flags.ts +16 -7
  1063. package/agent/web/src/lib/reasoning-effort.test.ts +48 -0
  1064. package/agent/web/src/lib/reasoning-effort.ts +36 -0
  1065. package/agent/web/src/lib/session-refresh.test.ts +21 -0
  1066. package/agent/web/src/lib/session-refresh.ts +26 -0
  1067. package/agent/web/src/pages/ChannelsPage.tsx +529 -68
  1068. package/agent/web/src/pages/ChatPage.tsx +249 -56
  1069. package/agent/web/src/pages/ConfigPage.tsx +11 -1
  1070. package/agent/web/src/pages/CronPage.tsx +219 -31
  1071. package/agent/web/src/pages/EnvPage.tsx +25 -6
  1072. package/agent/web/src/pages/FilesPage.tsx +525 -0
  1073. package/agent/web/src/pages/McpPage.tsx +80 -3
  1074. package/agent/web/src/pages/ModelsPage.tsx +97 -12
  1075. package/agent/web/src/pages/PluginsPage.tsx +1 -1
  1076. package/agent/web/src/pages/ProfileBuilderPage.tsx +611 -0
  1077. package/agent/web/src/pages/ProfilesPage.tsx +1038 -172
  1078. package/agent/web/src/pages/SessionsPage.tsx +144 -13
  1079. package/agent/web/src/pages/SkillsPage.tsx +851 -70
  1080. package/agent/web/src/pages/SystemPage.tsx +340 -4
  1081. package/agent/web/src/pages/WalletPage.tsx +401 -0
  1082. package/agent/web/src/pages/WebhooksPage.tsx +145 -15
  1083. package/agent/web/src/pages/X402Page.tsx +207 -0
  1084. package/agent/web/src/plugins/registry.ts +28 -11
  1085. package/agent/web/src/plugins/sdk.d.ts +160 -0
  1086. package/agent/web/src/themes/context.tsx +112 -5
  1087. package/agent/web/src/themes/fonts.ts +167 -0
  1088. package/agent/web/src/themes/index.ts +7 -0
  1089. package/agent/web/tsconfig.app.json +0 -1
  1090. package/agent/web/vite.config.ts +1 -8
  1091. package/agent/web/vitest.config.ts +16 -0
  1092. package/package.json +1 -1
  1093. package/agent/apps/desktop/package-lock.json +0 -18363
  1094. package/agent/apps/desktop/src/app/chat/composer/skin-slash-popover.tsx +0 -56
  1095. package/agent/apps/desktop/src/components/assistant-ui/thread-virtualizer.tsx +0 -382
  1096. package/agent/apps/desktop/src/components/assistant-ui/todo-tool.tsx +0 -109
  1097. package/agent/apps/desktop/src/components/chat/generated-image-context.tsx +0 -19
  1098. package/agent/optional-skills/productivity/shop-app/SKILL.md +0 -340
  1099. package/agent/skills/autonomous-ai-agents/kanban-codex-lane/SKILL.md +0 -277
  1100. package/agent/skills/autonomous-ai-agents/kanban-codex-lane/templates/pmb-codex-lane-prompt.md +0 -57
  1101. package/agent/skills/diagramming/DESCRIPTION.md +0 -3
  1102. package/agent/skills/domain/DESCRIPTION.md +0 -24
  1103. package/agent/skills/gifs/DESCRIPTION.md +0 -3
  1104. package/agent/skills/inference-sh/DESCRIPTION.md +0 -19
  1105. package/agent/skills/mcp/DESCRIPTION.md +0 -3
  1106. package/agent/skills/media/spotify/SKILL.md +0 -135
  1107. package/agent/skills/mlops/training/DESCRIPTION.md +0 -3
  1108. package/agent/skills/mlops/vector-databases/DESCRIPTION.md +0 -3
  1109. package/agent/skills/productivity/linear/SKILL.md +0 -380
  1110. package/agent/skills/productivity/linear/scripts/linear_api.py +0 -445
  1111. package/agent/skills/software-development/debugging-hermes-tui-commands/SKILL.md +0 -152
  1112. package/agent/skills/software-development/writing-plans/SKILL.md +0 -297
  1113. package/agent/ui-tui/package-lock.json +0 -7449
  1114. package/agent/ui-tui/packages/hermes-ink/package-lock.json +0 -1289
  1115. package/agent/web/package-lock.json +0 -8887
  1116. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/PORT_NOTES.md +0 -0
  1117. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/SKILL.md +0 -0
  1118. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/prompts/system.md +0 -0
  1119. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/palettes/macaron.md +0 -0
  1120. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/palettes/mono-ink.md +0 -0
  1121. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/palettes/neon.md +0 -0
  1122. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/palettes/warm.md +0 -0
  1123. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/prompt-construction.md +0 -0
  1124. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/style-presets.md +0 -0
  1125. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/blueprint.md +0 -0
  1126. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/chalkboard.md +0 -0
  1127. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/editorial.md +0 -0
  1128. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/elegant.md +0 -0
  1129. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/fantasy-animation.md +0 -0
  1130. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/flat-doodle.md +0 -0
  1131. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/flat.md +0 -0
  1132. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/ink-notes.md +0 -0
  1133. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/intuition-machine.md +0 -0
  1134. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/minimal.md +0 -0
  1135. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/nature.md +0 -0
  1136. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/notion.md +0 -0
  1137. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/pixel-art.md +0 -0
  1138. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/playful.md +0 -0
  1139. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/retro.md +0 -0
  1140. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/scientific.md +0 -0
  1141. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/screen-print.md +0 -0
  1142. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/sketch-notes.md +0 -0
  1143. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/sketch.md +0 -0
  1144. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/vector-illustration.md +0 -0
  1145. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/vintage.md +0 -0
  1146. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/warm.md +0 -0
  1147. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/watercolor.md +0 -0
  1148. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles.md +0 -0
  1149. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/usage.md +0 -0
  1150. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/workflow.md +0 -0
  1151. /package/agent/{skills → optional-skills}/creative/baoyu-comic/PORT_NOTES.md +0 -0
  1152. /package/agent/{skills → optional-skills}/creative/baoyu-comic/SKILL.md +0 -0
  1153. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/analysis-framework.md +0 -0
  1154. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/chalk.md +0 -0
  1155. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/ink-brush.md +0 -0
  1156. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/ligne-claire.md +0 -0
  1157. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/manga.md +0 -0
  1158. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/minimalist.md +0 -0
  1159. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/realistic.md +0 -0
  1160. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/auto-selection.md +0 -0
  1161. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/base-prompt.md +0 -0
  1162. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/character-template.md +0 -0
  1163. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/cinematic.md +0 -0
  1164. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/dense.md +0 -0
  1165. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/four-panel.md +0 -0
  1166. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/mixed.md +0 -0
  1167. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/splash.md +0 -0
  1168. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/standard.md +0 -0
  1169. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/webtoon.md +0 -0
  1170. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/ohmsha-guide.md +0 -0
  1171. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/partial-workflows.md +0 -0
  1172. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/presets/concept-story.md +0 -0
  1173. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/presets/four-panel.md +0 -0
  1174. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/presets/ohmsha.md +0 -0
  1175. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/presets/shoujo.md +0 -0
  1176. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/presets/wuxia.md +0 -0
  1177. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/storyboard-template.md +0 -0
  1178. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/action.md +0 -0
  1179. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/dramatic.md +0 -0
  1180. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/energetic.md +0 -0
  1181. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/neutral.md +0 -0
  1182. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/romantic.md +0 -0
  1183. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/vintage.md +0 -0
  1184. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/warm.md +0 -0
  1185. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/workflow.md +0 -0
  1186. /package/agent/{skills → optional-skills}/creative/creative-ideation/SKILL.md +0 -0
  1187. /package/agent/{skills → optional-skills}/creative/creative-ideation/references/full-prompt-library.md +0 -0
  1188. /package/agent/{skills → optional-skills}/creative/pixel-art/ATTRIBUTION.md +0 -0
  1189. /package/agent/{skills → optional-skills}/creative/pixel-art/SKILL.md +0 -0
  1190. /package/agent/{skills → optional-skills}/creative/pixel-art/references/palettes.md +0 -0
  1191. /package/agent/{skills → optional-skills}/creative/pixel-art/scripts/__init__.py +0 -0
  1192. /package/agent/{skills → optional-skills}/creative/pixel-art/scripts/palettes.py +0 -0
  1193. /package/agent/{skills → optional-skills}/creative/pixel-art/scripts/pixel_art.py +0 -0
  1194. /package/agent/{skills → optional-skills}/creative/pixel-art/scripts/pixel_art_video.py +0 -0
  1195. /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/SKILL.md +0 -0
  1196. /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/references/analysis-modules.md +0 -0
  1197. /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/references/methods-guide.md +0 -0
  1198. /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/templates/abliteration-config.yaml +0 -0
  1199. /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/templates/analysis-study.yaml +0 -0
  1200. /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/templates/batch-abliteration.yaml +0 -0
  1201. /package/agent/{skills → optional-skills}/mlops/research/DESCRIPTION.md +0 -0
  1202. /package/agent/{skills → optional-skills}/mlops/research/dspy/SKILL.md +0 -0
  1203. /package/agent/{skills → optional-skills}/mlops/research/dspy/references/examples.md +0 -0
  1204. /package/agent/{skills → optional-skills}/mlops/research/dspy/references/modules.md +0 -0
  1205. /package/agent/{skills → optional-skills}/mlops/research/dspy/references/optimizers.md +0 -0
  1206. /package/agent/{skills/red-teaming → optional-skills/security}/godmode/references/jailbreak-templates.md +0 -0
  1207. /package/agent/{skills/red-teaming → optional-skills/security}/godmode/references/refusal-detection.md +0 -0
  1208. /package/agent/{skills/red-teaming → optional-skills/security}/godmode/scripts/godmode_race.py +0 -0
  1209. /package/agent/{skills/red-teaming → optional-skills/security}/godmode/scripts/load_godmode.py +0 -0
  1210. /package/agent/{skills/red-teaming → optional-skills/security}/godmode/scripts/parseltongue.py +0 -0
  1211. /package/agent/{skills/red-teaming → optional-skills/security}/godmode/templates/prefill-subtle.json +0 -0
  1212. /package/agent/{skills/red-teaming → optional-skills/security}/godmode/templates/prefill.json +0 -0
  1213. /package/agent/{skills → optional-skills}/software-development/subagent-driven-development/references/context-budget-discipline.md +0 -0
  1214. /package/agent/{skills → optional-skills}/software-development/subagent-driven-development/references/gates-taxonomy.md +0 -0
@@ -7,12 +7,14 @@ import {
7
7
  MessagePrimitive,
8
8
  type ToolCallMessagePartProps,
9
9
  useAui,
10
- useAuiState
10
+ useAuiState,
11
+ useMessageRuntime
11
12
  } from '@assistant-ui/react'
12
13
  import { useStore } from '@nanostores/react'
13
14
  import { IconPlayerStopFilled } from '@tabler/icons-react'
14
15
  import {
15
16
  type ClipboardEvent,
17
+ type ComponentProps,
16
18
  type FC,
17
19
  type FocusEvent,
18
20
  type FormEvent,
@@ -36,7 +38,12 @@ import {
36
38
  } from '@/app/chat/composer/focus'
37
39
  import { useAtCompletions } from '@/app/chat/composer/hooks/use-at-completions'
38
40
  import { useSlashCompletions } from '@/app/chat/composer/hooks/use-slash-completions'
39
- import { dragHasAttachments, droppedFileInlineRef, insertInlineRefsIntoEditor } from '@/app/chat/composer/inline-refs'
41
+ import {
42
+ dragHasAttachments,
43
+ droppedFileInlineRefs,
44
+ type InlineRefInput,
45
+ insertInlineRefsIntoEditor
46
+ } from '@/app/chat/composer/inline-refs'
40
47
  import {
41
48
  composerPlainText,
42
49
  placeCaretEnd,
@@ -46,23 +53,28 @@ import {
46
53
  } from '@/app/chat/composer/rich-editor'
47
54
  import { detectTrigger, textBeforeCaret, type TriggerState } from '@/app/chat/composer/text-utils'
48
55
  import { ComposerTriggerPopover } from '@/app/chat/composer/trigger-popover'
49
- import { extractDroppedFiles, HERMES_PATHS_MIME } from '@/app/chat/hooks/use-composer-actions'
56
+ import {
57
+ extractDroppedFiles,
58
+ HERMES_PATHS_MIME,
59
+ isImagePath,
60
+ partitionDroppedFiles
61
+ } from '@/app/chat/hooks/use-composer-actions'
62
+ import { uploadComposerAttachment } from '@/app/session/hooks/use-prompt-actions'
50
63
  import { ClarifyTool } from '@/components/assistant-ui/clarify-tool'
51
- import { DirectiveContent, DirectiveText } from '@/components/assistant-ui/directive-text'
52
- import { hermesDirectiveFormatter } from '@/components/assistant-ui/directive-text'
53
- import { MarkdownText } from '@/components/assistant-ui/markdown-text'
54
- import { VirtualizedThread } from '@/components/assistant-ui/thread-virtualizer'
55
- import { HoistedTodoPanel, todosFromMessageContent } from '@/components/assistant-ui/todo-tool'
64
+ import { DirectiveContent, hermesDirectiveFormatter } from '@/components/assistant-ui/directive-text'
65
+ import { MarkdownText, MarkdownTextContent } from '@/components/assistant-ui/markdown-text'
66
+ import { ThreadMessageList } from '@/components/assistant-ui/thread-list'
56
67
  import { ToolFallback, ToolGroupSlot } from '@/components/assistant-ui/tool-fallback'
57
68
  import { TooltipIconButton } from '@/components/assistant-ui/tooltip-icon-button'
69
+ import { UserMessageText } from '@/components/assistant-ui/user-message-text'
58
70
  import { useElapsedSeconds } from '@/components/chat/activity-timer'
59
71
  import { ActivityTimerText } from '@/components/chat/activity-timer-text'
60
72
  import { DisclosureRow } from '@/components/chat/disclosure-row'
61
- import { GeneratedImageProvider, useGeneratedImageContext } from '@/components/chat/generated-image-context'
62
- import { ImageGenerationPlaceholder } from '@/components/chat/image-generation-placeholder'
73
+ import { GeneratedImage } from '@/components/chat/generated-image-result'
63
74
  import { Intro, type IntroProps } from '@/components/chat/intro'
64
75
  import { PreviewAttachment } from '@/components/chat/preview-attachment'
65
76
  import { Codicon } from '@/components/ui/codicon'
77
+ import { ConfirmDialog } from '@/components/ui/confirm-dialog'
66
78
  import { CopyButton } from '@/components/ui/copy-button'
67
79
  import {
68
80
  DropdownMenu,
@@ -73,21 +85,33 @@ import {
73
85
  } from '@/components/ui/dropdown-menu'
74
86
  import { Loader } from '@/components/ui/loader'
75
87
  import type { HermesGateway } from '@/hermes'
88
+ import { useResizeObserver } from '@/hooks/use-resize-observer'
89
+ import { useI18n } from '@/i18n'
90
+ import { attachmentDisplayText, attachmentId, pathLabel } from '@/lib/chat-runtime'
76
91
  import { DATA_IMAGE_URL_RE } from '@/lib/embedded-images'
92
+ import { LinkifiedText } from '@/lib/external-link'
77
93
  import { triggerHaptic } from '@/lib/haptics'
78
- import { GitBranchIcon, Loader2Icon, Volume2Icon, VolumeXIcon } from '@/lib/icons'
94
+ import { GitBranchIcon, Loader2Icon, Volume2Icon, VolumeXIcon, XIcon } from '@/lib/icons'
79
95
  import { extractPreviewTargets } from '@/lib/preview-targets'
80
96
  import { useEnterAnimation } from '@/lib/use-enter-animation'
81
97
  import { cn } from '@/lib/utils'
82
98
  import { playSpeechText, stopVoicePlayback } from '@/lib/voice-playback'
99
+ import { $compactionActive } from '@/store/compaction'
100
+ import type { ComposerAttachment } from '@/store/composer'
83
101
  import { notifyError } from '@/store/notifications'
102
+ import { $connection } from '@/store/session'
103
+ import { notifyThreadEditClose, notifyThreadEditOpen } from '@/store/thread-scroll'
84
104
  import { $voicePlayback } from '@/store/voice-playback'
85
105
 
86
106
  type ThreadLoadingState = 'response' | 'session'
87
107
 
88
108
  interface MessageActionProps {
89
109
  messageId: string
90
- messageText: string
110
+ /** Lazy accessor — reads the live message text at action time. Passing the
111
+ * text itself as a prop forces the whole footer to re-render on every
112
+ * streaming delta flush (the text changes ~30×/s), which profiling showed
113
+ * was a large slice of per-token script time on long transcripts. */
114
+ getMessageText: () => string
91
115
  onBranchInNewChat?: (messageId: string) => void
92
116
  }
93
117
 
@@ -115,9 +139,27 @@ function messageContentText(content: unknown): string {
115
139
  return Array.isArray(content) ? content.map(partText).join('').trim() : ''
116
140
  }
117
141
 
118
- const INTERRUPTED_ONLY_RE = /^_?\[interrupted\]_?$/i
142
+ // Cheap streaming-stable "does this message have visible text" check: returns
143
+ // on the first non-whitespace text part without concatenating the whole
144
+ // message. Used as a useAuiState selector so its boolean output stays stable
145
+ // across token flushes (flips false→true once per turn).
146
+ function contentHasVisibleText(content: unknown): boolean {
147
+ if (typeof content === 'string') {
148
+ return content.trim().length > 0
149
+ }
150
+
151
+ if (!Array.isArray(content)) {
152
+ return false
153
+ }
154
+
155
+ for (const part of content) {
156
+ if (partText(part).trim().length > 0) {
157
+ return true
158
+ }
159
+ }
119
160
 
120
- const isInterruptedOnlyMessage = (text: string) => INTERRUPTED_ONLY_RE.test(text.trim())
161
+ return false
162
+ }
121
163
 
122
164
  export const Thread: FC<{
123
165
  clampToComposer?: boolean
@@ -127,6 +169,8 @@ export const Thread: FC<{
127
169
  loading?: ThreadLoadingState
128
170
  onBranchInNewChat?: (messageId: string) => void
129
171
  onCancel?: () => Promise<void> | void
172
+ onDismissError?: (messageId: string) => void
173
+ onRestoreToMessage?: (messageId: string) => Promise<void> | void
130
174
  sessionId?: string | null
131
175
  sessionKey?: string | null
132
176
  }> = ({
@@ -137,41 +181,38 @@ export const Thread: FC<{
137
181
  loading,
138
182
  onBranchInNewChat,
139
183
  onCancel,
184
+ onDismissError,
185
+ onRestoreToMessage,
140
186
  sessionId = null,
141
187
  sessionKey
142
188
  }) => {
143
189
  const messageComponents = useMemo(
144
190
  () => ({
145
- AssistantMessage: () => <AssistantMessage onBranchInNewChat={onBranchInNewChat} />,
191
+ AssistantMessage: () => <AssistantMessage onBranchInNewChat={onBranchInNewChat} onDismissError={onDismissError} />,
146
192
  SystemMessage,
147
193
  UserEditComposer: () => <UserEditComposer cwd={cwd} gateway={gateway} sessionId={sessionId} />,
148
- UserMessage: () => <UserMessage onCancel={onCancel} />
194
+ UserMessage: () => <UserMessage onCancel={onCancel} onRestoreToMessage={onRestoreToMessage} />
149
195
  }),
150
- [cwd, gateway, onBranchInNewChat, onCancel, sessionId]
196
+ [cwd, gateway, onBranchInNewChat, onCancel, onDismissError, onRestoreToMessage, sessionId]
151
197
  )
152
198
 
153
199
  const emptyPlaceholder = intro ? (
154
- <div
155
- className="flex min-h-0 w-full flex-col items-center justify-center"
156
- style={{ paddingBottom: 'var(--composer-measured-height)' }}
157
- >
200
+ <div className="flex min-h-0 w-full flex-col items-center justify-center pt-[var(--composer-measured-height)]">
158
201
  <Intro {...intro} />
159
202
  </div>
160
203
  ) : undefined
161
204
 
162
205
  return (
163
- <GeneratedImageProvider>
164
- <div className="relative grid h-full min-h-0 max-w-full grid-rows-[minmax(0,1fr)] overflow-hidden bg-transparent contain-[layout_paint]">
165
- <VirtualizedThread
166
- clampToComposer={clampToComposer}
167
- components={messageComponents}
168
- emptyPlaceholder={emptyPlaceholder}
169
- loadingIndicator={loading === 'response' ? <ResponseLoadingIndicator /> : null}
170
- sessionKey={sessionKey}
171
- />
172
- {loading === 'session' && <CenteredThreadSpinner />}
173
- </div>
174
- </GeneratedImageProvider>
206
+ <div className="relative grid h-full min-h-0 max-w-full grid-rows-[minmax(0,1fr)] overflow-hidden bg-transparent contain-[layout_paint]">
207
+ <ThreadMessageList
208
+ clampToComposer={clampToComposer}
209
+ components={messageComponents}
210
+ emptyPlaceholder={emptyPlaceholder}
211
+ loadingIndicator={loading === 'response' ? <ResponseLoadingIndicator /> : null}
212
+ sessionKey={sessionKey}
213
+ />
214
+ {loading === 'session' && <CenteredThreadSpinner />}
215
+ </div>
175
216
  )
176
217
  }
177
218
 
@@ -185,40 +226,63 @@ function pickPrimaryPreviewTarget(targets: string[]): string[] {
185
226
  return [localUrl || targets[targets.length - 1]]
186
227
  }
187
228
 
188
- const CenteredThreadSpinner: FC = () => (
189
- <div
190
- aria-label="Loading session"
191
- className="pointer-events-none absolute inset-0 z-1 grid place-items-center"
192
- role="status"
193
- >
194
- <Loader
195
- aria-hidden="true"
196
- className="size-12 text-midground/70"
197
- pathSteps={220}
198
- role="presentation"
199
- strokeScale={0.72}
200
- type="rose-curve"
201
- />
202
- </div>
203
- )
229
+ const CenteredThreadSpinner: FC = () => {
230
+ const { t } = useI18n()
231
+
232
+ return (
233
+ <div
234
+ aria-label={t.assistant.thread.loadingSession}
235
+ className="pointer-events-none absolute inset-0 z-1 grid place-items-center"
236
+ role="status"
237
+ >
238
+ <Loader
239
+ aria-hidden="true"
240
+ className="size-12 text-midground/70"
241
+ pathSteps={220}
242
+ role="presentation"
243
+ strokeScale={0.72}
244
+ type="rose-curve"
245
+ />
246
+ </div>
247
+ )
248
+ }
204
249
 
205
- const AssistantMessage: FC<{ onBranchInNewChat?: (messageId: string) => void }> = ({ onBranchInNewChat }) => {
250
+ const AssistantMessage: FC<{
251
+ onBranchInNewChat?: (messageId: string) => void
252
+ onDismissError?: (messageId: string) => void
253
+ }> = ({ onBranchInNewChat, onDismissError }) => {
206
254
  const messageId = useAuiState(s => s.message.id)
207
- const content = useAuiState(s => s.message.content)
208
- const messageText = messageContentText(content)
209
- const hoistedTodos = useMemo(() => todosFromMessageContent(content), [content])
255
+ const messageRuntime = useMessageRuntime()
256
+ const { t } = useI18n()
257
+
258
+ // PERF: this component must NOT subscribe to the streaming text. Every
259
+ // selector here returns a value that stays referentially stable across
260
+ // token flushes (booleans, status strings, '' while running), so the
261
+ // 30 Hz delta stream only re-renders the markdown part and the tiny
262
+ // StreamStallIndicator leaf — not the footer/preview/root subtree.
263
+ const messageStatus = useAuiState(s => s.message.status?.type)
264
+ const isRunning = messageStatus === 'running'
265
+ const isPlaceholder = useAuiState(s => s.message.status?.type === 'running' && s.message.content.length === 0)
266
+ const hasVisibleText = useAuiState(s => contentHasVisibleText(s.message.content))
267
+
268
+ // Preview targets only materialize once the turn completes — while running
269
+ // the selector returns '' (stable), so per-token flushes skip the regex
270
+ // scan and the re-render it would cause.
271
+ const completedText = useAuiState(s =>
272
+ s.message.status?.type === 'running' ? '' : messageContentText(s.message.content)
273
+ )
210
274
 
211
275
  const previewTargets = useMemo(() => {
212
- if (!messageText || !/(https?:\/\/|file:\/\/)/i.test(messageText)) {
276
+ if (!completedText || !/(https?:\/\/|file:\/\/)/i.test(completedText)) {
213
277
  return []
214
278
  }
215
279
 
216
- return pickPrimaryPreviewTarget(extractPreviewTargets(messageText))
217
- }, [messageText])
280
+ return pickPrimaryPreviewTarget(extractPreviewTargets(completedText))
281
+ }, [completedText])
218
282
 
219
- const messageStatus = useAuiState(s => s.message.status?.type)
220
- const isPlaceholder = messageStatus === 'running' && content.length === 0
221
- const interruptedOnly = useMemo(() => isInterruptedOnlyMessage(messageText), [messageText])
283
+ const getMessageText = useCallback(() => messageContentText(messageRuntime.getState().content), [messageRuntime])
284
+
285
+ const enterRef = useEnterAnimation(isRunning, `assistant-message:${messageId}`)
222
286
 
223
287
  if (isPlaceholder) {
224
288
  return null
@@ -229,16 +293,16 @@ const AssistantMessage: FC<{ onBranchInNewChat?: (messageId: string) => void }>
229
293
  className="group flex w-full min-w-0 max-w-full flex-col gap-0 self-start overflow-hidden"
230
294
  data-role="assistant"
231
295
  data-slot="aui_assistant-message-root"
296
+ data-streaming={isRunning ? 'true' : undefined}
297
+ ref={enterRef}
232
298
  >
233
299
  <div
234
- className={cn(
235
- 'wrap-anywhere min-w-0 max-w-full overflow-hidden text-pretty text-[length:var(--conversation-text-font-size)] leading-(--dt-line-height) text-foreground',
236
- interruptedOnly && 'text-[0.8rem] leading-5 text-muted-foreground/82'
237
- )}
300
+ className="wrap-anywhere min-w-0 max-w-full overflow-hidden text-pretty text-[length:var(--conversation-text-font-size)] leading-(--dt-line-height) text-foreground"
238
301
  data-slot="aui_assistant-message-content"
239
302
  >
240
- {hoistedTodos.length > 0 && <HoistedTodoPanel todos={hoistedTodos} />}
303
+ {/* Todos render in the composer status stack now, not inline. */}
241
304
  <MessagePrimitive.Parts components={MESSAGE_PARTS_COMPONENTS} />
305
+ {isRunning && <StreamStallIndicator />}
242
306
  {previewTargets.length > 0 && (
243
307
  <div className="mt-3 flex flex-wrap gap-2">
244
308
  {previewTargets.map(target => (
@@ -248,15 +312,25 @@ const AssistantMessage: FC<{ onBranchInNewChat?: (messageId: string) => void }>
248
312
  )}
249
313
  <MessagePrimitive.Error>
250
314
  <ErrorPrimitive.Root
251
- className="mt-1.5 text-[0.78rem] leading-5 text-[color-mix(in_srgb,var(--dt-destructive)_78%,var(--ui-text-secondary))]"
315
+ className="mt-1.5 flex items-start gap-1.5 text-[0.78rem] leading-5 text-[color-mix(in_srgb,var(--dt-destructive)_78%,var(--ui-text-secondary))]"
252
316
  role="alert"
253
317
  >
254
- <ErrorPrimitive.Message />
318
+ <ErrorPrimitive.Message className="min-w-0 flex-1" />
319
+ {onDismissError && (
320
+ <TooltipIconButton
321
+ className="-my-0.5 shrink-0 text-current opacity-70 hover:opacity-100"
322
+ onClick={() => onDismissError(messageId)}
323
+ side="top"
324
+ tooltip={t.assistant.thread.dismissError}
325
+ >
326
+ <XIcon className="size-3.5" />
327
+ </TooltipIconButton>
328
+ )}
255
329
  </ErrorPrimitive.Root>
256
330
  </MessagePrimitive.Error>
257
331
  </div>
258
- {messageText.trim().length > 0 && !interruptedOnly && (
259
- <AssistantFooter messageId={messageId} messageText={messageText} onBranchInNewChat={onBranchInNewChat} />
332
+ {hasVisibleText && (
333
+ <AssistantFooter getMessageText={getMessageText} messageId={messageId} onBranchInNewChat={onBranchInNewChat} />
260
334
  )}
261
335
  </MessagePrimitive.Root>
262
336
  )
@@ -279,32 +353,94 @@ const StatusRow: FC<{ children: ReactNode; label: string } & React.ComponentProp
279
353
  </div>
280
354
  )
281
355
 
356
+ // Fixed label while auto-compaction runs — decoupled from backend status text.
357
+ const COMPACTION_LABEL = 'Summarizing thread'
358
+
359
+ const CompactionHint: FC = () => (
360
+ <span className="shimmer min-w-0 truncate text-muted-foreground/55">{COMPACTION_LABEL}</span>
361
+ )
362
+
282
363
  const ResponseLoadingIndicator: FC = () => {
364
+ const { t } = useI18n()
283
365
  const elapsed = useElapsedSeconds()
366
+ const compacting = useStore($compactionActive)
284
367
 
285
368
  return (
286
- <StatusRow data-slot="aui_response-loading" label="Hermes is loading a response">
369
+ <StatusRow
370
+ data-slot="aui_response-loading"
371
+ label={compacting ? COMPACTION_LABEL : t.assistant.thread.loadingResponse}
372
+ >
287
373
  <span aria-hidden="true" className="dither inline-block size-3 rounded-[2px] text-midground/80 animate-pulse" />
374
+ {compacting && <CompactionHint />}
288
375
  <ActivityTimerText seconds={elapsed} />
289
376
  </StatusRow>
290
377
  )
291
378
  }
292
379
 
293
- const ImageGenerateTool: FC<ToolCallMessagePartProps> = ({ result }) => {
294
- const generatedImage = useGeneratedImageContext()
295
- const running = result === undefined
380
+ // Seconds of no visible output (text or part count) before a still-running turn
381
+ // is treated as stalled and the thinking indicator returns at the tail.
382
+ const STREAM_STALL_S = 2
383
+
384
+ // Tail "still thinking" indicator: the pre-first-token spinner goes away once
385
+ // text flows, but if the stream then goes quiet mid-turn (tool think-time,
386
+ // provider stall) nothing signals that work continues. Watch a per-flush
387
+ // activity signal; when it hasn't changed for STREAM_STALL_S, re-show the
388
+ // dither + a timer counting from the last activity.
389
+ //
390
+ // Subscribes to the activity signal ITSELF (rather than taking it as a prop)
391
+ // so that per-token updates re-render only this leaf, not the whole
392
+ // AssistantMessage subtree.
393
+ const StreamStallIndicator: FC = () => {
394
+ const activity = useAuiState(s => {
395
+ let textLength = 0
396
+
397
+ for (const part of s.message.content) {
398
+ const text = (part as { text?: unknown }).text
399
+
400
+ if (typeof text === 'string') {
401
+ textLength += text.length
402
+ }
403
+ }
404
+
405
+ return `${s.message.content.length}:${textLength}`
406
+ })
407
+
408
+ const [stalled, setStalled] = useState(false)
409
+ const compacting = useStore($compactionActive)
296
410
 
297
411
  useEffect(() => {
298
- generatedImage?.setPending(running)
299
- }, [generatedImage, running])
412
+ setStalled(false)
413
+ const id = window.setTimeout(() => setStalled(true), STREAM_STALL_S * 1000)
414
+
415
+ return () => window.clearTimeout(id)
416
+ }, [activity])
417
+
418
+ const active = stalled || compacting
419
+ const elapsed = useElapsedSeconds(active)
300
420
 
301
- if (!running) {
421
+ if (!active) {
302
422
  return null
303
423
  }
304
424
 
425
+ return (
426
+ <StatusRow
427
+ className="mt-1.5"
428
+ data-slot="aui_stream-stall"
429
+ label={compacting ? COMPACTION_LABEL : 'Hermes is thinking'}
430
+ >
431
+ <span aria-hidden="true" className="dither inline-block size-3 rounded-[2px] text-midground/80 animate-pulse" />
432
+ {compacting && <CompactionHint />}
433
+ <ActivityTimerText seconds={elapsed} />
434
+ </StatusRow>
435
+ )
436
+ }
437
+
438
+ const ImageGenerateTool: FC<ToolCallMessagePartProps> = ({ args, result }) => {
439
+ const aspectRatio = typeof args?.aspect_ratio === 'string' ? args.aspect_ratio : undefined
440
+
305
441
  return (
306
442
  <div className="mt-1.5">
307
- <ImageGenerationPlaceholder />
443
+ <GeneratedImage aspectRatio={aspectRatio} result={result} />
308
444
  </div>
309
445
  )
310
446
  }
@@ -332,6 +468,7 @@ const ThinkingDisclosure: FC<{
332
468
  pending?: boolean
333
469
  timerKey?: string
334
470
  }> = ({ children, messageRunning = false, pending = false, timerKey }) => {
471
+ const { t } = useI18n()
335
472
  // `null` = no explicit user toggle yet, defer to the streaming default.
336
473
  // The default is "auto-open while streaming, auto-collapse when done" so
337
474
  // reasoning surfaces a live preview without manual interaction. The first
@@ -370,7 +507,9 @@ const ThinkingDisclosure: FC<{
370
507
  observer.observe(content)
371
508
 
372
509
  return () => observer.disconnect()
373
- }, [isPreview])
510
+ // Re-run when the disclosure toggles so the observer attaches to the new
511
+ // DOM after expand/collapse (refs are conditionally rendered on `open`).
512
+ }, [isPreview, open])
374
513
 
375
514
  return (
376
515
  <div
@@ -386,7 +525,7 @@ const ThinkingDisclosure: FC<{
386
525
  pending && 'shimmer text-foreground/55'
387
526
  )}
388
527
  >
389
- Thinking
528
+ {t.assistant.thread.thinking}
390
529
  </span>
391
530
  {pending && (
392
531
  <ActivityTimerText
@@ -431,10 +570,26 @@ const ReasoningAccordionGroup: FC<{ children?: ReactNode; endIndex: number; star
431
570
  s.thread.isRunning &&
432
571
  s.message.status?.type === 'running' &&
433
572
  s.message.parts
434
- .slice(Math.max(0, startIndex), Math.min(s.message.parts.length, endIndex))
573
+ .slice(Math.max(0, startIndex), endIndex + 1)
435
574
  .some(p => p?.type === 'reasoning' && p.status?.type !== 'complete')
436
575
  )
437
576
 
577
+ // A reasoning group with no actual text is pure noise — drop the whole
578
+ // "Thinking" disclosure rather than leave an empty header eating a row. This
579
+ // applies live too: encrypted/spinner-coerced reasoning (Opus reasoning max)
580
+ // never carries visible text, and the bottom-of-thread loader already signals
581
+ // "thinking", so an empty header is never wanted. Real reasoning surfaces the
582
+ // instant its first token lands.
583
+ const hasContent = useAuiState(s =>
584
+ s.message.parts
585
+ .slice(Math.max(0, startIndex), endIndex + 1)
586
+ .some(p => p?.type === 'reasoning' && typeof p.text === 'string' && p.text.trim().length > 0)
587
+ )
588
+
589
+ if (!hasContent) {
590
+ return null
591
+ }
592
+
438
593
  return (
439
594
  <ThinkingDisclosure messageRunning={messageRunning} pending={pending} timerKey={`reasoning:${messageId}`}>
440
595
  {children}
@@ -444,17 +599,16 @@ const ReasoningAccordionGroup: FC<{ children?: ReactNode; endIndex: number; star
444
599
 
445
600
  const ReasoningTextPart: FC<{ text: string; status?: { type: string } }> = ({ text, status }) => {
446
601
  const displayText = text.trimStart()
602
+ const messageRunning = useAuiState(s => s.message.status?.type === 'running')
603
+ const isRunning = status?.type === 'running' || messageRunning
447
604
 
448
605
  return (
449
- <div
450
- className={cn(
451
- 'whitespace-pre-wrap text-xs leading-relaxed text-muted-foreground/85',
452
- status?.type === 'running' && 'shimmer text-muted-foreground/55'
453
- )}
454
- data-slot="aui_reasoning-text"
455
- >
456
- {displayText}
457
- </div>
606
+ <MarkdownTextContent
607
+ containerClassName="text-xs leading-snug text-muted-foreground/85"
608
+ containerProps={{ 'data-slot': 'aui_reasoning-text' } as ComponentProps<'div'>}
609
+ isRunning={isRunning}
610
+ text={displayText}
611
+ />
458
612
  )
459
613
  }
460
614
 
@@ -486,7 +640,10 @@ function startOfDay(d: Date): number {
486
640
  return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime()
487
641
  }
488
642
 
489
- function formatMessageTimestamp(value: Date | string | number | undefined): string {
643
+ function formatMessageTimestamp(
644
+ value: Date | string | number | undefined,
645
+ labels: { today: (time: string) => string; yesterday: (time: string) => string }
646
+ ): string {
490
647
  if (!value) {
491
648
  return ''
492
649
  }
@@ -500,17 +657,19 @@ function formatMessageTimestamp(value: Date | string | number | undefined): stri
500
657
  const dayDelta = Math.round((startOfDay(new Date()) - startOfDay(date)) / 86_400_000)
501
658
 
502
659
  if (dayDelta === 0) {
503
- return `Today, ${TIME_FMT.format(date)}`
660
+ return labels.today(TIME_FMT.format(date))
504
661
  }
505
662
 
506
663
  if (dayDelta === 1) {
507
- return `Yesterday, ${TIME_FMT.format(date)}`
664
+ return labels.yesterday(TIME_FMT.format(date))
508
665
  }
509
666
 
510
667
  return SHORT_FMT.format(date)
511
668
  }
512
669
 
513
- const AssistantActionBar: FC<MessageActionProps> = ({ messageId, messageText, onBranchInNewChat }) => {
670
+ const AssistantActionBar: FC<MessageActionProps> = ({ messageId, getMessageText, onBranchInNewChat }) => {
671
+ const { t } = useI18n()
672
+ const copy = t.assistant.thread
514
673
  const [menuOpen, setMenuOpen] = useState(false)
515
674
 
516
675
  return (
@@ -529,15 +688,15 @@ const AssistantActionBar: FC<MessageActionProps> = ({ messageId, messageText, on
529
688
  )}
530
689
  data-slot="aui_msg-actions"
531
690
  >
532
- <CopyButton appearance="icon" buttonSize="icon" disabled={!messageText} label="Copy" text={messageText} />
691
+ <CopyButton appearance="icon" buttonSize="icon" label={copy.copy} text={getMessageText} />
533
692
  <ActionBarPrimitive.Reload asChild>
534
- <TooltipIconButton onClick={() => triggerHaptic('submit')} tooltip="Refresh">
693
+ <TooltipIconButton onClick={() => triggerHaptic('submit')} tooltip={copy.refresh}>
535
694
  <Codicon name="refresh" />
536
695
  </TooltipIconButton>
537
696
  </ActionBarPrimitive.Reload>
538
697
  <DropdownMenu onOpenChange={setMenuOpen} open={menuOpen}>
539
698
  <DropdownMenuTrigger asChild>
540
- <TooltipIconButton tooltip="More actions">
699
+ <TooltipIconButton tooltip={copy.moreActions}>
541
700
  <Codicon name="ellipsis" />
542
701
  </TooltipIconButton>
543
702
  </DropdownMenuTrigger>
@@ -545,9 +704,9 @@ const AssistantActionBar: FC<MessageActionProps> = ({ messageId, messageText, on
545
704
  <MessageTimestamp />
546
705
  <DropdownMenuItem onSelect={() => onBranchInNewChat?.(messageId)}>
547
706
  <GitBranchIcon />
548
- Branch in new chat
707
+ {copy.branchNewChat}
549
708
  </DropdownMenuItem>
550
- <ReadAloudItem messageId={messageId} text={messageText} />
709
+ <ReadAloudItem getText={getMessageText} messageId={messageId} />
551
710
  </DropdownMenuContent>
552
711
  </DropdownMenu>
553
712
  </ActionBarPrimitive.Root>
@@ -555,7 +714,9 @@ const AssistantActionBar: FC<MessageActionProps> = ({ messageId, messageText, on
555
714
  )
556
715
  }
557
716
 
558
- const ReadAloudItem: FC<{ messageId: string; text: string }> = ({ messageId, text }) => {
717
+ const ReadAloudItem: FC<{ getText: () => string; messageId: string }> = ({ getText, messageId }) => {
718
+ const { t } = useI18n()
719
+ const copy = t.assistant.thread
559
720
  const voicePlayback = useStore($voicePlayback)
560
721
 
561
722
  const readAloudStatus =
@@ -567,6 +728,8 @@ const ReadAloudItem: FC<{ messageId: string; text: string }> = ({ messageId, tex
567
728
  const Icon = isPreparing ? Loader2Icon : isSpeaking ? VolumeXIcon : Volume2Icon
568
729
 
569
730
  const read = useCallback(async () => {
731
+ const text = getText()
732
+
570
733
  if (!text || $voicePlayback.get().status !== 'idle') {
571
734
  return
572
735
  }
@@ -574,27 +737,28 @@ const ReadAloudItem: FC<{ messageId: string; text: string }> = ({ messageId, tex
574
737
  try {
575
738
  await playSpeechText(text, { messageId, source: 'read-aloud' })
576
739
  } catch (error) {
577
- notifyError(error, 'Read aloud failed')
740
+ notifyError(error, copy.readAloudFailed)
578
741
  }
579
- }, [messageId, text])
742
+ }, [copy.readAloudFailed, getText, messageId])
580
743
 
581
744
  return (
582
745
  <DropdownMenuItem
583
- disabled={isPreparing || (!isSpeaking && (anyPlaybackActive || !text))}
746
+ disabled={isPreparing || (!isSpeaking && anyPlaybackActive)}
584
747
  onSelect={e => {
585
748
  e.preventDefault()
586
749
  void (isSpeaking ? stopVoicePlayback() : read())
587
750
  }}
588
751
  >
589
752
  <Icon className={isPreparing ? 'animate-spin' : undefined} />
590
- {isPreparing ? 'Preparing audio...' : isSpeaking ? 'Stop reading' : 'Read aloud'}
753
+ {isPreparing ? copy.preparingAudio : isSpeaking ? copy.stopReading : copy.readAloud}
591
754
  </DropdownMenuItem>
592
755
  )
593
756
  }
594
757
 
595
758
  const MessageTimestamp: FC = () => {
759
+ const { t } = useI18n()
596
760
  const createdAt = useAuiState(s => s.message.createdAt)
597
- const label = formatMessageTimestamp(createdAt)
761
+ const label = formatMessageTimestamp(createdAt, t.assistant.thread)
598
762
 
599
763
  if (!label) {
600
764
  return null
@@ -609,13 +773,13 @@ const AssistantFooter: FC<MessageActionProps> = props => (
609
773
  className="inline-flex h-6 items-center gap-1 text-xs text-muted-foreground"
610
774
  hideWhenSingleBranch
611
775
  >
612
- <BranchPickerPrimitive.Previous className="grid size-6 cursor-pointer place-items-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground disabled:cursor-default disabled:opacity-35">
776
+ <BranchPickerPrimitive.Previous className="grid size-6 place-items-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground disabled:cursor-default disabled:opacity-35">
613
777
  <Codicon name="chevron-left" size="0.875rem" />
614
778
  </BranchPickerPrimitive.Previous>
615
779
  <span className="tabular-nums">
616
780
  <BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />
617
781
  </span>
618
- <BranchPickerPrimitive.Next className="grid size-6 cursor-pointer place-items-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground disabled:cursor-default disabled:opacity-35">
782
+ <BranchPickerPrimitive.Next className="grid size-6 place-items-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground disabled:cursor-default disabled:opacity-35">
619
783
  <Codicon name="chevron-right" size="0.875rem" />
620
784
  </BranchPickerPrimitive.Next>
621
785
  </BranchPickerPrimitive.Root>
@@ -633,34 +797,87 @@ function messageAttachmentRefs(value: unknown): string[] {
633
797
  return value.every(ref => typeof ref === 'string') ? value : EMPTY_ATTACHMENT_REFS
634
798
  }
635
799
 
636
- function StickyHumanMessageContainer({ children }: { children: ReactNode }) {
800
+ function StickyHumanMessageContainer({ attachments, children }: { attachments?: ReactNode; children: ReactNode }) {
637
801
  return (
638
- <div
639
- className="group/user-message sticky top-0 z-40 -mx-4 flex w-[calc(100%+2rem)] min-w-0 max-w-none flex-col items-stretch gap-0 self-end overflow-visible bg-(--ui-chat-surface-background) px-4 pb-(--conversation-turn-gap) pt-2"
640
- data-role="user"
641
- data-slot="aui_user-message-root"
642
- >
643
- {children}
644
- </div>
802
+ // Fragment, not a wrapper: a wrapping element becomes the sticky's
803
+ // containing block (it'd stick within its own height = never). The bubble
804
+ // and attachments are flow siblings so the bubble pins against the scroller
805
+ // while attachments below it scroll away.
806
+ <>
807
+ <div
808
+ className="group/user-message sticky z-40 -mx-4 flex w-[calc(100%+2rem)] min-w-0 max-w-none flex-col items-stretch gap-0 self-end overflow-visible bg-(--ui-chat-surface-background) px-4 pb-(--conversation-turn-gap) pt-1"
809
+ data-role="user"
810
+ data-slot="aui_user-message-root"
811
+ >
812
+ {children}
813
+ </div>
814
+ {attachments}
815
+ </>
645
816
  )
646
817
  }
647
818
 
648
819
  // Shared "user bubble" base. Both the read-only message and the inline
649
- // edit composer render the same bubble surface (rounded glass card,
650
- // shadow-composer); they only differ in border weight, cursor, and
651
- // padding-right (the read-only view reserves room for the restore icon).
820
+ // edit composer render the same bubble surface (rounded glass card);
821
+ // they only differ in border weight, cursor, and padding-right (the
822
+ // read-only view reserves room for the restore icon).
823
+ //
824
+ // no-drag: sticky bubbles park at --sticky-human-top (~4px), sliding under the
825
+ // titlebar's [-webkit-app-region:drag] strips (app-shell.tsx). Electron resolves
826
+ // drag regions at the compositor level — z-index and pointer-events don't help —
827
+ // so without the carve-out, clicking a stuck bubble drags the window instead of
828
+ // opening the edit composer.
652
829
  const USER_BUBBLE_BASE_CLASS =
653
- 'composer-human-message standalone-glass relative flex w-full min-w-0 max-w-full flex-col gap-1.5 overflow-hidden rounded-xl border bg-(--dt-user-bubble) px-3 py-2 text-left shadow-composer'
830
+ 'composer-human-message standalone-glass relative flex w-full min-w-0 max-w-full flex-col gap-1.5 overflow-y-auto rounded-xl border bg-(--dt-user-bubble) px-3 py-2 text-left [-webkit-app-region:no-drag]'
654
831
 
655
832
  const USER_ACTION_ICON_BUTTON_CLASS =
656
- 'grid cursor-pointer place-items-center rounded-md bg-transparent text-(--ui-text-secondary) transition-colors hover:bg-(--ui-control-active-background) hover:text-foreground disabled:cursor-default disabled:text-(--ui-text-quaternary) disabled:opacity-70'
833
+ 'grid place-items-center rounded-md bg-transparent text-(--ui-text-secondary) transition-colors hover:bg-(--ui-control-active-background) hover:text-foreground disabled:cursor-default disabled:text-(--ui-text-quaternary) disabled:opacity-70'
657
834
 
658
835
  const USER_ACTION_ICON_SIZE = '0.6875rem'
659
836
  const StopGlyph = <IconPlayerStopFilled aria-hidden className="size-3.5 -translate-y-px" />
660
837
 
838
+ // Background-process notifications are injected into the conversation as user
839
+ // messages (the agent must react to them, and message-role alternation forbids
840
+ // a synthetic system row mid-loop). They are NOT something the human typed, so
841
+ // render them as a compact system-style notice instead of a user bubble.
842
+ // Shape: see tools/process_registry.py format_process_notification().
843
+ const PROCESS_NOTIFICATION_RE = /^\[IMPORTANT: Background process [\s\S]*\]$/
844
+
845
+ const ProcessNotificationNote: FC<{ text: string }> = ({ text }) => {
846
+ const body = text.replace(/^\[IMPORTANT:\s*/, '').replace(/\]$/, '')
847
+ const newline = body.indexOf('\n')
848
+ const headline = (newline === -1 ? body : body.slice(0, newline)).trim()
849
+ const detail = newline === -1 ? '' : body.slice(newline + 1).trim()
850
+
851
+ return (
852
+ <div className="flex max-w-[min(86%,44rem)] flex-col gap-0.5 self-center px-2 py-0.5 text-[0.6875rem] leading-5 text-muted-foreground/60">
853
+ <span className="flex items-center gap-1.5">
854
+ <Codicon className="shrink-0 text-muted-foreground/55" name="terminal" size="0.75rem" />
855
+ <span className="wrap-anywhere">{headline}</span>
856
+ </span>
857
+ {detail && (
858
+ <details className="pl-[1.3125rem]">
859
+ <summary className="cursor-pointer select-none text-muted-foreground/45 hover:text-muted-foreground/70">
860
+ output
861
+ </summary>
862
+ <pre
863
+ className="mt-0.5 max-h-48 overflow-auto whitespace-pre-wrap font-mono text-[0.625rem] leading-4 text-muted-foreground/55"
864
+ data-selectable-text="true"
865
+ >
866
+ {detail}
867
+ </pre>
868
+ </details>
869
+ )}
870
+ </div>
871
+ )
872
+ }
873
+
661
874
  const UserMessage: FC<{
662
875
  onCancel?: () => Promise<void> | void
663
- }> = ({ onCancel }) => {
876
+ onRestoreToMessage?: (messageId: string) => Promise<void> | void
877
+ }> = ({ onCancel, onRestoreToMessage }) => {
878
+ const { t } = useI18n()
879
+ const copy = t.assistant.thread
880
+ const [restoreConfirmOpen, setRestoreConfirmOpen] = useState(false)
664
881
  const messageId = useAuiState(s => s.message.id)
665
882
  const content = useAuiState(s => s.message.content)
666
883
  const messageText = messageContentText(content)
@@ -684,77 +901,154 @@ const UserMessage: FC<{
684
901
  return messageAttachmentRefs(custom.attachmentRefs)
685
902
  })
686
903
 
904
+ // Sticky human bubbles clamp to ~2 lines with a soft fade so a long prompt
905
+ // doesn't dominate the viewport while the response streams underneath; the
906
+ // clamp lifts on hover / focus (see styles.css). We measure the *unclamped*
907
+ // inner wrapper so the ResizeObserver only fires on real content / width
908
+ // changes, not on every frame while the outer max-height animates open.
909
+ const clampInnerRef = useRef<HTMLDivElement | null>(null)
910
+ const [bodyClamped, setBodyClamped] = useState(false)
911
+ const lastClampHeightRef = useRef(-1)
912
+ const lineHeightRef = useRef(0)
913
+
914
+ const measureClamp = useCallback((entries: readonly ResizeObserverEntry[]) => {
915
+ const inner = clampInnerRef.current
916
+ const outer = inner?.parentElement
917
+
918
+ if (!inner || !outer) {
919
+ return
920
+ }
921
+
922
+ // Prefer the size the ResizeObserver already computed — reading
923
+ // `scrollHeight` outside RO timing forces a synchronous layout, and with
924
+ // many user bubbles observed at once those reads interleave with the
925
+ // style write below into a read-write-read reflow cascade.
926
+ const entryHeight = entries.find(entry => entry.target === inner)?.borderBoxSize?.[0]?.blockSize
927
+ const fullHeight = Math.ceil(entryHeight ?? inner.scrollHeight)
928
+
929
+ if (fullHeight === lastClampHeightRef.current) {
930
+ return
931
+ }
932
+
933
+ lastClampHeightRef.current = fullHeight
934
+
935
+ // Line-height is stable for the life of the bubble (font settings don't
936
+ // change under it) — resolve the computed style once.
937
+ if (!lineHeightRef.current) {
938
+ const styles = getComputedStyle(inner)
939
+ lineHeightRef.current = parseFloat(styles.lineHeight) || 1.5 * parseFloat(styles.fontSize) || 20
940
+ }
941
+
942
+ outer.style.setProperty('--human-msg-full', `${fullHeight}px`)
943
+ setBodyClamped(fullHeight > lineHeightRef.current * 2 + 1)
944
+ }, [])
945
+
946
+ useResizeObserver(measureClamp, clampInnerRef)
947
+
948
+ // Injected background-process notification, not a human prompt — render the
949
+ // compact system-style notice (after all hooks above have run).
950
+ if (PROCESS_NOTIFICATION_RE.test(messageText.trim())) {
951
+ return (
952
+ <MessagePrimitive.Root
953
+ className="flex w-full min-w-0 flex-col items-stretch"
954
+ data-role="user"
955
+ data-slot="aui_user-message-root"
956
+ >
957
+ <ProcessNotificationNote text={messageText.trim()} />
958
+ </MessagePrimitive.Root>
959
+ )
960
+ }
961
+
687
962
  const hasBody = messageText.trim().length > 0
688
963
  const isLatestUser = messageId === latestUserId
689
964
  const showStop = isLatestUser && threadRunning && Boolean(onCancel)
690
- const showRestore = !isLatestUser && !threadRunning
965
+ // Restore (re-run this exact prompt) is available everywhere the Stop button
966
+ // isn't — including mid-stream on older prompts, since the action interrupts
967
+ // the live turn before rewinding.
968
+ const showRestore = !showStop && Boolean(onRestoreToMessage) && hasBody
691
969
 
692
970
  const bubbleClassName = cn(
693
971
  USER_BUBBLE_BASE_CLASS,
694
- 'border-(--ui-stroke-tertiary) pr-9 text-[length:var(--conversation-text-font-size)] leading-(--dt-line-height) text-foreground/95 transition-colors',
695
- !threadRunning && 'cursor-pointer hover:border-(--ui-stroke-secondary)'
972
+ 'cursor-pointer pr-9 text-[length:var(--conversation-text-font-size)] leading-(--dt-line-height) text-foreground/95 transition-colors',
973
+ 'border-(--ui-stroke-tertiary) hover:border-(--ui-stroke-secondary)'
696
974
  )
697
975
 
698
- const bubbleContent = (
699
- <>
700
- {attachmentRefs.length > 0 && (
701
- <span className="-mx-1 flex flex-wrap gap-1 border-b border-border/45 pb-1.5">
702
- <DirectiveContent text={attachmentRefs.join(' ')} />
703
- </span>
704
- )}
705
- {hasBody && (
706
- <span className="wrap-anywhere block whitespace-pre-line">
707
- <MessagePrimitive.Parts components={{ Text: DirectiveText }} />
708
- </span>
709
- )}
710
- </>
976
+ const bubbleContent = hasBody && (
977
+ // Render the user's text through a minimal markdown pipeline:
978
+ // backtick `code` and ``` fenced ``` blocks, with directive chips
979
+ // (`@file:` etc.) still resolved inside the plain-text spans.
980
+ <div className="sticky-human-clamp" data-clamped={bodyClamped ? 'true' : undefined}>
981
+ {/* Match the edit composer's collapsed line box (min-h-[1.25rem]) so
982
+ clicking to edit can't grow the bubble by a sub-pixel and reflow the
983
+ turn 1px. */}
984
+ <div className="min-h-[1.25rem]" ref={clampInnerRef}>
985
+ <UserMessageText className="wrap-anywhere" text={messageText} />
986
+ </div>
987
+ </div>
711
988
  )
712
989
 
713
990
  return (
714
991
  <MessagePrimitive.Root asChild>
715
- <StickyHumanMessageContainer>
992
+ <StickyHumanMessageContainer
993
+ attachments={
994
+ // Attachments live BELOW the sticky bubble in normal flow, so they
995
+ // scroll away behind the pinned bubble instead of riding along with
996
+ // it. Image refs render as thumbnails, file refs as chips; no border.
997
+ attachmentRefs.length > 0 ? (
998
+ <div className="flex flex-wrap gap-1 -mt-3 mb-2">
999
+ <DirectiveContent text={attachmentRefs.join(' ')} />
1000
+ </div>
1001
+ ) : null
1002
+ }
1003
+ >
716
1004
  <ActionBarPrimitive.Root className="relative w-full max-w-full" data-slot="aui_user-bubble-actions">
717
1005
  <div className="human-message-with-todos-wrapper flex w-full flex-col gap-0">
718
1006
  <div className="relative w-full">
719
- {threadRunning ? (
720
- <div className={bubbleClassName}>{bubbleContent}</div>
721
- ) : (
722
- <ActionBarPrimitive.Edit asChild>
723
- <button
724
- aria-label="Edit message"
725
- className={bubbleClassName}
726
- onClick={() => triggerHaptic('selection')}
727
- title="Edit message"
728
- type="button"
729
- >
730
- {bubbleContent}
731
- </button>
732
- </ActionBarPrimitive.Edit>
733
- )}
1007
+ {/* Always editable — clicking opens the edit composer even while a
1008
+ turn streams; sending the edit reverts (interrupt + rewind). */}
1009
+ <ActionBarPrimitive.Edit asChild>
1010
+ <button
1011
+ aria-label={copy.editMessage}
1012
+ className={bubbleClassName}
1013
+ onClick={() => triggerHaptic('selection')}
1014
+ onPointerDown={() => notifyThreadEditOpen()}
1015
+ title={copy.editMessage}
1016
+ type="button"
1017
+ >
1018
+ {bubbleContent}
1019
+ </button>
1020
+ </ActionBarPrimitive.Edit>
734
1021
  {(showStop || showRestore) && (
735
1022
  <div className="pointer-events-none absolute right-2 bottom-2 z-10 flex items-center justify-center opacity-0 transition-opacity group-hover/user-message:opacity-100 group-focus-within/user-message:opacity-100">
736
1023
  {showStop ? (
737
1024
  <button
738
- aria-label="Stop"
1025
+ aria-label={copy.stop}
739
1026
  className={cn('pointer-events-auto size-5', USER_ACTION_ICON_BUTTON_CLASS)}
740
1027
  onClick={event => {
741
1028
  event.preventDefault()
742
1029
  event.stopPropagation()
743
1030
  void onCancel?.()
744
1031
  }}
745
- title="Stop"
1032
+ title={copy.stop}
746
1033
  type="button"
747
1034
  >
748
1035
  {StopGlyph}
749
1036
  </button>
750
1037
  ) : (
751
- <span
752
- aria-hidden="true"
753
- className="flex size-6 items-center justify-center rounded-md text-(--ui-text-tertiary)"
754
- title="Editable checkpoint"
1038
+ <button
1039
+ aria-label={copy.restoreCheckpoint}
1040
+ className={cn('pointer-events-auto size-6', USER_ACTION_ICON_BUTTON_CLASS)}
1041
+ onClick={event => {
1042
+ event.preventDefault()
1043
+ event.stopPropagation()
1044
+ triggerHaptic('selection')
1045
+ setRestoreConfirmOpen(true)
1046
+ }}
1047
+ title={copy.restoreFromHere}
1048
+ type="button"
755
1049
  >
756
1050
  <Codicon name="discard" size="0.875rem" />
757
- </span>
1051
+ </button>
758
1052
  )}
759
1053
  </div>
760
1054
  )}
@@ -765,29 +1059,41 @@ const UserMessage: FC<{
765
1059
  >
766
1060
  <span aria-hidden className="checkpoint-icon size-1.5 rounded-full border border-current" />
767
1061
  <BranchPickerPrimitive.Previous
768
- className="checkpoint-restore-text cursor-pointer rounded-sm bg-transparent px-1 opacity-65 hover:opacity-100 disabled:hidden disabled:cursor-default"
769
- title="Restore previous checkpoint"
1062
+ className="checkpoint-restore-text rounded-sm bg-transparent px-1 opacity-65 hover:opacity-100 disabled:hidden disabled:cursor-default"
1063
+ title={copy.restorePrevious}
770
1064
  >
771
- Restore checkpoint
1065
+ {copy.restoreCheckpoint}
772
1066
  </BranchPickerPrimitive.Previous>
773
1067
  <span className="checkpoint-divider opacity-55">
774
1068
  <BranchPickerPrimitive.Number />/<BranchPickerPrimitive.Count />
775
1069
  </span>
776
1070
  <BranchPickerPrimitive.Next
777
- className="checkpoint-restore-text cursor-pointer rounded-sm bg-transparent px-1 opacity-65 hover:opacity-100 disabled:hidden disabled:cursor-default"
778
- title="Restore next checkpoint"
1071
+ className="checkpoint-restore-text rounded-sm bg-transparent px-1 opacity-65 hover:opacity-100 disabled:hidden disabled:cursor-default"
1072
+ title={copy.restoreNext}
779
1073
  >
780
- Go forward
1074
+ {copy.goForward}
781
1075
  </BranchPickerPrimitive.Next>
782
1076
  </BranchPickerPrimitive.Root>
783
1077
  </div>
784
1078
  </ActionBarPrimitive.Root>
1079
+ {showRestore && (
1080
+ <ConfirmDialog
1081
+ confirmLabel={copy.restoreConfirm}
1082
+ description={copy.restoreBody}
1083
+ destructive
1084
+ onClose={() => setRestoreConfirmOpen(false)}
1085
+ onConfirm={() => onRestoreToMessage?.(messageId)}
1086
+ open={restoreConfirmOpen}
1087
+ title={copy.restoreTitle}
1088
+ />
1089
+ )}
785
1090
  </StickyHumanMessageContainer>
786
1091
  </MessagePrimitive.Root>
787
1092
  )
788
1093
  }
789
1094
 
790
1095
  const SLASH_STATUS_RE = /^slash:(?<command>\/[^\n]+)\n(?<output>[\s\S]*)$/
1096
+ const STEER_NOTE_RE = /^steer:(?<text>[\s\S]+)$/
791
1097
 
792
1098
  const SystemMessage: FC = () => {
793
1099
  const text = useAuiState(s => messageContentText(s.message.content))
@@ -796,29 +1102,66 @@ const SystemMessage: FC = () => {
796
1102
  return null
797
1103
  }
798
1104
 
1105
+ const steerNote = text.match(STEER_NOTE_RE)
1106
+
1107
+ if (steerNote?.groups) {
1108
+ return (
1109
+ <MessagePrimitive.Root
1110
+ className="flex max-w-[min(86%,44rem)] items-center gap-1.5 self-center px-2 py-0.5 text-[0.6875rem] leading-5 text-muted-foreground/60"
1111
+ data-role="system"
1112
+ data-slot="aui_system-message-root"
1113
+ >
1114
+ <Codicon className="text-muted-foreground/55" name="compass" size="0.75rem" />
1115
+ <span className="text-muted-foreground/55">steered</span>
1116
+ <span className="text-muted-foreground/35">·</span>
1117
+ <span className="whitespace-pre-wrap">{steerNote.groups.text.trim()}</span>
1118
+ </MessagePrimitive.Root>
1119
+ )
1120
+ }
1121
+
799
1122
  const slashStatus = text.match(SLASH_STATUS_RE)
800
1123
 
801
1124
  if (slashStatus?.groups) {
1125
+ const output = slashStatus.groups.output.trim()
1126
+ // Single-line status (e.g. "model → x") reads best centered inline; padded
1127
+ // multiline output (catalogs, usage tables) needs left-aligned, wider room
1128
+ // or the column alignment breaks.
1129
+ const multiline = output.includes('\n')
1130
+
802
1131
  return (
803
1132
  <MessagePrimitive.Root
804
- className="max-w-[min(86%,44rem)] self-center px-2 py-0.5 text-center text-[0.6875rem] leading-5 text-muted-foreground/60"
1133
+ className={cn(
1134
+ 'w-[60%] max-w-[44rem] self-center px-2 py-0.5 text-[0.6875rem] leading-5 text-muted-foreground/60',
1135
+ multiline ? 'text-left' : 'text-center'
1136
+ )}
805
1137
  data-role="system"
806
1138
  data-slot="aui_system-message-root"
807
1139
  >
808
1140
  <span className="font-mono text-muted-foreground/55">{slashStatus.groups.command}</span>
809
- <span className="mx-1.5 text-muted-foreground/35">·</span>
810
- <span className="whitespace-pre-wrap">{slashStatus.groups.output.trim()}</span>
1141
+ {multiline ? (
1142
+ <LinkifiedText className="mt-0.5 block whitespace-pre-wrap" explicitOnly pretty={false} text={output} />
1143
+ ) : (
1144
+ <>
1145
+ <span className="mx-1.5 text-muted-foreground/35">·</span>
1146
+ <LinkifiedText className="whitespace-pre-wrap" explicitOnly pretty={false} text={output} />
1147
+ </>
1148
+ )}
811
1149
  </MessagePrimitive.Root>
812
1150
  )
813
1151
  }
814
1152
 
1153
+ const multiline = text.includes('\n')
1154
+
815
1155
  return (
816
1156
  <MessagePrimitive.Root
817
- className="max-w-[min(86%,44rem)] self-center px-2 py-0.5 text-center text-[0.6875rem] leading-5 text-muted-foreground/55"
1157
+ className={cn(
1158
+ 'w-[60%] max-w-[44rem] self-center px-2 py-0.5 text-[0.6875rem] leading-5 text-muted-foreground/55',
1159
+ multiline ? 'text-left' : 'text-center'
1160
+ )}
818
1161
  data-role="system"
819
1162
  data-slot="aui_system-message-root"
820
1163
  >
821
- <span className="whitespace-pre-wrap">{text}</span>
1164
+ <LinkifiedText className="whitespace-pre-wrap" explicitOnly pretty={false} text={text} />
822
1165
  </MessagePrimitive.Root>
823
1166
  )
824
1167
  }
@@ -830,6 +1173,8 @@ interface UserEditComposerProps {
830
1173
  }
831
1174
 
832
1175
  const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }) => {
1176
+ const { t } = useI18n()
1177
+ const copy = t.assistant.thread
833
1178
  const aui = useAui()
834
1179
  const draft = useAuiState(s => s.composer.text)
835
1180
  const rootRef = useRef<HTMLDivElement | null>(null)
@@ -840,14 +1185,24 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
840
1185
  const [trigger, setTrigger] = useState<TriggerState | null>(null)
841
1186
  const [triggerActive, setTriggerActive] = useState(0)
842
1187
  const [triggerItems, setTriggerItems] = useState<readonly Unstable_TriggerItem[]>([])
1188
+ // See index.tsx: set in keydown when the open popover consumes a nav/control
1189
+ // key so the matching keyup skips refreshTrigger (timing-immune vs reading
1190
+ // `trigger`, which keyup sees as already-null after Escape).
1191
+ const triggerKeyConsumedRef = useRef(false)
843
1192
  const [triggerPlacement, setTriggerPlacement] = useState<'bottom' | 'top'>('top')
844
1193
  const [focusRequestId, setFocusRequestId] = useState(0)
845
1194
  const [submitting, setSubmitting] = useState(false)
1195
+ // True while OS-drop files are being staged/uploaded into the session. Blocks
1196
+ // submit and shows a spinner so confirming the edit can't race the async
1197
+ // upload and drop the gateway-side ref before it lands in the draft.
1198
+ const [staging, setStaging] = useState(false)
846
1199
  const expanded = draft.includes('\n')
847
1200
  const canSubmit = draft.trim().length > 0
848
1201
  const at = useAtCompletions({ cwd, gateway, sessionId })
849
1202
  const slash = useSlashCompletions({ gateway })
850
1203
 
1204
+ useEffect(() => () => notifyThreadEditClose(), [])
1205
+
851
1206
  const focusEditor = useCallback(() => {
852
1207
  const editor = editorRef.current
853
1208
 
@@ -964,8 +1319,15 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
964
1319
  }
965
1320
 
966
1321
  setTrigger(detected)
967
- setTriggerActive(0)
968
- }, [])
1322
+
1323
+ // Only reset the highlight when the trigger actually changed (opened, or
1324
+ // the query/kind differs). Re-detecting the *same* trigger — e.g. on a
1325
+ // caret move (mouseup) or a stray refresh — must preserve the user's
1326
+ // current selection instead of snapping back to the first item.
1327
+ if (detected?.kind !== trigger?.kind || detected?.query !== trigger?.query) {
1328
+ setTriggerActive(0)
1329
+ }
1330
+ }, [trigger])
969
1331
 
970
1332
  const closeTrigger = useCallback(() => {
971
1333
  setTrigger(null)
@@ -1052,18 +1414,14 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
1052
1414
  [aui, closeTrigger, refreshTrigger, requestEditFocus, trigger]
1053
1415
  )
1054
1416
 
1055
- const insertDroppedRefs = useCallback(
1056
- (candidates: ReturnType<typeof extractDroppedFiles>) => {
1417
+ const insertRefStrings = useCallback(
1418
+ (refs: InlineRefInput[]) => {
1057
1419
  const editor = editorRef.current
1058
1420
 
1059
- if (!editor) {
1421
+ if (!editor || refs.length === 0) {
1060
1422
  return false
1061
1423
  }
1062
1424
 
1063
- const refs = candidates
1064
- .map(candidate => droppedFileInlineRef(candidate, cwd))
1065
- .filter((ref): ref is string => Boolean(ref))
1066
-
1067
1425
  const nextDraft = insertInlineRefsIntoEditor(editor, refs)
1068
1426
 
1069
1427
  if (nextDraft === null) {
@@ -1076,7 +1434,63 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
1076
1434
 
1077
1435
  return true
1078
1436
  },
1079
- [aui, cwd, requestEditFocus]
1437
+ [aui, requestEditFocus]
1438
+ )
1439
+
1440
+ const insertDroppedRefs = useCallback(
1441
+ (candidates: ReturnType<typeof extractDroppedFiles>) => insertRefStrings(droppedFileInlineRefs(candidates, cwd)),
1442
+ [cwd, insertRefStrings]
1443
+ )
1444
+
1445
+ // OS/Finder drops carry an absolute path on THIS machine — the gateway can't
1446
+ // read it in remote mode, and an image needs its bytes uploaded for vision.
1447
+ // Stage each through the same file.attach/image.attach_bytes pipeline the main
1448
+ // composer uses, then insert the *gateway-side* ref the agent can resolve —
1449
+ // never the raw local path (the MahmoudR remote-attach bug, which the main
1450
+ // composer fixes but this edit composer used to reproduce).
1451
+ const uploadOsDropRefs = useCallback(
1452
+ async (osDrops: ReturnType<typeof extractDroppedFiles>): Promise<InlineRefInput[]> => {
1453
+ if (!gateway || !sessionId) {
1454
+ // No session to stage into — best-effort inline refs (matches old path).
1455
+ return droppedFileInlineRefs(osDrops, cwd)
1456
+ }
1457
+
1458
+ const remote = $connection.get()?.mode === 'remote'
1459
+
1460
+ const requestGateway = <T,>(method: string, params?: Record<string, unknown>) =>
1461
+ gateway.request<T>(method, params)
1462
+
1463
+ const refs: InlineRefInput[] = []
1464
+
1465
+ for (const candidate of osDrops) {
1466
+ const path = candidate.path || ''
1467
+
1468
+ if (!path) {
1469
+ continue
1470
+ }
1471
+
1472
+ const kind: ComposerAttachment['kind'] =
1473
+ candidate.file?.type.startsWith('image/') || isImagePath(candidate.file?.name || path) ? 'image' : 'file'
1474
+
1475
+ try {
1476
+ const uploaded = await uploadComposerAttachment(
1477
+ { detail: path, id: attachmentId(kind, path), kind, label: pathLabel(path), path },
1478
+ { remote, requestGateway, sessionId }
1479
+ )
1480
+
1481
+ const ref = attachmentDisplayText(uploaded)
1482
+
1483
+ if (ref) {
1484
+ refs.push(ref)
1485
+ }
1486
+ } catch (err) {
1487
+ notifyError(err, t.desktop.dropFiles)
1488
+ }
1489
+ }
1490
+
1491
+ return refs
1492
+ },
1493
+ [cwd, gateway, sessionId, t.desktop.dropFiles]
1080
1494
  )
1081
1495
 
1082
1496
  const resetDragState = useCallback(() => {
@@ -1130,9 +1544,25 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
1130
1544
  event.stopPropagation()
1131
1545
  resetDragState()
1132
1546
 
1133
- if (insertDroppedRefs(candidates)) {
1547
+ // In-app drags (project tree / gutter) are workspace-relative paths that
1548
+ // resolve on the gateway as-is, so they stay inline refs. OS drops need to
1549
+ // be staged + uploaded first, then their gateway-side ref is inserted.
1550
+ const { inAppRefs, osDrops } = partitionDroppedFiles(candidates)
1551
+
1552
+ if (insertDroppedRefs(inAppRefs)) {
1134
1553
  triggerHaptic('selection')
1135
1554
  }
1555
+
1556
+ if (osDrops.length) {
1557
+ setStaging(true)
1558
+ void uploadOsDropRefs(osDrops)
1559
+ .then(refs => {
1560
+ if (insertRefStrings(refs)) {
1561
+ triggerHaptic('selection')
1562
+ }
1563
+ })
1564
+ .finally(() => setStaging(false))
1565
+ }
1136
1566
  }
1137
1567
 
1138
1568
  const handleInput = (event: FormEvent<HTMLDivElement>) => {
@@ -1163,7 +1593,7 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
1163
1593
  const submitEdit = (editor: HTMLDivElement) => {
1164
1594
  const nextDraft = syncDraftFromEditor(editor)
1165
1595
 
1166
- if (submitting || !nextDraft.trim()) {
1596
+ if (submitting || staging || !nextDraft.trim()) {
1167
1597
  return
1168
1598
  }
1169
1599
 
@@ -1198,6 +1628,7 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
1198
1628
  if (trigger && triggerItems.length > 0) {
1199
1629
  if (event.key === 'ArrowDown') {
1200
1630
  event.preventDefault()
1631
+ triggerKeyConsumedRef.current = true
1201
1632
  setTriggerActive(idx => (idx + 1) % triggerItems.length)
1202
1633
 
1203
1634
  return
@@ -1205,6 +1636,7 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
1205
1636
 
1206
1637
  if (event.key === 'ArrowUp') {
1207
1638
  event.preventDefault()
1639
+ triggerKeyConsumedRef.current = true
1208
1640
  setTriggerActive(idx => (idx - 1 + triggerItems.length) % triggerItems.length)
1209
1641
 
1210
1642
  return
@@ -1212,6 +1644,7 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
1212
1644
 
1213
1645
  if (event.key === 'Enter' || event.key === 'Tab') {
1214
1646
  event.preventDefault()
1647
+ triggerKeyConsumedRef.current = true
1215
1648
  const item = triggerItems[triggerActive]
1216
1649
 
1217
1650
  if (item) {
@@ -1223,6 +1656,7 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
1223
1656
 
1224
1657
  if (event.key === 'Escape') {
1225
1658
  event.preventDefault()
1659
+ triggerKeyConsumedRef.current = true
1226
1660
  closeTrigger()
1227
1661
 
1228
1662
  return
@@ -1242,6 +1676,22 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
1242
1676
  }
1243
1677
  }
1244
1678
 
1679
+ const handleKeyUp = () => {
1680
+ // If this keyup belongs to a key the open trigger popover already consumed
1681
+ // in keydown (Arrow/Enter/Tab/Escape), skip the refresh. Those keys never
1682
+ // edit text, and for Escape the keydown already closed the menu — a refresh
1683
+ // here would re-detect the still-present `/` and instantly reopen it. We
1684
+ // read a ref set during keydown rather than `trigger`, because by keyup
1685
+ // time React has re-rendered and `trigger` may already be null.
1686
+ if (triggerKeyConsumedRef.current) {
1687
+ triggerKeyConsumedRef.current = false
1688
+
1689
+ return
1690
+ }
1691
+
1692
+ window.setTimeout(refreshTrigger, 0)
1693
+ }
1694
+
1245
1695
  return (
1246
1696
  <ComposerPrimitive.Root className="contents" data-slot="aui_edit-composer-root">
1247
1697
  <StickyHumanMessageContainer>
@@ -1275,16 +1725,17 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
1275
1725
  data-expanded={expanded ? 'true' : undefined}
1276
1726
  >
1277
1727
  <div
1278
- aria-label="Edit message"
1279
- autoFocus
1728
+ aria-label={copy.editMessage}
1729
+ autoCapitalize="off"
1730
+ autoCorrect="off"
1280
1731
  className={cn(
1281
- 'ui-prompt-input-editor__input max-h-48 w-full resize-none bg-transparent p-0 pr-7 text-[length:var(--conversation-text-font-size)] leading-(--dt-line-height) text-foreground/95 outline-none',
1732
+ 'ui-prompt-input-editor__input max-h-48 w-full resize-none bg-transparent p-0 pr-7 text-[length:var(--conversation-text-font-size)] text-foreground/95 outline-none',
1282
1733
  'empty:before:content-[attr(data-placeholder)] empty:before:text-muted-foreground/60',
1283
1734
  '**:data-ref-text:cursor-default',
1284
1735
  expanded ? 'min-h-16' : 'min-h-[1.25rem]'
1285
1736
  )}
1286
1737
  contentEditable
1287
- data-placeholder="Edit message"
1738
+ data-placeholder={copy.editMessage}
1288
1739
  data-slot={RICH_INPUT_SLOT}
1289
1740
  onBlur={() => window.setTimeout(closeTrigger, 80)}
1290
1741
  onDragOver={handleDragOver}
@@ -1292,18 +1743,44 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
1292
1743
  onFocus={() => markActiveComposer('edit')}
1293
1744
  onInput={handleInput}
1294
1745
  onKeyDown={handleKeyDown}
1295
- onKeyUp={() => window.setTimeout(refreshTrigger, 0)}
1746
+ onKeyUp={handleKeyUp}
1296
1747
  onMouseUp={refreshTrigger}
1297
1748
  onPaste={handlePaste}
1298
1749
  ref={editorRef}
1299
1750
  role="textbox"
1751
+ spellCheck={false}
1300
1752
  suppressContentEditableWarning
1301
1753
  />
1302
- <ComposerPrimitive.Input className="sr-only" tabIndex={-1} unstable_focusOnScrollToBottom={false} />
1754
+ <ComposerPrimitive.Input
1755
+ asChild
1756
+ className="sr-only"
1757
+ submitMode="ctrlEnter"
1758
+ tabIndex={-1}
1759
+ unstable_focusOnScrollToBottom={false}
1760
+ >
1761
+ <textarea
1762
+ aria-hidden
1763
+ autoCapitalize="off"
1764
+ autoComplete="off"
1765
+ autoCorrect="off"
1766
+ className="sr-only"
1767
+ spellCheck={false}
1768
+ tabIndex={-1}
1769
+ />
1770
+ </ComposerPrimitive.Input>
1771
+ {staging && (
1772
+ <span
1773
+ className="pointer-events-none absolute bottom-2 left-2 inline-flex items-center gap-1 rounded-full bg-background/80 px-1.5 py-0.5 text-[0.62rem] text-muted-foreground backdrop-blur-[1px]"
1774
+ data-slot="aui_edit-staging"
1775
+ >
1776
+ <Loader2Icon className="size-3 animate-spin" />
1777
+ {copy.attachingFile}
1778
+ </span>
1779
+ )}
1303
1780
  <button
1304
- aria-label="Send edited message"
1781
+ aria-label={copy.sendEdited}
1305
1782
  className={cn('absolute right-2 bottom-2 size-5', USER_ACTION_ICON_BUTTON_CLASS)}
1306
- disabled={!canSubmit || submitting}
1783
+ disabled={!canSubmit || submitting || staging}
1307
1784
  onClick={() => {
1308
1785
  const editor = editorRef.current
1309
1786
 
@@ -1311,7 +1788,7 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
1311
1788
  submitEdit(editor)
1312
1789
  }
1313
1790
  }}
1314
- title="Send edited message"
1791
+ title={copy.sendEdited}
1315
1792
  type="button"
1316
1793
  >
1317
1794
  {submitting ? StopGlyph : <Codicon name="arrow-up" size={USER_ACTION_ICON_SIZE} />}