@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
@@ -0,0 +1,536 @@
1
+ """User-authorization methods for ``GatewayRunner``.
2
+
3
+ Extracted from ``gateway/run.py`` as part of the god-file decomposition campaign
4
+ (``~/.hermes/plans/god-file-decomposition.md``, Phase 3 mechanical mixin lifts).
5
+ This mixin holds the inbound-message authorization cluster: whether a user/chat
6
+ is allowed to talk to the agent, the per-adapter DM policy, and the
7
+ unauthorized-DM behavior.
8
+
9
+ Behavior-neutral: every method is lifted verbatim from ``GatewayRunner``.
10
+ ``self.*`` calls resolve unchanged via the MRO. Neutral dependencies import at
11
+ module top; the module-level ``logger`` is imported lazily inside the one method
12
+ that uses it (``from gateway.run import logger`` resolves at call time, when
13
+ ``gateway.run`` is fully loaded) so this module never imports ``gateway.run`` at
14
+ import time -> no import cycle. The lazy import preserves the exact logger name
15
+ (``"gateway.run"``) so log records are unchanged.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import os
21
+ from typing import Optional
22
+
23
+ from gateway.config import Platform
24
+ from gateway.session import SessionSource
25
+ from gateway.whatsapp_identity import (
26
+ expand_whatsapp_aliases as _expand_whatsapp_auth_aliases,
27
+ normalize_whatsapp_identifier as _normalize_whatsapp_identifier,
28
+ )
29
+
30
+
31
+ class GatewayAuthorizationMixin:
32
+ """User/chat authorization methods for ``GatewayRunner``."""
33
+
34
+ def _adapter_enforces_own_access_policy(self, platform: Optional[Platform]) -> bool:
35
+ """Whether the adapter for *platform* gates access at intake itself.
36
+
37
+ Mirrors ``BasePlatformAdapter.enforces_own_access_policy``. Adapters
38
+ such as WeCom, Weixin, Yuanbao, QQBot, and WhatsApp evaluate their
39
+ documented ``dm_policy`` / ``group_policy`` / ``allow_from`` config before a
40
+ message is dispatched to the gateway. The flag alone is NOT "already
41
+ authorized": these adapters default to ``open``, which forwards every
42
+ sender, so ``_is_user_authorized`` only trusts the adapter when its
43
+ effective policy for the chat type is an actual ``allowlist`` restriction
44
+ (see that method). Defaults to ``False`` when the adapter is unknown or
45
+ doesn't expose the flag.
46
+ """
47
+ if not platform:
48
+ return False
49
+ # Some test helpers build a bare GatewayRunner via object.__new__ and
50
+ # never set ``adapters``; treat a missing/empty map as "no adapter"
51
+ # rather than raising (see pitfalls.md #17).
52
+ adapters = getattr(self, "adapters", None)
53
+ if not adapters:
54
+ return False
55
+ adapter = adapters.get(platform)
56
+ if adapter is None:
57
+ return False
58
+ return bool(getattr(adapter, "enforces_own_access_policy", False))
59
+
60
+ def _adapter_dm_policy(self, platform: Optional[Platform]) -> str:
61
+ """Best-effort read of an own-policy adapter's effective DM policy.
62
+
63
+ Returns the lowercased ``dm_policy`` (``"open"`` / ``"allowlist"`` /
64
+ ``"disabled"`` / ``"pairing"``) for *platform*, or ``""`` when unknown.
65
+ Prefers the live adapter's resolved ``_dm_policy`` — which already folds
66
+ in both ``config.extra`` and the ``<PLATFORM>_DM_POLICY`` env var (the
67
+ env var is not always bridged back into ``config.extra``) — and falls
68
+ back to ``config.extra`` for bare runners built without a live adapter.
69
+
70
+ Used by ``_is_user_authorized`` to decide whether an own-policy adapter
71
+ actually restricted DM senders to a configured allowlist (trustworthy)
72
+ or merely forwarded everyone under ``dm_policy: open`` / for a pairing
73
+ handshake (not authorization). "Reached the gateway" only carries an
74
+ authorization signal in the ``allowlist`` case.
75
+ """
76
+ if not platform:
77
+ return ""
78
+ adapters = getattr(self, "adapters", None) or {}
79
+ adapter = adapters.get(platform)
80
+ policy = getattr(adapter, "_dm_policy", None) if adapter is not None else None
81
+ if policy is None:
82
+ config = getattr(self, "config", None)
83
+ platform_cfg = (
84
+ config.platforms.get(platform)
85
+ if config is not None and hasattr(config, "platforms")
86
+ else None
87
+ )
88
+ extra = getattr(platform_cfg, "extra", None) if platform_cfg else None
89
+ if isinstance(extra, dict):
90
+ policy = extra.get("dm_policy")
91
+ return str(policy or "").strip().lower()
92
+
93
+ def _adapter_group_policy(self, platform: Optional[Platform]) -> str:
94
+ """Best-effort read of an own-policy adapter's effective group policy.
95
+
96
+ Mirror of ``_adapter_dm_policy`` for group / forum / channel traffic:
97
+ returns the lowercased ``group_policy`` (``"open"`` / ``"allowlist"`` /
98
+ ``"disabled"``) for *platform*, or ``""`` when unknown. Prefers the live
99
+ adapter's resolved ``_group_policy`` and falls back to ``config.extra``
100
+ for bare runners built without a live adapter.
101
+
102
+ Used by ``_is_user_authorized`` to decide whether an own-policy adapter
103
+ restricted group senders to a configured allowlist (trustworthy) or
104
+ forwarded the whole channel under ``group_policy: open`` (not
105
+ authorization).
106
+ """
107
+ if not platform:
108
+ return ""
109
+ adapters = getattr(self, "adapters", None) or {}
110
+ adapter = adapters.get(platform)
111
+ policy = getattr(adapter, "_group_policy", None) if adapter is not None else None
112
+ if policy is None:
113
+ config = getattr(self, "config", None)
114
+ platform_cfg = (
115
+ config.platforms.get(platform)
116
+ if config is not None and hasattr(config, "platforms")
117
+ else None
118
+ )
119
+ extra = getattr(platform_cfg, "extra", None) if platform_cfg else None
120
+ if isinstance(extra, dict):
121
+ policy = extra.get("group_policy")
122
+ return str(policy or "").strip().lower()
123
+
124
+ def _adapter_group_has_sender_allowlist(
125
+ self,
126
+ platform: Optional[Platform],
127
+ chat_id: Optional[str],
128
+ ) -> bool:
129
+ """Whether a per-group sender allowlist gated this group message.
130
+
131
+ WeCom supports ``groups.<group_id>.allow_from`` on top of the top-level
132
+ ``group_policy``. A group may be open at the chat level while still
133
+ restricting which senders inside that group can invoke Hermes. If such a
134
+ message reached the gateway, the adapter already checked that sender
135
+ allowlist, so it is a trustworthy intake decision rather than the
136
+ fail-open ``group_policy: open`` case.
137
+ """
138
+ if not platform or not chat_id:
139
+ return False
140
+ adapters = getattr(self, "adapters", None) or {}
141
+ adapter = adapters.get(platform)
142
+ groups = getattr(adapter, "_groups", None) if adapter is not None else None
143
+ if groups is None:
144
+ config = getattr(self, "config", None)
145
+ platform_cfg = (
146
+ config.platforms.get(platform)
147
+ if config is not None and hasattr(config, "platforms")
148
+ else None
149
+ )
150
+ extra = getattr(platform_cfg, "extra", None) if platform_cfg else None
151
+ if isinstance(extra, dict):
152
+ groups = extra.get("groups")
153
+ if not isinstance(groups, dict):
154
+ return False
155
+
156
+ chat_id_str = str(chat_id)
157
+ group_cfg = groups.get(chat_id_str)
158
+ if not isinstance(group_cfg, dict):
159
+ lowered = chat_id_str.lower()
160
+ for key, value in groups.items():
161
+ if isinstance(key, str) and key.lower() == lowered and isinstance(value, dict):
162
+ group_cfg = value
163
+ break
164
+ if not isinstance(group_cfg, dict):
165
+ group_cfg = groups.get("*")
166
+ if not isinstance(group_cfg, dict):
167
+ return False
168
+
169
+ sender_allow = group_cfg.get("allow_from") or group_cfg.get("allowFrom")
170
+ if isinstance(sender_allow, str):
171
+ return bool(sender_allow.strip())
172
+ if isinstance(sender_allow, (list, tuple, set)):
173
+ return any(str(item).strip() for item in sender_allow)
174
+ return False
175
+
176
+ def _is_user_authorized(self, source: SessionSource) -> bool:
177
+ """
178
+ Check if a user is authorized to use the bot.
179
+
180
+ Checks in order:
181
+ 1. Per-platform allow-all flag (e.g., DISCORD_ALLOW_ALL_USERS=true)
182
+ 2. Environment variable allowlists (TELEGRAM_ALLOWED_USERS, etc.)
183
+ 3. DM pairing approved list
184
+ 4. Global allow-all (GATEWAY_ALLOW_ALL_USERS=true)
185
+ 5. Default: deny
186
+ """
187
+ from gateway.run import logger
188
+ # Home Assistant events are system-generated (state changes), not
189
+ # user-initiated messages. The HASS_TOKEN already authenticates the
190
+ # connection, so HA events are always authorized.
191
+ # Webhook events are authenticated via HMAC signature validation in
192
+ # the adapter itself — no user allowlist applies.
193
+ if source.platform in {Platform.HOMEASSISTANT, Platform.WEBHOOK}:
194
+ return True
195
+
196
+ user_id = source.user_id
197
+
198
+ # Telegram (and similar) authorize entire group/forum/channel chats
199
+ # by chat ID via TELEGRAM_GROUP_ALLOWED_CHATS / QQ_GROUP_ALLOWED_USERS.
200
+ # That allowlist is chat-scoped, so it must work even when
201
+ # source.user_id is None — Telegram emits anonymous-admin posts,
202
+ # sender_chat traffic, and channel broadcasts with no `from_user`,
203
+ # and an operator who explicitly listed the chat expects those to
204
+ # be honored. Run this check before the no-user-id guard below so
205
+ # documented behavior matches reality
206
+ # (website/docs/reference/environment-variables.md,
207
+ # website/docs/user-guide/messaging/telegram.md).
208
+ if source.chat_type in {"group", "forum", "channel"} and source.chat_id:
209
+ chat_allowlist_env = {
210
+ Platform.TELEGRAM: "TELEGRAM_GROUP_ALLOWED_CHATS",
211
+ Platform.QQBOT: "QQ_GROUP_ALLOWED_USERS",
212
+ }.get(source.platform, "")
213
+ if chat_allowlist_env:
214
+ raw_chat_allowlist = os.getenv(chat_allowlist_env, "").strip()
215
+ if raw_chat_allowlist:
216
+ allowed_group_ids = {
217
+ cid.strip()
218
+ for cid in raw_chat_allowlist.split(",")
219
+ if cid.strip()
220
+ }
221
+ if "*" in allowed_group_ids or source.chat_id in allowed_group_ids:
222
+ return True
223
+
224
+ if not user_id:
225
+ return False
226
+
227
+ platform_env_map = {
228
+ Platform.TELEGRAM: "TELEGRAM_ALLOWED_USERS",
229
+ Platform.DISCORD: "DISCORD_ALLOWED_USERS",
230
+ Platform.WHATSAPP: "WHATSAPP_ALLOWED_USERS",
231
+ Platform.WHATSAPP_CLOUD: "WHATSAPP_CLOUD_ALLOWED_USERS",
232
+ Platform.SLACK: "SLACK_ALLOWED_USERS",
233
+ Platform.SIGNAL: "SIGNAL_ALLOWED_USERS",
234
+ Platform.EMAIL: "EMAIL_ALLOWED_USERS",
235
+ Platform.SMS: "SMS_ALLOWED_USERS",
236
+ Platform.MATTERMOST: "MATTERMOST_ALLOWED_USERS",
237
+ Platform.MATRIX: "MATRIX_ALLOWED_USERS",
238
+ Platform.DINGTALK: "DINGTALK_ALLOWED_USERS",
239
+ Platform.FEISHU: "FEISHU_ALLOWED_USERS",
240
+ Platform.WECOM: "WECOM_ALLOWED_USERS",
241
+ Platform.WECOM_CALLBACK: "WECOM_CALLBACK_ALLOWED_USERS",
242
+ Platform.WEIXIN: "WEIXIN_ALLOWED_USERS",
243
+ Platform.BLUEBUBBLES: "BLUEBUBBLES_ALLOWED_USERS",
244
+ Platform.QQBOT: "QQ_ALLOWED_USERS",
245
+ Platform.YUANBAO: "YUANBAO_ALLOWED_USERS",
246
+ }
247
+ platform_group_user_env_map = {
248
+ Platform.TELEGRAM: "TELEGRAM_GROUP_ALLOWED_USERS",
249
+ }
250
+ platform_group_chat_env_map = {
251
+ Platform.TELEGRAM: "TELEGRAM_GROUP_ALLOWED_CHATS",
252
+ Platform.QQBOT: "QQ_GROUP_ALLOWED_USERS",
253
+ }
254
+ platform_allow_all_map = {
255
+ Platform.TELEGRAM: "TELEGRAM_ALLOW_ALL_USERS",
256
+ Platform.DISCORD: "DISCORD_ALLOW_ALL_USERS",
257
+ Platform.WHATSAPP: "WHATSAPP_ALLOW_ALL_USERS",
258
+ Platform.WHATSAPP_CLOUD: "WHATSAPP_CLOUD_ALLOW_ALL_USERS",
259
+ Platform.SLACK: "SLACK_ALLOW_ALL_USERS",
260
+ Platform.SIGNAL: "SIGNAL_ALLOW_ALL_USERS",
261
+ Platform.EMAIL: "EMAIL_ALLOW_ALL_USERS",
262
+ Platform.SMS: "SMS_ALLOW_ALL_USERS",
263
+ Platform.MATTERMOST: "MATTERMOST_ALLOW_ALL_USERS",
264
+ Platform.MATRIX: "MATRIX_ALLOW_ALL_USERS",
265
+ Platform.DINGTALK: "DINGTALK_ALLOW_ALL_USERS",
266
+ Platform.FEISHU: "FEISHU_ALLOW_ALL_USERS",
267
+ Platform.WECOM: "WECOM_ALLOW_ALL_USERS",
268
+ Platform.WECOM_CALLBACK: "WECOM_CALLBACK_ALLOW_ALL_USERS",
269
+ Platform.WEIXIN: "WEIXIN_ALLOW_ALL_USERS",
270
+ Platform.BLUEBUBBLES: "BLUEBUBBLES_ALLOW_ALL_USERS",
271
+ Platform.QQBOT: "QQ_ALLOW_ALL_USERS",
272
+ Platform.YUANBAO: "YUANBAO_ALLOW_ALL_USERS",
273
+ }
274
+ # Bots admitted by {PLATFORM}_ALLOW_BOTS bypass the human allowlist (#4466).
275
+ platform_allow_bots_map = {
276
+ Platform.DISCORD: "DISCORD_ALLOW_BOTS",
277
+ Platform.FEISHU: "FEISHU_ALLOW_BOTS",
278
+ }
279
+
280
+ # Plugin platforms: check the registry for auth env var names
281
+ if source.platform not in platform_env_map:
282
+ try:
283
+ from gateway.platform_registry import platform_registry
284
+ entry = platform_registry.get(source.platform.value)
285
+ if entry:
286
+ if entry.allowed_users_env:
287
+ platform_env_map[source.platform] = entry.allowed_users_env
288
+ if entry.allow_all_env:
289
+ platform_allow_all_map[source.platform] = entry.allow_all_env
290
+ except Exception:
291
+ pass
292
+
293
+ # Per-platform allow-all flag (e.g., DISCORD_ALLOW_ALL_USERS=true)
294
+ platform_allow_all_var = platform_allow_all_map.get(source.platform, "")
295
+ if platform_allow_all_var and os.getenv(platform_allow_all_var, "").lower() in {"true", "1", "yes"}:
296
+ return True
297
+
298
+ # Adapter-verified role auth: the Discord adapter already confirmed the
299
+ # user holds a role in DISCORD_ALLOWED_ROLES before dispatching the message.
300
+ # Compare with ``is True`` so the real bool field authorizes while a
301
+ # MagicMock source (test fixtures using ``object.__new__`` runners with
302
+ # mock sources) does not auto-truthy through this gate (see pitfall #13).
303
+ if getattr(source, "role_authorized", False) is True:
304
+ return True
305
+
306
+ if getattr(source, "is_bot", False):
307
+ allow_bots_var = platform_allow_bots_map.get(source.platform)
308
+ if allow_bots_var and os.getenv(allow_bots_var, "none").lower().strip() in {"mentions", "all"}:
309
+ return True
310
+
311
+ # Check pairing store (always checked, regardless of allowlists)
312
+ platform_name = source.platform.value if source.platform else ""
313
+ if self.pairing_store.is_approved(platform_name, user_id):
314
+ return True
315
+
316
+ # Check platform-specific and global allowlists
317
+ platform_allowlist = os.getenv(platform_env_map.get(source.platform, ""), "").strip()
318
+ group_user_allowlist = ""
319
+ group_chat_allowlist = ""
320
+ if source.chat_type in {"group", "forum"}:
321
+ group_user_allowlist = os.getenv(platform_group_user_env_map.get(source.platform, ""), "").strip()
322
+ group_chat_allowlist = os.getenv(platform_group_chat_env_map.get(source.platform, ""), "").strip()
323
+ global_allowlist = os.getenv("GATEWAY_ALLOWED_USERS", "").strip()
324
+
325
+ if not platform_allowlist and not group_user_allowlist and not group_chat_allowlist and not global_allowlist:
326
+ # No env allowlist configured. Adapters that own their own
327
+ # config-driven access policy (dm_policy / group_policy /
328
+ # allow_from / group_allow_from) gate access at intake, so for those
329
+ # platforms we can honor the adapter's decision instead of the
330
+ # env-only default-deny below -- but ONLY when that decision was an
331
+ # actual allowlist restriction.
332
+ #
333
+ # The adapters default dm_policy / group_policy to "open", which
334
+ # forwards EVERY sender. Reading "reached the gateway" as
335
+ # authorization in that case would admit the whole external network
336
+ # with no operator-configured allowlist -- the fail-open SECURITY.md
337
+ # §2.6 forbids ("an allowlist is required for every enabled
338
+ # network-exposed adapter ... code paths that fail open when no
339
+ # allowlist is configured are code bugs"). "disabled" never
340
+ # forwards, and "pairing" forwards unpaired DMs only so the gateway
341
+ # can run its pairing handshake (the pairing-store check above
342
+ # already denied this sender). So trust the adapter only when its
343
+ # effective policy for THIS chat type is "allowlist"; for "open" /
344
+ # "pairing" / anything else, fall through to default-deny, where
345
+ # GATEWAY_ALLOW_ALL_USERS, the per-platform {PLATFORM}_ALLOW_ALL_USERS
346
+ # flag (checked above), and the pairing flow remain the explicit
347
+ # opt-ins to broader access. (#34515 follow-up: trusting "open" was a
348
+ # fail-open.)
349
+ if self._adapter_enforces_own_access_policy(source.platform):
350
+ if source.chat_type in {"group", "forum", "channel"}:
351
+ effective_policy = self._adapter_group_policy(source.platform)
352
+ if self._adapter_group_has_sender_allowlist(
353
+ source.platform,
354
+ source.chat_id,
355
+ ):
356
+ return True
357
+ else:
358
+ effective_policy = self._adapter_dm_policy(source.platform)
359
+ if effective_policy == "allowlist":
360
+ return True
361
+ # No allowlists configured -- check global allow-all flag
362
+ return os.getenv("GATEWAY_ALLOW_ALL_USERS", "").lower() in {"true", "1", "yes"}
363
+
364
+ # Telegram can optionally authorize group traffic by chat ID.
365
+ # Keep this separate from TELEGRAM_GROUP_ALLOWED_USERS, which gates
366
+ # the sender user ID for group/forum messages.
367
+ if group_chat_allowlist and source.chat_type in {"group", "forum"} and source.chat_id:
368
+ allowed_group_ids = {
369
+ chat_id.strip() for chat_id in group_chat_allowlist.split(",") if chat_id.strip()
370
+ }
371
+ if "*" in allowed_group_ids or source.chat_id in allowed_group_ids:
372
+ return True
373
+
374
+ # Backward-compat shim for #15027: prior to PR #17686,
375
+ # TELEGRAM_GROUP_ALLOWED_USERS was (mis)used as a chat-ID allowlist.
376
+ # Values starting with "-" are Telegram chat IDs, not user IDs, so if
377
+ # users still have those in TELEGRAM_GROUP_ALLOWED_USERS we honor them
378
+ # as chat IDs and warn once. The correct var is now
379
+ # TELEGRAM_GROUP_ALLOWED_CHATS.
380
+ if (
381
+ source.platform == Platform.TELEGRAM
382
+ and group_user_allowlist
383
+ and source.chat_type in {"group", "forum"}
384
+ and source.chat_id
385
+ ):
386
+ legacy_chat_ids = {
387
+ v.strip()
388
+ for v in group_user_allowlist.split(",")
389
+ if v.strip().startswith("-")
390
+ }
391
+ if legacy_chat_ids:
392
+ if not getattr(self, "_warned_telegram_group_users_legacy", False):
393
+ logger.warning(
394
+ "TELEGRAM_GROUP_ALLOWED_USERS contains chat-ID-shaped values "
395
+ "(%s). Treating them as chat IDs for backward compatibility. "
396
+ "Move chat IDs to TELEGRAM_GROUP_ALLOWED_CHATS — the _USERS var "
397
+ "is now for sender user IDs.",
398
+ ",".join(sorted(legacy_chat_ids)),
399
+ )
400
+ self._warned_telegram_group_users_legacy = True
401
+ if source.chat_id in legacy_chat_ids:
402
+ return True
403
+
404
+ # Check if user is in any allowlist. In group/forum chats,
405
+ # TELEGRAM_GROUP_ALLOWED_USERS is the scoped allowlist and should not
406
+ # imply DM access; TELEGRAM_ALLOWED_USERS remains the platform-wide
407
+ # allowlist and still works everywhere for backward compatibility.
408
+ allowed_ids = set()
409
+ if platform_allowlist:
410
+ allowed_ids.update(uid.strip() for uid in platform_allowlist.split(",") if uid.strip())
411
+ if group_user_allowlist:
412
+ allowed_ids.update(uid.strip() for uid in group_user_allowlist.split(",") if uid.strip())
413
+ if global_allowlist:
414
+ allowed_ids.update(uid.strip() for uid in global_allowlist.split(",") if uid.strip())
415
+
416
+ # "*" in any allowlist means allow everyone (consistent with
417
+ # SIGNAL_GROUP_ALLOWED_USERS precedent)
418
+ if "*" in allowed_ids:
419
+ return True
420
+
421
+ check_ids = {user_id}
422
+ if "@" in user_id:
423
+ check_ids.add(user_id.split("@")[0])
424
+
425
+ # WhatsApp: resolve phone↔LID aliases from bridge session mapping files
426
+ if source.platform == Platform.WHATSAPP:
427
+ normalized_allowed_ids = set()
428
+ for allowed_id in allowed_ids:
429
+ normalized_allowed_ids.update(_expand_whatsapp_auth_aliases(allowed_id))
430
+ if normalized_allowed_ids:
431
+ allowed_ids = normalized_allowed_ids
432
+
433
+ check_ids.update(_expand_whatsapp_auth_aliases(user_id))
434
+ normalized_user_id = _normalize_whatsapp_identifier(user_id)
435
+ if normalized_user_id:
436
+ check_ids.add(normalized_user_id)
437
+
438
+ # SimpleX: SIMPLEX_ALLOWED_USERS accepts either the numeric contactId
439
+ # or the contact's display name. The adapter sets user_id=contactId for
440
+ # stability across renames, but the SimpleX UI never surfaces the
441
+ # numeric id — operators only see display names, so that's what they
442
+ # naturally put in the env var. Match both so the allowlist works
443
+ # regardless of which form was chosen.
444
+ # Plugin platform: compare by value since Platform.SIMPLEX is not a
445
+ # hardcoded enum member (it's a dynamic plugin platform).
446
+ if (
447
+ source.platform is not None
448
+ and source.platform.value == "simplex"
449
+ and source.user_name
450
+ ):
451
+ check_ids.add(source.user_name)
452
+
453
+ return bool(check_ids & allowed_ids)
454
+
455
+ def _get_unauthorized_dm_behavior(self, platform: Optional[Platform]) -> str:
456
+ """Return how unauthorized DMs should be handled for a platform.
457
+
458
+ Resolution order:
459
+ 1. Explicit per-platform ``unauthorized_dm_behavior`` in config — always wins.
460
+ 2. Explicit global ``unauthorized_dm_behavior`` in config — wins when no per-platform.
461
+ 3. When an allowlist (``PLATFORM_ALLOWED_USERS``,
462
+ ``PLATFORM_GROUP_ALLOWED_USERS`` / ``PLATFORM_GROUP_ALLOWED_CHATS``,
463
+ or ``GATEWAY_ALLOWED_USERS``) is configured, default to ``"ignore"`` —
464
+ the allowlist signals that the owner has deliberately restricted
465
+ access; spamming unknown contacts with pairing codes is both noisy
466
+ and a potential info-leak. (#9337)
467
+ 4. No allowlist and no explicit config → ``"pair"`` (open-gateway default).
468
+ """
469
+ config = getattr(self, "config", None)
470
+
471
+ # Check for an explicit per-platform override first.
472
+ if config and hasattr(config, "get_unauthorized_dm_behavior") and platform:
473
+ platform_cfg = config.platforms.get(platform) if hasattr(config, "platforms") else None
474
+ if platform_cfg and "unauthorized_dm_behavior" in getattr(platform_cfg, "extra", {}):
475
+ # Operator explicitly configured behavior for this platform — respect it.
476
+ return config.get_unauthorized_dm_behavior(platform)
477
+
478
+ # Check for an explicit global config override.
479
+ if config and hasattr(config, "unauthorized_dm_behavior"):
480
+ if config.unauthorized_dm_behavior != "pair": # non-default → explicit override
481
+ return config.unauthorized_dm_behavior
482
+
483
+ # Config-driven dm_policy (WeCom / Weixin / Yuanbao / QQBot). An
484
+ # allowlist or disabled DM policy means the operator restricted access,
485
+ # so unauthorized DMs should be dropped silently rather than answered
486
+ # with a pairing code. An explicit pairing policy opts back into codes.
487
+ if platform and config and hasattr(config, "platforms"):
488
+ platform_cfg = config.platforms.get(platform)
489
+ extra = getattr(platform_cfg, "extra", None) if platform_cfg else None
490
+ if isinstance(extra, dict):
491
+ dm_policy = str(extra.get("dm_policy") or "").strip().lower()
492
+ if dm_policy == "pairing":
493
+ return "pair"
494
+ if dm_policy in {"allowlist", "disabled"}:
495
+ return "ignore"
496
+
497
+ # No explicit override. Fall back to allowlist-aware default:
498
+ # if any allowlist is configured for this platform, silently drop
499
+ # unauthorized messages instead of sending pairing codes.
500
+ if platform:
501
+ platform_env_map = {
502
+ Platform.TELEGRAM: "TELEGRAM_ALLOWED_USERS",
503
+ Platform.DISCORD: "DISCORD_ALLOWED_USERS",
504
+ Platform.WHATSAPP: "WHATSAPP_ALLOWED_USERS",
505
+ Platform.WHATSAPP_CLOUD: "WHATSAPP_CLOUD_ALLOWED_USERS",
506
+ Platform.SLACK: "SLACK_ALLOWED_USERS",
507
+ Platform.SIGNAL: "SIGNAL_ALLOWED_USERS",
508
+ Platform.EMAIL: "EMAIL_ALLOWED_USERS",
509
+ Platform.SMS: "SMS_ALLOWED_USERS",
510
+ Platform.MATTERMOST: "MATTERMOST_ALLOWED_USERS",
511
+ Platform.MATRIX: "MATRIX_ALLOWED_USERS",
512
+ Platform.DINGTALK: "DINGTALK_ALLOWED_USERS",
513
+ Platform.FEISHU: "FEISHU_ALLOWED_USERS",
514
+ Platform.WECOM: "WECOM_ALLOWED_USERS",
515
+ Platform.WECOM_CALLBACK: "WECOM_CALLBACK_ALLOWED_USERS",
516
+ Platform.WEIXIN: "WEIXIN_ALLOWED_USERS",
517
+ Platform.BLUEBUBBLES: "BLUEBUBBLES_ALLOWED_USERS",
518
+ Platform.QQBOT: "QQ_ALLOWED_USERS",
519
+ }
520
+ platform_group_env_map = {
521
+ Platform.TELEGRAM: (
522
+ "TELEGRAM_GROUP_ALLOWED_USERS",
523
+ "TELEGRAM_GROUP_ALLOWED_CHATS",
524
+ ),
525
+ Platform.QQBOT: ("QQ_GROUP_ALLOWED_USERS",),
526
+ }
527
+ if os.getenv(platform_env_map.get(platform, ""), "").strip():
528
+ return "ignore"
529
+ for env_key in platform_group_env_map.get(platform, ()):
530
+ if os.getenv(env_key, "").strip():
531
+ return "ignore"
532
+
533
+ if os.getenv("GATEWAY_ALLOWED_USERS", "").strip():
534
+ return "ignore"
535
+
536
+ return "pair"
@@ -17,6 +17,57 @@ from utils import atomic_json_write
17
17
  logger = logging.getLogger(__name__)
18
18
 
19
19
  DIRECTORY_PATH = get_hermes_home() / "channel_directory.json"
20
+ # User-maintained friendly-name overlay. The directory is fully regenerated
21
+ # from live adapters + session data on a timer, so hand-edits to
22
+ # channel_directory.json don't survive. Aliases declared here are re-applied
23
+ # on every build AND every load, giving durable human-friendly names (and
24
+ # letting you pre-name a chat before it has produced any traffic).
25
+ # Format: {"<platform>": {"<chat_id>": "<friendly name>", ...}, ...}
26
+ CHANNEL_ALIASES_PATH = get_hermes_home() / "channel_aliases.json"
27
+
28
+
29
+ def _load_channel_aliases() -> Dict[str, Dict[str, str]]:
30
+ if not CHANNEL_ALIASES_PATH.exists():
31
+ return {}
32
+ try:
33
+ with open(CHANNEL_ALIASES_PATH, encoding="utf-8") as f:
34
+ data = json.load(f)
35
+ return data if isinstance(data, dict) else {}
36
+ except Exception:
37
+ return {}
38
+
39
+
40
+ def _apply_channel_aliases(platforms: Dict[str, Any]) -> None:
41
+ """Overlay friendly names onto directory entries by chat_id.
42
+
43
+ Renames matching entries in place; injects a placeholder entry for an
44
+ aliased id that hasn't been discovered yet (so a freshly-created group is
45
+ addressable by name before its first message). Mutates *platforms*.
46
+ """
47
+ aliases = _load_channel_aliases()
48
+ for plat_name, id_map in aliases.items():
49
+ if not isinstance(id_map, dict):
50
+ continue
51
+ entries = platforms.setdefault(plat_name, [])
52
+ if not isinstance(entries, list):
53
+ continue
54
+ for chat_id, friendly in id_map.items():
55
+ if not isinstance(friendly, str) or not friendly.strip():
56
+ continue
57
+ chat_id = str(chat_id)
58
+ friendly = friendly.strip()
59
+ matched = False
60
+ for e in entries:
61
+ if isinstance(e, dict) and e.get("id") == chat_id:
62
+ e["name"] = friendly
63
+ matched = True
64
+ if not matched:
65
+ entries.append({
66
+ "id": chat_id,
67
+ "name": friendly,
68
+ "type": "group" if str(chat_id).endswith("@g.us") else "dm",
69
+ "thread_id": None,
70
+ })
20
71
 
21
72
 
22
73
  def _normalize_channel_query(value: str) -> str:
@@ -96,6 +147,9 @@ async def build_channel_directory(adapters: Dict[Any, Any]) -> Dict[str, Any]:
96
147
  except Exception:
97
148
  pass
98
149
 
150
+ # Overlay user-maintained friendly names before persisting.
151
+ _apply_channel_aliases(platforms)
152
+
99
153
  directory = {
100
154
  "updated_at": datetime.now().isoformat(),
101
155
  "platforms": platforms,
@@ -247,12 +301,20 @@ def _build_from_sessions(platform_name: str) -> List[Dict[str, str]]:
247
301
  def load_directory() -> Dict[str, Any]:
248
302
  """Load the cached channel directory from disk."""
249
303
  if not DIRECTORY_PATH.exists():
250
- return {"updated_at": None, "platforms": {}}
304
+ base = {"updated_at": None, "platforms": {}}
305
+ _apply_channel_aliases(base["platforms"])
306
+ return base
251
307
  try:
252
308
  with open(DIRECTORY_PATH, encoding="utf-8") as f:
253
- return json.load(f)
309
+ data = json.load(f)
310
+ # Re-apply aliases on read so friendly names take effect immediately,
311
+ # even between timed rebuilds and for brand-new alias entries.
312
+ _apply_channel_aliases(data.setdefault("platforms", {}))
313
+ return data
254
314
  except Exception:
255
- return {"updated_at": None, "platforms": {}}
315
+ base = {"updated_at": None, "platforms": {}}
316
+ _apply_channel_aliases(base["platforms"])
317
+ return base
256
318
 
257
319
 
258
320
  def lookup_channel_type(platform_name: str, chat_id: str) -> Optional[str]: