@clawpump/claw-agent 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1212) hide show
  1. package/agent/.dockerignore +67 -0
  2. package/agent/.envrc +1 -1
  3. package/agent/.gitattributes +8 -0
  4. package/agent/AGENTS.md +216 -4
  5. package/agent/CONTRIBUTING.md +46 -8
  6. package/agent/Dockerfile +78 -35
  7. package/agent/MANIFEST.in +2 -0
  8. package/agent/README.md +12 -5
  9. package/agent/README.ur-pk.md +261 -0
  10. package/agent/README.zh-CN.md +11 -8
  11. package/agent/SECURITY.md +5 -4
  12. package/agent/acp_adapter/provenance.py +127 -0
  13. package/agent/acp_adapter/server.py +112 -5
  14. package/agent/acp_adapter/session.py +1 -6
  15. package/agent/acp_registry/agent.json +2 -2
  16. package/agent/agent/account_usage.py +313 -1
  17. package/agent/agent/agent_init.py +140 -37
  18. package/agent/agent/agent_runtime_helpers.py +342 -83
  19. package/agent/agent/anthropic_adapter.py +320 -33
  20. package/agent/agent/auxiliary_client.py +525 -105
  21. package/agent/agent/background_review.py +157 -19
  22. package/agent/agent/bedrock_adapter.py +71 -6
  23. package/agent/agent/billing_view.py +295 -0
  24. package/agent/agent/chat_completion_helpers.py +229 -4
  25. package/agent/agent/codex_responses_adapter.py +86 -10
  26. package/agent/agent/codex_runtime.py +153 -1
  27. package/agent/agent/coding_context.py +738 -0
  28. package/agent/agent/context_compressor.py +392 -44
  29. package/agent/agent/context_references.py +34 -1
  30. package/agent/agent/conversation_compression.py +159 -22
  31. package/agent/agent/conversation_loop.py +643 -908
  32. package/agent/agent/copilot_acp_client.py +4 -11
  33. package/agent/agent/credential_pool.py +5 -3
  34. package/agent/agent/credits_tracker.py +794 -0
  35. package/agent/agent/curator.py +91 -18
  36. package/agent/agent/curator_backup.py +26 -10
  37. package/agent/agent/display.py +42 -1
  38. package/agent/agent/error_classifier.py +52 -3
  39. package/agent/agent/errors.py +3 -0
  40. package/agent/agent/file_safety.py +0 -17
  41. package/agent/agent/gemini_native_adapter.py +31 -1
  42. package/agent/agent/i18n.py +48 -4
  43. package/agent/agent/image_gen_provider.py +74 -5
  44. package/agent/agent/image_routing.py +29 -0
  45. package/agent/agent/insights.py +8 -17
  46. package/agent/agent/lsp/install.py +3 -0
  47. package/agent/agent/memory_manager.py +326 -31
  48. package/agent/agent/message_content.py +50 -0
  49. package/agent/agent/model_metadata.py +214 -3
  50. package/agent/agent/moonshot_schema.py +8 -1
  51. package/agent/agent/onboarding.py +60 -0
  52. package/agent/agent/prompt_builder.py +327 -37
  53. package/agent/agent/redact.py +1 -0
  54. package/agent/agent/runtime_cwd.py +34 -5
  55. package/agent/agent/secret_scope.py +205 -0
  56. package/agent/agent/secret_sources/bitwarden.py +34 -2
  57. package/agent/agent/skill_commands.py +90 -1
  58. package/agent/agent/skill_preprocessing.py +1 -0
  59. package/agent/agent/skill_utils.py +209 -36
  60. package/agent/agent/ssl_guard.py +94 -0
  61. package/agent/agent/system_prompt.py +133 -5
  62. package/agent/agent/tool_executor.py +496 -70
  63. package/agent/agent/transports/anthropic.py +83 -21
  64. package/agent/agent/transports/chat_completions.py +94 -5
  65. package/agent/agent/transports/codex.py +67 -2
  66. package/agent/agent/transports/codex_app_server.py +1 -0
  67. package/agent/agent/transports/codex_app_server_session.py +30 -0
  68. package/agent/agent/transports/types.py +12 -0
  69. package/agent/agent/turn_context.py +408 -0
  70. package/agent/agent/turn_finalizer.py +428 -0
  71. package/agent/agent/turn_retry_state.py +68 -0
  72. package/agent/agent/usage_pricing.py +3 -0
  73. package/agent/apps/bootstrap-installer/package.json +6 -5
  74. package/agent/apps/bootstrap-installer/src/routes/failure.tsx +12 -5
  75. package/agent/apps/bootstrap-installer/src/routes/progress.tsx +1 -3
  76. package/agent/apps/bootstrap-installer/src/store.ts +3 -2
  77. package/agent/apps/bootstrap-installer/src-tauri/src/bootstrap.rs +172 -7
  78. package/agent/apps/bootstrap-installer/src-tauri/src/events.rs +14 -1
  79. package/agent/apps/bootstrap-installer/src-tauri/src/paths.rs +29 -0
  80. package/agent/apps/bootstrap-installer/src-tauri/src/powershell.rs +93 -3
  81. package/agent/apps/bootstrap-installer/src-tauri/src/update.rs +695 -39
  82. package/agent/apps/bootstrap-installer/tsconfig.json +3 -4
  83. package/agent/apps/desktop/DESIGN.md +167 -0
  84. package/agent/apps/desktop/README.md +20 -16
  85. package/agent/apps/desktop/assets/icon.icns +0 -0
  86. package/agent/apps/desktop/assets/icon.ico +0 -0
  87. package/agent/apps/desktop/assets/icon.png +0 -0
  88. package/agent/apps/desktop/electron/backend-env.cjs +112 -0
  89. package/agent/apps/desktop/electron/backend-env.test.cjs +111 -0
  90. package/agent/apps/desktop/electron/backend-probes.test.cjs +3 -1
  91. package/agent/apps/desktop/electron/backend-ready.cjs +66 -0
  92. package/agent/apps/desktop/electron/bootstrap-platform.cjs +52 -0
  93. package/agent/apps/desktop/electron/bootstrap-platform.test.cjs +59 -1
  94. package/agent/apps/desktop/electron/bootstrap-runner.cjs +176 -38
  95. package/agent/apps/desktop/electron/bootstrap-runner.test.cjs +112 -1
  96. package/agent/apps/desktop/electron/connection-config.cjs +288 -0
  97. package/agent/apps/desktop/electron/connection-config.test.cjs +396 -0
  98. package/agent/apps/desktop/electron/dashboard-token.cjs +99 -0
  99. package/agent/apps/desktop/electron/dashboard-token.test.cjs +142 -0
  100. package/agent/apps/desktop/electron/desktop-uninstall.cjs +232 -0
  101. package/agent/apps/desktop/electron/desktop-uninstall.test.cjs +246 -0
  102. package/agent/apps/desktop/electron/entitlements.mac.inherit.plist +2 -0
  103. package/agent/apps/desktop/electron/fs-read-dir.cjs +109 -0
  104. package/agent/apps/desktop/electron/fs-read-dir.test.cjs +364 -0
  105. package/agent/apps/desktop/electron/gateway-ws-probe.cjs +188 -0
  106. package/agent/apps/desktop/electron/gateway-ws-probe.test.cjs +122 -0
  107. package/agent/apps/desktop/electron/git-root.cjs +54 -0
  108. package/agent/apps/desktop/electron/git-root.test.cjs +40 -0
  109. package/agent/apps/desktop/electron/git-worktrees.cjs +174 -0
  110. package/agent/apps/desktop/electron/hardening.cjs +123 -28
  111. package/agent/apps/desktop/electron/hardening.test.cjs +163 -0
  112. package/agent/apps/desktop/electron/main.cjs +3121 -331
  113. package/agent/apps/desktop/electron/oauth-net-request.cjs +20 -0
  114. package/agent/apps/desktop/electron/oauth-net-request.test.cjs +34 -0
  115. package/agent/apps/desktop/electron/preload.cjs +52 -2
  116. package/agent/apps/desktop/electron/session-windows.cjs +124 -0
  117. package/agent/apps/desktop/electron/session-windows.test.cjs +199 -0
  118. package/agent/apps/desktop/electron/update-rebuild.cjs +29 -0
  119. package/agent/apps/desktop/electron/update-rebuild.test.cjs +55 -0
  120. package/agent/apps/desktop/electron/update-remote.cjs +56 -0
  121. package/agent/apps/desktop/electron/update-remote.test.cjs +78 -0
  122. package/agent/apps/desktop/electron/vscode-marketplace.cjs +331 -0
  123. package/agent/apps/desktop/electron/vscode-marketplace.test.cjs +113 -0
  124. package/agent/apps/desktop/electron/windows-child-process.test.cjs +57 -0
  125. package/agent/apps/desktop/electron/windows-user-env.cjs +76 -0
  126. package/agent/apps/desktop/electron/windows-user-env.test.cjs +90 -0
  127. package/agent/apps/desktop/electron/workspace-cwd.cjs +38 -0
  128. package/agent/apps/desktop/electron/workspace-cwd.test.cjs +45 -0
  129. package/agent/apps/desktop/eslint.config.mjs +0 -3
  130. package/agent/apps/desktop/index.html +27 -2
  131. package/agent/apps/desktop/package.json +31 -11
  132. package/agent/apps/desktop/pr-assets/session-source-folders.png +0 -0
  133. package/agent/apps/desktop/public/apple-touch-icon.png +0 -0
  134. package/agent/apps/desktop/public/nous-girl.jpg +0 -0
  135. package/agent/apps/desktop/scripts/assert-dist-built.cjs +70 -0
  136. package/agent/apps/desktop/scripts/assert-dist-built.test.cjs +84 -0
  137. package/agent/apps/desktop/scripts/before-pack.cjs +78 -0
  138. package/agent/apps/desktop/scripts/before-pack.test.cjs +53 -0
  139. package/agent/apps/desktop/scripts/diag-scroll-reset.mjs +229 -0
  140. package/agent/apps/desktop/scripts/patch-electron-builder-mac-binary.cjs +64 -0
  141. package/agent/apps/desktop/scripts/run-electron-builder.cjs +57 -0
  142. package/agent/apps/desktop/src/app/agents/index.tsx +53 -45
  143. package/agent/apps/desktop/src/app/artifacts/index.tsx +102 -83
  144. package/agent/apps/desktop/src/app/chat/chat-drop-overlay.tsx +29 -8
  145. package/agent/apps/desktop/src/app/chat/chat-swap-overlay.tsx +47 -0
  146. package/agent/apps/desktop/src/app/chat/composer/attachments.tsx +81 -45
  147. package/agent/apps/desktop/src/app/chat/composer/completion-drawer.tsx +13 -24
  148. package/agent/apps/desktop/src/app/chat/composer/context-menu.tsx +138 -88
  149. package/agent/apps/desktop/src/app/chat/composer/controls.tsx +138 -90
  150. package/agent/apps/desktop/src/app/chat/composer/enter-submit-dom-race.test.tsx +218 -0
  151. package/agent/apps/desktop/src/app/chat/composer/focus.ts +32 -0
  152. package/agent/apps/desktop/src/app/chat/composer/help-hint.tsx +38 -25
  153. package/agent/apps/desktop/src/app/chat/composer/hooks/use-live-completion-adapter.ts +7 -0
  154. package/agent/apps/desktop/src/app/chat/composer/hooks/use-mic-recorder.ts +22 -12
  155. package/agent/apps/desktop/src/app/chat/composer/hooks/use-slash-completions.ts +142 -14
  156. package/agent/apps/desktop/src/app/chat/composer/hooks/use-voice-conversation.ts +14 -11
  157. package/agent/apps/desktop/src/app/chat/composer/hooks/use-voice-recorder.ts +9 -6
  158. package/agent/apps/desktop/src/app/chat/composer/ime-composition-dom-repro.test.tsx +108 -0
  159. package/agent/apps/desktop/src/app/chat/composer/index.tsx +930 -180
  160. package/agent/apps/desktop/src/app/chat/composer/inline-refs.ts +136 -32
  161. package/agent/apps/desktop/src/app/chat/composer/model-pill.tsx +86 -0
  162. package/agent/apps/desktop/src/app/chat/composer/queue-panel.tsx +54 -75
  163. package/agent/apps/desktop/src/app/chat/composer/rich-editor.test.ts +117 -1
  164. package/agent/apps/desktop/src/app/chat/composer/rich-editor.ts +117 -6
  165. package/agent/apps/desktop/src/app/chat/composer/slash-nav-dom-repro.test.tsx +186 -0
  166. package/agent/apps/desktop/src/app/chat/composer/status-stack/index.tsx +202 -0
  167. package/agent/apps/desktop/src/app/chat/composer/status-stack/status-row.tsx +155 -0
  168. package/agent/apps/desktop/src/app/chat/composer/text-utils.test.ts +104 -0
  169. package/agent/apps/desktop/src/app/chat/composer/text-utils.ts +37 -9
  170. package/agent/apps/desktop/src/app/chat/composer/trigger-popover.test.tsx +50 -0
  171. package/agent/apps/desktop/src/app/chat/composer/trigger-popover.tsx +105 -40
  172. package/agent/apps/desktop/src/app/chat/composer/types.ts +5 -0
  173. package/agent/apps/desktop/src/app/chat/composer/url-dialog.tsx +11 -15
  174. package/agent/apps/desktop/src/app/chat/composer/voice-activity.tsx +8 -4
  175. package/agent/apps/desktop/src/app/chat/hooks/use-composer-actions.test.ts +57 -0
  176. package/agent/apps/desktop/src/app/chat/hooks/use-composer-actions.ts +70 -16
  177. package/agent/apps/desktop/src/app/chat/hooks/use-file-drop-zone.ts +52 -16
  178. package/agent/apps/desktop/src/app/chat/index.tsx +234 -81
  179. package/agent/apps/desktop/src/app/chat/perf-probe.tsx +69 -21
  180. package/agent/apps/desktop/src/app/chat/right-rail/preview-console.tsx +44 -40
  181. package/agent/apps/desktop/src/app/chat/right-rail/preview-file.tsx +71 -25
  182. package/agent/apps/desktop/src/app/chat/right-rail/preview-pane.test.tsx +40 -1
  183. package/agent/apps/desktop/src/app/chat/right-rail/preview-pane.tsx +55 -53
  184. package/agent/apps/desktop/src/app/chat/right-rail/preview.tsx +35 -17
  185. package/agent/apps/desktop/src/app/chat/scroll-to-bottom-button.test.tsx +67 -0
  186. package/agent/apps/desktop/src/app/chat/scroll-to-bottom-button.tsx +74 -0
  187. package/agent/apps/desktop/src/app/chat/sidebar/cron-jobs-section.tsx +356 -0
  188. package/agent/apps/desktop/src/app/chat/sidebar/index.tsx +1189 -364
  189. package/agent/apps/desktop/src/app/chat/sidebar/load-more-row.tsx +30 -0
  190. package/agent/apps/desktop/src/app/chat/sidebar/order.test.ts +21 -0
  191. package/agent/apps/desktop/src/app/chat/sidebar/order.ts +17 -0
  192. package/agent/apps/desktop/src/app/chat/sidebar/profile-switcher.tsx +524 -0
  193. package/agent/apps/desktop/src/app/chat/sidebar/session-actions-menu.tsx +80 -45
  194. package/agent/apps/desktop/src/app/chat/sidebar/session-row.tsx +120 -25
  195. package/agent/apps/desktop/src/app/chat/sidebar/virtual-session-list.tsx +7 -13
  196. package/agent/apps/desktop/src/app/chat/sidebar/workspace-groups.test.ts +149 -0
  197. package/agent/apps/desktop/src/app/chat/sidebar/workspace-groups.ts +326 -0
  198. package/agent/apps/desktop/src/app/chat/thread-loading.ts +7 -2
  199. package/agent/apps/desktop/src/app/command-center/index.tsx +320 -581
  200. package/agent/apps/desktop/src/app/command-palette/index.tsx +681 -0
  201. package/agent/apps/desktop/src/app/command-palette/marketplace-theme-page.tsx +157 -0
  202. package/agent/apps/desktop/src/app/cron/index.tsx +392 -324
  203. package/agent/apps/desktop/src/app/cron/job-state.ts +29 -0
  204. package/agent/apps/desktop/src/app/desktop-controller.tsx +618 -123
  205. package/agent/apps/desktop/src/app/floating-hud.ts +22 -0
  206. package/agent/apps/desktop/src/app/gateway/hooks/use-gateway-boot.test.tsx +265 -0
  207. package/agent/apps/desktop/src/app/gateway/hooks/use-gateway-boot.ts +260 -14
  208. package/agent/apps/desktop/src/app/gateway/hooks/use-gateway-request.ts +48 -4
  209. package/agent/apps/desktop/src/app/hooks/use-keybinds.ts +270 -0
  210. package/agent/apps/desktop/src/app/hooks/use-refresh-hotkey.ts +45 -0
  211. package/agent/apps/desktop/src/app/layout-constants.ts +19 -0
  212. package/agent/apps/desktop/src/app/messaging/index.tsx +136 -241
  213. package/agent/apps/desktop/src/app/messaging/platform-icon.tsx +95 -0
  214. package/agent/apps/desktop/src/app/model-visibility-overlay.tsx +31 -0
  215. package/agent/apps/desktop/src/app/overlays/overlay-search-input.tsx +18 -62
  216. package/agent/apps/desktop/src/app/overlays/overlay-split-layout.tsx +59 -7
  217. package/agent/apps/desktop/src/app/overlays/overlay-view.tsx +9 -5
  218. package/agent/apps/desktop/src/app/page-search-shell.tsx +42 -20
  219. package/agent/apps/desktop/src/app/profiles/create-profile-dialog.tsx +165 -0
  220. package/agent/apps/desktop/src/app/profiles/delete-profile-dialog.tsx +65 -0
  221. package/agent/apps/desktop/src/app/profiles/index.tsx +174 -199
  222. package/agent/apps/desktop/src/app/profiles/rename-profile-dialog.tsx +125 -0
  223. package/agent/apps/desktop/src/app/right-sidebar/files/dnd-manager.ts +27 -0
  224. package/agent/apps/desktop/src/app/right-sidebar/files/ipc.test.ts +100 -0
  225. package/agent/apps/desktop/src/app/right-sidebar/files/ipc.ts +12 -18
  226. package/agent/apps/desktop/src/app/right-sidebar/files/remote-picker.tsx +177 -0
  227. package/agent/apps/desktop/src/app/right-sidebar/files/tree.tsx +35 -21
  228. package/agent/apps/desktop/src/app/right-sidebar/files/use-project-tree.test.ts +75 -3
  229. package/agent/apps/desktop/src/app/right-sidebar/files/use-project-tree.ts +152 -5
  230. package/agent/apps/desktop/src/app/right-sidebar/index.test.tsx +75 -0
  231. package/agent/apps/desktop/src/app/right-sidebar/index.tsx +166 -129
  232. package/agent/apps/desktop/src/app/right-sidebar/store.ts +19 -4
  233. package/agent/apps/desktop/src/app/right-sidebar/terminal/buffer.ts +65 -0
  234. package/agent/apps/desktop/src/app/right-sidebar/terminal/index.tsx +29 -34
  235. package/agent/apps/desktop/src/app/right-sidebar/terminal/persistent.tsx +18 -6
  236. package/agent/apps/desktop/src/app/right-sidebar/terminal/selection.ts +93 -32
  237. package/agent/apps/desktop/src/app/right-sidebar/terminal/use-terminal-session.ts +381 -119
  238. package/agent/apps/desktop/src/app/routes.ts +9 -0
  239. package/agent/apps/desktop/src/app/session/hooks/use-cwd-actions.ts +17 -7
  240. package/agent/apps/desktop/src/app/session/hooks/use-message-stream.ts +365 -47
  241. package/agent/apps/desktop/src/app/session/hooks/use-model-controls.test.tsx +198 -0
  242. package/agent/apps/desktop/src/app/session/hooks/use-model-controls.ts +70 -34
  243. package/agent/apps/desktop/src/app/session/hooks/use-prompt-actions.test.tsx +1061 -0
  244. package/agent/apps/desktop/src/app/session/hooks/use-prompt-actions.ts +1143 -165
  245. package/agent/apps/desktop/src/app/session/hooks/use-route-resume.test.tsx +341 -2
  246. package/agent/apps/desktop/src/app/session/hooks/use-route-resume.ts +176 -5
  247. package/agent/apps/desktop/src/app/session/hooks/use-session-actions.test.tsx +259 -0
  248. package/agent/apps/desktop/src/app/session/hooks/use-session-actions.ts +452 -149
  249. package/agent/apps/desktop/src/app/session/hooks/use-session-state-cache.test.tsx +327 -0
  250. package/agent/apps/desktop/src/app/session/hooks/use-session-state-cache.ts +133 -4
  251. package/agent/apps/desktop/src/app/session-picker-overlay.tsx +32 -0
  252. package/agent/apps/desktop/src/app/session-switcher.tsx +107 -0
  253. package/agent/apps/desktop/src/app/settings/about-settings.tsx +45 -36
  254. package/agent/apps/desktop/src/app/settings/appearance-settings.tsx +243 -162
  255. package/agent/apps/desktop/src/app/settings/config-settings.tsx +86 -66
  256. package/agent/apps/desktop/src/app/settings/constants.ts +459 -122
  257. package/agent/apps/desktop/src/app/settings/credential-key-ui.tsx +373 -0
  258. package/agent/apps/desktop/src/app/settings/env-credentials.tsx +198 -0
  259. package/agent/apps/desktop/src/app/settings/env-var-actions-menu.tsx +136 -0
  260. package/agent/apps/desktop/src/app/settings/field-copy.ts +56 -0
  261. package/agent/apps/desktop/src/app/settings/gateway-settings.tsx +385 -72
  262. package/agent/apps/desktop/src/app/settings/helpers.test.ts +156 -1
  263. package/agent/apps/desktop/src/app/settings/helpers.ts +30 -2
  264. package/agent/apps/desktop/src/app/settings/index.tsx +118 -84
  265. package/agent/apps/desktop/src/app/settings/keys-settings.tsx +62 -419
  266. package/agent/apps/desktop/src/app/settings/mcp-settings.tsx +65 -60
  267. package/agent/apps/desktop/src/app/settings/model-settings.test.tsx +129 -5
  268. package/agent/apps/desktop/src/app/settings/model-settings.tsx +370 -65
  269. package/agent/apps/desktop/src/app/settings/notifications-settings.tsx +150 -0
  270. package/agent/apps/desktop/src/app/settings/primitives.tsx +5 -11
  271. package/agent/apps/desktop/src/app/settings/provider-config-panel.test.tsx +142 -0
  272. package/agent/apps/desktop/src/app/settings/provider-config-panel.tsx +182 -0
  273. package/agent/apps/desktop/src/app/settings/providers-settings.test.tsx +171 -0
  274. package/agent/apps/desktop/src/app/settings/providers-settings.tsx +471 -0
  275. package/agent/apps/desktop/src/app/settings/sessions-settings.tsx +183 -71
  276. package/agent/apps/desktop/src/app/settings/toolset-config-panel.test.tsx +135 -1
  277. package/agent/apps/desktop/src/app/settings/toolset-config-panel.tsx +180 -57
  278. package/agent/apps/desktop/src/app/settings/types.ts +9 -6
  279. package/agent/apps/desktop/src/app/settings/uninstall-section.tsx +185 -0
  280. package/agent/apps/desktop/src/app/settings/use-deep-link-highlight.ts +60 -0
  281. package/agent/apps/desktop/src/app/shell/app-shell.tsx +59 -13
  282. package/agent/apps/desktop/src/app/shell/gateway-menu-panel.tsx +37 -32
  283. package/agent/apps/desktop/src/app/shell/hooks/use-overlay-routing.ts +6 -3
  284. package/agent/apps/desktop/src/app/shell/hooks/use-statusbar-items.tsx +212 -53
  285. package/agent/apps/desktop/src/app/shell/keybind-panel.tsx +215 -0
  286. package/agent/apps/desktop/src/app/shell/model-edit-submenu.test.tsx +84 -0
  287. package/agent/apps/desktop/src/app/shell/model-edit-submenu.tsx +244 -0
  288. package/agent/apps/desktop/src/app/shell/model-menu-panel.tsx +392 -0
  289. package/agent/apps/desktop/src/app/shell/statusbar-controls.tsx +23 -33
  290. package/agent/apps/desktop/src/app/shell/titlebar-controls.tsx +79 -95
  291. package/agent/apps/desktop/src/app/shell/titlebar.ts +8 -2
  292. package/agent/apps/desktop/src/app/skills/index.test.tsx +11 -0
  293. package/agent/apps/desktop/src/app/skills/index.tsx +79 -64
  294. package/agent/apps/desktop/src/app/types.ts +85 -0
  295. package/agent/apps/desktop/src/app/updates-overlay.tsx +110 -105
  296. package/agent/apps/desktop/src/components/assistant-ui/ansi-text.tsx +34 -0
  297. package/agent/apps/desktop/src/components/assistant-ui/block-direction.test.tsx +129 -0
  298. package/agent/apps/desktop/src/components/assistant-ui/clarify-tool.tsx +102 -81
  299. package/agent/apps/desktop/src/components/assistant-ui/directive-text.tsx +92 -15
  300. package/agent/apps/desktop/src/components/assistant-ui/markdown-text.test.ts +38 -0
  301. package/agent/apps/desktop/src/components/assistant-ui/markdown-text.tsx +304 -45
  302. package/agent/apps/desktop/src/components/assistant-ui/message-render-boundary.test.tsx +80 -0
  303. package/agent/apps/desktop/src/components/assistant-ui/message-render-boundary.tsx +48 -0
  304. package/agent/apps/desktop/src/components/assistant-ui/streaming.test.tsx +142 -90
  305. package/agent/apps/desktop/src/components/assistant-ui/thread-list.tsx +337 -0
  306. package/agent/apps/desktop/src/components/assistant-ui/thread.tsx +667 -190
  307. package/agent/apps/desktop/src/components/assistant-ui/tool-approval-group.test.tsx +299 -0
  308. package/agent/apps/desktop/src/components/assistant-ui/tool-approval.test.tsx +133 -0
  309. package/agent/apps/desktop/src/components/assistant-ui/tool-approval.tsx +239 -0
  310. package/agent/apps/desktop/src/components/assistant-ui/tool-fallback-model.test.ts +31 -0
  311. package/agent/apps/desktop/src/components/assistant-ui/tool-fallback-model.ts +152 -134
  312. package/agent/apps/desktop/src/components/assistant-ui/tool-fallback.tsx +142 -150
  313. package/agent/apps/desktop/src/components/assistant-ui/tooltip-icon-button.tsx +14 -12
  314. package/agent/apps/desktop/src/components/assistant-ui/user-message-edit.test.tsx +141 -0
  315. package/agent/apps/desktop/src/components/assistant-ui/user-message-text.tsx +152 -0
  316. package/agent/apps/desktop/src/components/boot-failure-overlay.tsx +150 -33
  317. package/agent/apps/desktop/src/components/boot-failure-reauth.test.ts +100 -0
  318. package/agent/apps/desktop/src/components/boot-failure-reauth.ts +81 -0
  319. package/agent/apps/desktop/src/components/brand-mark.tsx +19 -0
  320. package/agent/apps/desktop/src/components/chat/code-card.tsx +1 -1
  321. package/agent/apps/desktop/src/components/chat/composer-dock.ts +31 -0
  322. package/agent/apps/desktop/src/components/chat/diff-lines.tsx +1 -1
  323. package/agent/apps/desktop/src/components/chat/disclosure-row.tsx +13 -3
  324. package/agent/apps/desktop/src/components/chat/expandable-block.tsx +52 -0
  325. package/agent/apps/desktop/src/components/chat/generated-image-result.tsx +174 -0
  326. package/agent/apps/desktop/src/components/chat/image-generation-placeholder.tsx +70 -37
  327. package/agent/apps/desktop/src/components/chat/intro.tsx +8 -7
  328. package/agent/apps/desktop/src/components/chat/preview-attachment.tsx +4 -2
  329. package/agent/apps/desktop/src/components/chat/shiki-highlighter.test.ts +37 -0
  330. package/agent/apps/desktop/src/components/chat/shiki-highlighter.tsx +96 -22
  331. package/agent/apps/desktop/src/components/chat/status-row.tsx +70 -0
  332. package/agent/apps/desktop/src/components/chat/status-section.tsx +42 -0
  333. package/agent/apps/desktop/src/components/chat/terminal-output.tsx +54 -0
  334. package/agent/apps/desktop/src/components/chat/zoomable-image.tsx +70 -109
  335. package/agent/apps/desktop/src/components/desktop-install-overlay.tsx +154 -84
  336. package/agent/apps/desktop/src/components/desktop-onboarding-overlay.test.tsx +38 -8
  337. package/agent/apps/desktop/src/components/desktop-onboarding-overlay.tsx +789 -233
  338. package/agent/apps/desktop/src/components/error-boundary.tsx +77 -0
  339. package/agent/apps/desktop/src/components/gateway-connecting-overlay.test.tsx +144 -0
  340. package/agent/apps/desktop/src/components/gateway-connecting-overlay.tsx +7 -1
  341. package/agent/apps/desktop/src/components/haptics-provider.tsx +24 -0
  342. package/agent/apps/desktop/src/components/language-switcher.test.tsx +53 -0
  343. package/agent/apps/desktop/src/components/language-switcher.tsx +175 -0
  344. package/agent/apps/desktop/src/components/model-picker.tsx +42 -40
  345. package/agent/apps/desktop/src/components/model-visibility-dialog.tsx +166 -0
  346. package/agent/apps/desktop/src/components/notifications.tsx +48 -27
  347. package/agent/apps/desktop/src/components/pane-shell/index.ts +1 -1
  348. package/agent/apps/desktop/src/components/pane-shell/pane-shell.tsx +146 -9
  349. package/agent/apps/desktop/src/components/prompt-overlays.tsx +234 -0
  350. package/agent/apps/desktop/src/components/session-picker.tsx +108 -0
  351. package/agent/apps/desktop/src/components/ui/action-status.tsx +25 -0
  352. package/agent/apps/desktop/src/components/ui/badge.tsx +35 -0
  353. package/agent/apps/desktop/src/components/ui/button.tsx +37 -13
  354. package/agent/apps/desktop/src/components/ui/confirm-dialog.tsx +109 -0
  355. package/agent/apps/desktop/src/components/ui/control.ts +25 -0
  356. package/agent/apps/desktop/src/components/ui/copy-button.test.tsx +36 -0
  357. package/agent/apps/desktop/src/components/ui/copy-button.tsx +38 -27
  358. package/agent/apps/desktop/src/components/ui/dialog.tsx +39 -11
  359. package/agent/apps/desktop/src/components/ui/dropdown-menu.tsx +98 -24
  360. package/agent/apps/desktop/src/components/ui/error-state.tsx +50 -0
  361. package/agent/apps/desktop/src/components/ui/fade-text.tsx +9 -2
  362. package/agent/apps/desktop/src/components/ui/{braille-spinner.tsx → glyph-spinner.tsx} +15 -13
  363. package/agent/apps/desktop/src/components/ui/input.tsx +5 -2
  364. package/agent/apps/desktop/src/components/ui/kbd.tsx +83 -12
  365. package/agent/apps/desktop/src/components/ui/log-view.tsx +19 -0
  366. package/agent/apps/desktop/src/components/ui/pagination.tsx +12 -5
  367. package/agent/apps/desktop/src/components/ui/popover.tsx +44 -0
  368. package/agent/apps/desktop/src/components/ui/search-field.tsx +80 -0
  369. package/agent/apps/desktop/src/components/ui/segmented-control.tsx +51 -0
  370. package/agent/apps/desktop/src/components/ui/select.tsx +10 -3
  371. package/agent/apps/desktop/src/components/ui/sheet.tsx +8 -2
  372. package/agent/apps/desktop/src/components/ui/sidebar.tsx +18 -25
  373. package/agent/apps/desktop/src/components/ui/switch.tsx +38 -15
  374. package/agent/apps/desktop/src/components/ui/textarea.tsx +4 -11
  375. package/agent/apps/desktop/src/components/ui/tool-icon.tsx +65 -0
  376. package/agent/apps/desktop/src/components/ui/tooltip.tsx +31 -4
  377. package/agent/apps/desktop/src/fonts/JetBrainsMono-Bold.woff2 +0 -0
  378. package/agent/apps/desktop/src/fonts/JetBrainsMono-Italic.woff2 +0 -0
  379. package/agent/apps/desktop/src/fonts/JetBrainsMono-Regular.woff2 +0 -0
  380. package/agent/apps/desktop/src/global.d.ts +181 -4
  381. package/agent/apps/desktop/src/hermes.test.ts +60 -0
  382. package/agent/apps/desktop/src/hermes.ts +190 -13
  383. package/agent/apps/desktop/src/hooks/use-image-download.ts +85 -0
  384. package/agent/apps/desktop/src/hooks/use-resize-observer.ts +13 -4
  385. package/agent/apps/desktop/src/hooks/use-worktree-info.ts +68 -0
  386. package/agent/apps/desktop/src/i18n/catalog.ts +12 -0
  387. package/agent/apps/desktop/src/i18n/context.test.tsx +232 -0
  388. package/agent/apps/desktop/src/i18n/context.tsx +183 -0
  389. package/agent/apps/desktop/src/i18n/define-locale.ts +41 -0
  390. package/agent/apps/desktop/src/i18n/en.ts +1921 -0
  391. package/agent/apps/desktop/src/i18n/index.ts +20 -0
  392. package/agent/apps/desktop/src/i18n/ja.ts +2053 -0
  393. package/agent/apps/desktop/src/i18n/languages.test.ts +43 -0
  394. package/agent/apps/desktop/src/i18n/languages.ts +86 -0
  395. package/agent/apps/desktop/src/i18n/runtime.test.ts +75 -0
  396. package/agent/apps/desktop/src/i18n/runtime.ts +53 -0
  397. package/agent/apps/desktop/src/i18n/types.ts +1559 -0
  398. package/agent/apps/desktop/src/i18n/zh-hant.ts +1992 -0
  399. package/agent/apps/desktop/src/i18n/zh.ts +2099 -0
  400. package/agent/apps/desktop/src/lib/ansi.test.ts +123 -0
  401. package/agent/apps/desktop/src/lib/ansi.ts +186 -0
  402. package/agent/apps/desktop/src/lib/chat-messages.test.ts +79 -0
  403. package/agent/apps/desktop/src/lib/chat-messages.ts +68 -29
  404. package/agent/apps/desktop/src/lib/chat-runtime.test.ts +65 -1
  405. package/agent/apps/desktop/src/lib/chat-runtime.ts +39 -3
  406. package/agent/apps/desktop/src/lib/completion-sound.ts +519 -0
  407. package/agent/apps/desktop/src/lib/desktop-fs.test.ts +116 -0
  408. package/agent/apps/desktop/src/lib/desktop-fs.ts +113 -0
  409. package/agent/apps/desktop/src/lib/desktop-slash-commands.test.ts +89 -6
  410. package/agent/apps/desktop/src/lib/desktop-slash-commands.ts +270 -131
  411. package/agent/apps/desktop/src/lib/external-link.test.tsx +27 -0
  412. package/agent/apps/desktop/src/lib/external-link.tsx +9 -2
  413. package/agent/apps/desktop/src/lib/gateway-events.test.ts +27 -0
  414. package/agent/apps/desktop/src/lib/gateway-events.ts +16 -0
  415. package/agent/apps/desktop/src/lib/gateway-ws-url.test.ts +78 -0
  416. package/agent/apps/desktop/src/lib/gateway-ws-url.ts +91 -0
  417. package/agent/apps/desktop/src/lib/generated-images.test.ts +97 -0
  418. package/agent/apps/desktop/src/lib/generated-images.ts +116 -0
  419. package/agent/apps/desktop/src/lib/haptics.ts +17 -0
  420. package/agent/apps/desktop/src/lib/icons.ts +10 -2
  421. package/agent/apps/desktop/src/lib/keybinds/actions.ts +137 -0
  422. package/agent/apps/desktop/src/lib/keybinds/combo.test.ts +86 -0
  423. package/agent/apps/desktop/src/lib/keybinds/combo.ts +195 -0
  424. package/agent/apps/desktop/src/lib/local-preview.ts +23 -2
  425. package/agent/apps/desktop/src/lib/markdown-preprocess.ts +20 -7
  426. package/agent/apps/desktop/src/lib/media.remote.test.ts +90 -0
  427. package/agent/apps/desktop/src/lib/media.ts +40 -1
  428. package/agent/apps/desktop/src/lib/model-status-label.test.ts +59 -0
  429. package/agent/apps/desktop/src/lib/model-status-label.ts +122 -0
  430. package/agent/apps/desktop/src/lib/mutable-ref.ts +6 -0
  431. package/agent/apps/desktop/src/lib/profile-color.ts +58 -0
  432. package/agent/apps/desktop/src/lib/query-client.ts +13 -0
  433. package/agent/apps/desktop/src/lib/remend-tail.test.ts +105 -0
  434. package/agent/apps/desktop/src/lib/remend-tail.ts +108 -0
  435. package/agent/apps/desktop/src/lib/session-export.ts +6 -3
  436. package/agent/apps/desktop/src/lib/session-ids.test.ts +44 -0
  437. package/agent/apps/desktop/src/lib/session-ids.ts +26 -0
  438. package/agent/apps/desktop/src/lib/session-search.test.ts +66 -0
  439. package/agent/apps/desktop/src/lib/session-search.ts +21 -0
  440. package/agent/apps/desktop/src/lib/session-source.ts +126 -0
  441. package/agent/apps/desktop/src/lib/storage.test.ts +25 -0
  442. package/agent/apps/desktop/src/lib/storage.ts +35 -1
  443. package/agent/apps/desktop/src/lib/todos.test.ts +46 -1
  444. package/agent/apps/desktop/src/lib/todos.ts +37 -0
  445. package/agent/apps/desktop/src/lib/tool-result-summary.ts +5 -1
  446. package/agent/apps/desktop/src/lib/update-copy.test.ts +38 -0
  447. package/agent/apps/desktop/src/lib/update-copy.ts +44 -0
  448. package/agent/apps/desktop/src/lib/use-enter-animation.ts +2 -2
  449. package/agent/apps/desktop/src/lib/yolo-session.ts +50 -0
  450. package/agent/apps/desktop/src/main.tsx +19 -19
  451. package/agent/apps/desktop/src/store/boot.ts +4 -3
  452. package/agent/apps/desktop/src/store/clarify.test.ts +81 -0
  453. package/agent/apps/desktop/src/store/clarify.ts +50 -13
  454. package/agent/apps/desktop/src/store/command-palette.ts +20 -0
  455. package/agent/apps/desktop/src/store/compaction.test.ts +53 -0
  456. package/agent/apps/desktop/src/store/compaction.ts +38 -0
  457. package/agent/apps/desktop/src/store/completion-sound.ts +32 -0
  458. package/agent/apps/desktop/src/store/composer-input-history.test.ts +147 -0
  459. package/agent/apps/desktop/src/store/composer-input-history.ts +158 -0
  460. package/agent/apps/desktop/src/store/composer-queue.test.ts +68 -0
  461. package/agent/apps/desktop/src/store/composer-queue.ts +76 -0
  462. package/agent/apps/desktop/src/store/composer-status.test.ts +99 -0
  463. package/agent/apps/desktop/src/store/composer-status.ts +277 -0
  464. package/agent/apps/desktop/src/store/composer.test.ts +106 -0
  465. package/agent/apps/desktop/src/store/composer.ts +116 -0
  466. package/agent/apps/desktop/src/store/cron.ts +19 -0
  467. package/agent/apps/desktop/src/store/gateway.ts +280 -6
  468. package/agent/apps/desktop/src/store/keybinds.ts +143 -0
  469. package/agent/apps/desktop/src/store/layout.ts +107 -9
  470. package/agent/apps/desktop/src/store/model-presets.test.ts +51 -0
  471. package/agent/apps/desktop/src/store/model-presets.ts +86 -0
  472. package/agent/apps/desktop/src/store/model-visibility.test.ts +99 -0
  473. package/agent/apps/desktop/src/store/model-visibility.ts +161 -0
  474. package/agent/apps/desktop/src/store/native-notifications.test.ts +192 -0
  475. package/agent/apps/desktop/src/store/native-notifications.ts +203 -0
  476. package/agent/apps/desktop/src/store/notifications.ts +10 -7
  477. package/agent/apps/desktop/src/store/onboarding.test.ts +271 -1
  478. package/agent/apps/desktop/src/store/onboarding.ts +268 -38
  479. package/agent/apps/desktop/src/store/preview.ts +10 -1
  480. package/agent/apps/desktop/src/store/profile.test.ts +89 -0
  481. package/agent/apps/desktop/src/store/profile.ts +395 -0
  482. package/agent/apps/desktop/src/store/prompts.test.ts +127 -0
  483. package/agent/apps/desktop/src/store/prompts.ts +117 -0
  484. package/agent/apps/desktop/src/store/session-switcher.test.ts +115 -0
  485. package/agent/apps/desktop/src/store/session-switcher.ts +128 -0
  486. package/agent/apps/desktop/src/store/session-sync.ts +25 -0
  487. package/agent/apps/desktop/src/store/session.test.ts +268 -2
  488. package/agent/apps/desktop/src/store/session.ts +392 -18
  489. package/agent/apps/desktop/src/store/subagents.ts +3 -0
  490. package/agent/apps/desktop/src/store/system-actions.ts +48 -0
  491. package/agent/apps/desktop/src/store/thread-scroll.ts +58 -5
  492. package/agent/apps/desktop/src/store/todos.test.ts +47 -0
  493. package/agent/apps/desktop/src/store/todos.ts +64 -0
  494. package/agent/apps/desktop/src/store/tool-dismiss.ts +45 -0
  495. package/agent/apps/desktop/src/store/translucency.ts +38 -0
  496. package/agent/apps/desktop/src/store/updates.test.ts +187 -2
  497. package/agent/apps/desktop/src/store/updates.ts +268 -18
  498. package/agent/apps/desktop/src/store/windows.test.ts +143 -0
  499. package/agent/apps/desktop/src/store/windows.ts +115 -0
  500. package/agent/apps/desktop/src/styles.css +510 -119
  501. package/agent/apps/desktop/src/themes/color.ts +142 -0
  502. package/agent/apps/desktop/src/themes/context.tsx +128 -75
  503. package/agent/apps/desktop/src/themes/install.test.ts +119 -0
  504. package/agent/apps/desktop/src/themes/install.ts +95 -0
  505. package/agent/apps/desktop/src/themes/presets.test.ts +33 -0
  506. package/agent/apps/desktop/src/themes/presets.ts +13 -4
  507. package/agent/apps/desktop/src/themes/profile-theme.test.ts +41 -0
  508. package/agent/apps/desktop/src/themes/types.ts +35 -0
  509. package/agent/apps/desktop/src/themes/user-themes.test.ts +63 -0
  510. package/agent/apps/desktop/src/themes/user-themes.ts +122 -0
  511. package/agent/apps/desktop/src/themes/vscode.test.ts +171 -0
  512. package/agent/apps/desktop/src/themes/vscode.ts +343 -0
  513. package/agent/apps/desktop/src/types/hermes.ts +138 -1
  514. package/agent/apps/desktop/tsconfig.json +2 -2
  515. package/agent/apps/desktop/vite.config.ts +18 -0
  516. package/agent/apps/shared/package.json +1 -1
  517. package/agent/apps/shared/src/json-rpc-gateway.ts +63 -2
  518. package/agent/apps/shared/tsconfig.json +2 -2
  519. package/agent/cli-config.yaml.example +78 -1
  520. package/agent/cli.py +2177 -3162
  521. package/agent/cron/blueprint_catalog.py +713 -0
  522. package/agent/cron/jobs.py +226 -110
  523. package/agent/cron/scheduler.py +468 -193
  524. package/agent/cron/scheduler_provider.py +177 -0
  525. package/agent/cron/scripts/__init__.py +1 -0
  526. package/agent/cron/scripts/classify_items.py +226 -0
  527. package/agent/cron/suggestion_catalog.py +154 -0
  528. package/agent/cron/suggestions.py +257 -0
  529. package/agent/docs/chronos-managed-cron-contract.md +196 -0
  530. package/agent/docs/design/profile-builder.md +146 -0
  531. package/agent/docs/middleware/README.md +260 -0
  532. package/agent/docs/observability/README.md +316 -0
  533. package/agent/docs/plans/2026-06-09-003-fix-telegram-stream-overflow-continuations-plan.md +240 -0
  534. package/agent/docs/rca-ssl-cacert-post-git-pull.md +54 -0
  535. package/agent/docs/relay-connector-contract.md +285 -0
  536. package/agent/gateway/authz_mixin.py +536 -0
  537. package/agent/gateway/channel_directory.py +65 -3
  538. package/agent/gateway/config.py +222 -12
  539. package/agent/gateway/display_config.py +10 -0
  540. package/agent/gateway/hooks.py +17 -0
  541. package/agent/gateway/kanban_watchers.py +1146 -0
  542. package/agent/gateway/message_timestamps.py +166 -0
  543. package/agent/gateway/platforms/ADDING_A_PLATFORM.md +29 -0
  544. package/agent/gateway/platforms/api_server.py +216 -38
  545. package/agent/gateway/platforms/base.py +210 -58
  546. package/agent/gateway/platforms/email.py +122 -12
  547. package/agent/gateway/platforms/feishu.py +80 -11
  548. package/agent/gateway/platforms/feishu_meeting_invite.py +212 -0
  549. package/agent/gateway/platforms/matrix.py +1498 -297
  550. package/agent/gateway/platforms/qqbot/adapter.py +6 -0
  551. package/agent/gateway/platforms/signal.py +8 -0
  552. package/agent/gateway/platforms/slack.py +308 -12
  553. package/agent/gateway/platforms/telegram.py +831 -24
  554. package/agent/gateway/platforms/webhook.py +109 -21
  555. package/agent/gateway/platforms/weixin.py +113 -2
  556. package/agent/gateway/platforms/whatsapp.py +94 -288
  557. package/agent/gateway/platforms/whatsapp_cloud.py +1956 -0
  558. package/agent/gateway/platforms/whatsapp_common.py +367 -0
  559. package/agent/gateway/platforms/yuanbao.py +608 -191
  560. package/agent/gateway/platforms/yuanbao_proto.py +232 -23
  561. package/agent/gateway/relay/__init__.py +375 -0
  562. package/agent/gateway/relay/adapter.py +222 -0
  563. package/agent/gateway/relay/auth.py +168 -0
  564. package/agent/gateway/relay/descriptor.py +118 -0
  565. package/agent/gateway/relay/transport.py +101 -0
  566. package/agent/gateway/relay/ws_transport.py +327 -0
  567. package/agent/gateway/response_filters.py +53 -0
  568. package/agent/gateway/rich_sent_store.py +80 -0
  569. package/agent/gateway/run.py +2940 -5001
  570. package/agent/gateway/session.py +109 -8
  571. package/agent/gateway/session_context.py +22 -4
  572. package/agent/gateway/slash_commands.py +3854 -0
  573. package/agent/gateway/status.py +141 -21
  574. package/agent/gateway/stream_consumer.py +288 -31
  575. package/agent/hermes-already-has-routines.md +1 -1
  576. package/agent/hermes_cli/__init__.py +62 -17
  577. package/agent/hermes_cli/_parser.py +30 -0
  578. package/agent/hermes_cli/_subprocess_compat.py +61 -0
  579. package/agent/hermes_cli/active_sessions.py +320 -0
  580. package/agent/hermes_cli/auth.py +707 -59
  581. package/agent/hermes_cli/auth_commands.py +39 -22
  582. package/agent/hermes_cli/backup.py +109 -7
  583. package/agent/hermes_cli/banner.py +88 -0
  584. package/agent/hermes_cli/blueprint_cmd.py +318 -0
  585. package/agent/hermes_cli/cli_agent_setup_mixin.py +684 -0
  586. package/agent/hermes_cli/cli_commands_mixin.py +2293 -0
  587. package/agent/hermes_cli/commands.py +215 -91
  588. package/agent/hermes_cli/config.py +967 -130
  589. package/agent/hermes_cli/container_boot.py +76 -11
  590. package/agent/hermes_cli/cron.py +5 -11
  591. package/agent/hermes_cli/curator.py +21 -0
  592. package/agent/hermes_cli/dashboard_auth/__init__.py +2 -0
  593. package/agent/hermes_cli/dashboard_auth/base.py +62 -0
  594. package/agent/hermes_cli/dashboard_auth/cookies.py +32 -19
  595. package/agent/hermes_cli/dashboard_auth/login_page.py +156 -6
  596. package/agent/hermes_cli/dashboard_auth/middleware.py +28 -4
  597. package/agent/hermes_cli/dashboard_auth/prefix.py +46 -2
  598. package/agent/hermes_cli/dashboard_auth/public_paths.py +6 -0
  599. package/agent/hermes_cli/dashboard_auth/routes.py +158 -2
  600. package/agent/hermes_cli/dashboard_auth/ws_tickets.py +85 -11
  601. package/agent/hermes_cli/dashboard_register.py +427 -0
  602. package/agent/hermes_cli/debug.py +155 -50
  603. package/agent/hermes_cli/doctor.py +255 -14
  604. package/agent/hermes_cli/dump.py +60 -6
  605. package/agent/hermes_cli/env_loader.py +33 -0
  606. package/agent/hermes_cli/gateway.py +755 -103
  607. package/agent/hermes_cli/gateway_enroll.py +250 -0
  608. package/agent/hermes_cli/gateway_windows.py +254 -11
  609. package/agent/hermes_cli/gui_uninstall.py +285 -0
  610. package/agent/hermes_cli/inventory.py +105 -4
  611. package/agent/hermes_cli/kanban.py +58 -71
  612. package/agent/hermes_cli/kanban_db.py +391 -14
  613. package/agent/hermes_cli/kanban_decompose.py +2 -2
  614. package/agent/hermes_cli/kanban_specify.py +3 -1
  615. package/agent/hermes_cli/logs.py +2 -0
  616. package/agent/hermes_cli/main.py +2889 -5287
  617. package/agent/hermes_cli/managed_scope.py +214 -0
  618. package/agent/hermes_cli/managed_uv.py +254 -0
  619. package/agent/hermes_cli/mcp_catalog.py +6 -3
  620. package/agent/hermes_cli/mcp_config.py +145 -21
  621. package/agent/hermes_cli/mcp_security.py +96 -0
  622. package/agent/hermes_cli/mcp_startup.py +32 -3
  623. package/agent/hermes_cli/memory_providers.py +149 -0
  624. package/agent/hermes_cli/memory_setup.py +97 -42
  625. package/agent/hermes_cli/middleware.py +313 -0
  626. package/agent/hermes_cli/model_catalog.py +31 -0
  627. package/agent/hermes_cli/model_cost_guard.py +134 -0
  628. package/agent/hermes_cli/model_normalize.py +2 -1
  629. package/agent/hermes_cli/model_setup_flows.py +2759 -0
  630. package/agent/hermes_cli/model_switch.py +242 -27
  631. package/agent/hermes_cli/models.py +284 -44
  632. package/agent/hermes_cli/nous_account.py +33 -6
  633. package/agent/hermes_cli/nous_billing.py +406 -0
  634. package/agent/hermes_cli/nous_subscription.py +202 -5
  635. package/agent/hermes_cli/platforms.py +1 -0
  636. package/agent/hermes_cli/plugins.py +218 -18
  637. package/agent/hermes_cli/plugins_cmd.py +249 -105
  638. package/agent/hermes_cli/portal_cli.py +56 -16
  639. package/agent/hermes_cli/profile_distribution.py +6 -1
  640. package/agent/hermes_cli/profiles.py +283 -32
  641. package/agent/hermes_cli/provider_catalog.py +170 -0
  642. package/agent/hermes_cli/providers.py +4 -1
  643. package/agent/hermes_cli/pty_bridge.py +53 -4
  644. package/agent/hermes_cli/runtime_provider.py +216 -34
  645. package/agent/hermes_cli/secret_prompt.py +4 -4
  646. package/agent/hermes_cli/secrets_cli.py +24 -0
  647. package/agent/hermes_cli/send_cmd.py +28 -2
  648. package/agent/hermes_cli/service_manager.py +166 -19
  649. package/agent/hermes_cli/session_listing.py +97 -0
  650. package/agent/hermes_cli/setup.py +158 -94
  651. package/agent/hermes_cli/setup_whatsapp_cloud.py +541 -0
  652. package/agent/hermes_cli/skills_config.py +8 -2
  653. package/agent/hermes_cli/skills_hub.py +149 -7
  654. package/agent/hermes_cli/status.py +2 -2
  655. package/agent/hermes_cli/subcommands/__init__.py +18 -0
  656. package/agent/hermes_cli/subcommands/_shared.py +29 -0
  657. package/agent/hermes_cli/subcommands/acp.py +52 -0
  658. package/agent/hermes_cli/subcommands/auth.py +109 -0
  659. package/agent/hermes_cli/subcommands/backup.py +38 -0
  660. package/agent/hermes_cli/subcommands/claw.py +92 -0
  661. package/agent/hermes_cli/subcommands/config.py +49 -0
  662. package/agent/hermes_cli/subcommands/cron.py +163 -0
  663. package/agent/hermes_cli/subcommands/dashboard.py +143 -0
  664. package/agent/hermes_cli/subcommands/debug.py +77 -0
  665. package/agent/hermes_cli/subcommands/doctor.py +35 -0
  666. package/agent/hermes_cli/subcommands/dump.py +28 -0
  667. package/agent/hermes_cli/subcommands/gateway.py +332 -0
  668. package/agent/hermes_cli/subcommands/gui.py +63 -0
  669. package/agent/hermes_cli/subcommands/hooks.py +77 -0
  670. package/agent/hermes_cli/subcommands/import_cmd.py +31 -0
  671. package/agent/hermes_cli/subcommands/insights.py +25 -0
  672. package/agent/hermes_cli/subcommands/login.py +78 -0
  673. package/agent/hermes_cli/subcommands/logout.py +28 -0
  674. package/agent/hermes_cli/subcommands/logs.py +78 -0
  675. package/agent/hermes_cli/subcommands/mcp.py +108 -0
  676. package/agent/hermes_cli/subcommands/memory.py +53 -0
  677. package/agent/hermes_cli/subcommands/model.py +72 -0
  678. package/agent/hermes_cli/subcommands/pairing.py +36 -0
  679. package/agent/hermes_cli/subcommands/plugins.py +94 -0
  680. package/agent/hermes_cli/subcommands/postinstall.py +23 -0
  681. package/agent/hermes_cli/subcommands/profile.py +203 -0
  682. package/agent/hermes_cli/subcommands/prompt_size.py +36 -0
  683. package/agent/hermes_cli/subcommands/security.py +62 -0
  684. package/agent/hermes_cli/subcommands/setup.py +58 -0
  685. package/agent/hermes_cli/subcommands/skills.py +298 -0
  686. package/agent/hermes_cli/subcommands/slack.py +60 -0
  687. package/agent/hermes_cli/subcommands/status.py +28 -0
  688. package/agent/hermes_cli/subcommands/tools.py +95 -0
  689. package/agent/hermes_cli/subcommands/uninstall.py +41 -0
  690. package/agent/hermes_cli/subcommands/update.py +70 -0
  691. package/agent/hermes_cli/subcommands/version.py +18 -0
  692. package/agent/hermes_cli/subcommands/webhook.py +76 -0
  693. package/agent/hermes_cli/subcommands/whatsapp.py +22 -0
  694. package/agent/hermes_cli/suggestions_cmd.py +153 -0
  695. package/agent/hermes_cli/telegram_managed_bot.py +358 -0
  696. package/agent/hermes_cli/tips.py +3 -4
  697. package/agent/hermes_cli/tools_config.py +155 -28
  698. package/agent/hermes_cli/uninstall.py +231 -35
  699. package/agent/hermes_cli/web_server.py +6188 -975
  700. package/agent/hermes_cli/win_pty_bridge.py +179 -0
  701. package/agent/hermes_cli/write_approval_commands.py +209 -0
  702. package/agent/hermes_constants.py +164 -33
  703. package/agent/hermes_logging.py +74 -2
  704. package/agent/hermes_state.py +919 -106
  705. package/agent/hermes_time.py +20 -0
  706. package/agent/locales/af.yaml +23 -0
  707. package/agent/locales/de.yaml +23 -0
  708. package/agent/locales/en.yaml +20 -0
  709. package/agent/locales/es.yaml +23 -0
  710. package/agent/locales/fr.yaml +23 -0
  711. package/agent/locales/ga.yaml +23 -0
  712. package/agent/locales/hu.yaml +23 -0
  713. package/agent/locales/it.yaml +23 -0
  714. package/agent/locales/ja.yaml +23 -0
  715. package/agent/locales/ko.yaml +23 -0
  716. package/agent/locales/pt.yaml +23 -0
  717. package/agent/locales/ru.yaml +23 -0
  718. package/agent/locales/tr.yaml +23 -0
  719. package/agent/locales/uk.yaml +23 -0
  720. package/agent/locales/zh-hant.yaml +23 -0
  721. package/agent/locales/zh.yaml +23 -0
  722. package/agent/model_tools.py +204 -40
  723. package/agent/optional-mcps/clawpump/manifest.yaml +4 -2
  724. package/agent/optional-mcps/clawpump-stdio/manifest.yaml +2 -0
  725. package/agent/optional-mcps/unreal-engine/manifest.yaml +54 -0
  726. package/agent/optional-skills/blockchain/hyperliquid/SKILL.md +2 -2
  727. package/agent/optional-skills/blockchain/hyperliquid/scripts/hyperliquid_client.py +1 -1
  728. package/agent/optional-skills/creative/kanban-video-orchestrator/SKILL.md +1 -1
  729. package/agent/optional-skills/creative/kanban-video-orchestrator/assets/setup.sh.tmpl +4 -3
  730. package/agent/optional-skills/creative/kanban-video-orchestrator/references/kanban-setup.md +6 -4
  731. package/agent/optional-skills/creative/kanban-video-orchestrator/references/tool-matrix.md +2 -2
  732. package/agent/{skills/software-development → optional-skills/devops}/hermes-s6-container-supervision/SKILL.md +2 -0
  733. package/agent/optional-skills/devops/watchers/SKILL.md +1 -1
  734. package/agent/optional-skills/devops/watchers/scripts/watch_github.py +2 -1
  735. package/agent/optional-skills/payments/mpp-agent/SKILL.md +124 -0
  736. package/agent/optional-skills/payments/stripe-link-cli/SKILL.md +184 -0
  737. package/agent/optional-skills/payments/stripe-projects/SKILL.md +120 -0
  738. package/agent/optional-skills/productivity/canvas/SKILL.md +1 -1
  739. package/agent/optional-skills/productivity/canvas/scripts/canvas_api.py +4 -1
  740. package/agent/optional-skills/productivity/shop/SKILL.md +224 -0
  741. package/agent/optional-skills/productivity/shop/references/catalog-mcp.md +236 -0
  742. package/agent/optional-skills/productivity/shop/references/direct-api.md +278 -0
  743. package/agent/optional-skills/productivity/shop/references/legal.md +3 -0
  744. package/agent/optional-skills/productivity/shop/references/safety.md +36 -0
  745. package/agent/optional-skills/productivity/shopify/SKILL.md +1 -1
  746. package/agent/optional-skills/productivity/siyuan/SKILL.md +1 -1
  747. package/agent/optional-skills/productivity/telephony/SKILL.md +4 -4
  748. package/agent/optional-skills/productivity/telephony/scripts/telephony.py +15 -15
  749. package/agent/optional-skills/security/1password/SKILL.md +1 -1
  750. package/agent/{skills/red-teaming → optional-skills/security}/godmode/SKILL.md +3 -4
  751. package/agent/{skills/red-teaming → optional-skills/security}/godmode/scripts/auto_jailbreak.py +3 -1
  752. package/agent/optional-skills/software-development/rest-graphql-debug/SKILL.md +1 -1
  753. package/agent/{skills → optional-skills}/software-development/subagent-driven-development/SKILL.md +5 -5
  754. package/agent/package-lock.json +4082 -7907
  755. package/agent/package.json +18 -3
  756. package/agent/plugins/browser/firecrawl/provider.py +4 -1
  757. package/agent/plugins/cron/__init__.py +344 -0
  758. package/agent/plugins/cron/chronos/__init__.py +241 -0
  759. package/agent/plugins/cron/chronos/_nas_client.py +123 -0
  760. package/agent/plugins/cron/chronos/plugin.yaml +9 -0
  761. package/agent/plugins/cron/chronos/verify.py +103 -0
  762. package/agent/plugins/dashboard_auth/basic/__init__.py +491 -0
  763. package/agent/plugins/dashboard_auth/basic/plugin.yaml +7 -0
  764. package/agent/plugins/dashboard_auth/nous/__init__.py +12 -14
  765. package/agent/plugins/dashboard_auth/self_hosted/__init__.py +736 -0
  766. package/agent/plugins/dashboard_auth/self_hosted/plugin.yaml +8 -0
  767. package/agent/plugins/disk-cleanup/disk_cleanup.py +100 -20
  768. package/agent/plugins/google_meet/audio_bridge.py +4 -0
  769. package/agent/plugins/google_meet/meet_bot.py +7 -1
  770. package/agent/plugins/hermes-achievements/dashboard/dist/index.js +9 -15
  771. package/agent/plugins/image_gen/fal/__init__.py +35 -6
  772. package/agent/plugins/image_gen/krea/__init__.py +56 -13
  773. package/agent/plugins/image_gen/openai/__init__.py +122 -24
  774. package/agent/plugins/image_gen/openai-codex/__init__.py +28 -2
  775. package/agent/plugins/image_gen/xai/__init__.py +92 -12
  776. package/agent/plugins/kanban/dashboard/dist/index.js +63 -48
  777. package/agent/plugins/kanban/dashboard/plugin_api.py +39 -35
  778. package/agent/plugins/memory/__init__.py +48 -5
  779. package/agent/plugins/memory/byterover/__init__.py +1 -0
  780. package/agent/plugins/memory/hindsight/README.md +1 -1
  781. package/agent/plugins/memory/hindsight/__init__.py +138 -24
  782. package/agent/plugins/memory/hindsight/plugin.yaml +1 -1
  783. package/agent/plugins/memory/honcho/README.md +13 -10
  784. package/agent/plugins/memory/honcho/cli.py +247 -122
  785. package/agent/plugins/memory/honcho/client.py +112 -102
  786. package/agent/plugins/memory/openviking/README.md +12 -1
  787. package/agent/plugins/memory/openviking/__init__.py +2281 -107
  788. package/agent/plugins/memory/openviking/plugin.yaml +1 -2
  789. package/agent/plugins/memory/supermemory/README.md +22 -10
  790. package/agent/plugins/memory/supermemory/__init__.py +142 -37
  791. package/agent/plugins/memory/supermemory/plugin.yaml +1 -1
  792. package/agent/plugins/model-providers/anthropic/__init__.py +1 -0
  793. package/agent/plugins/model-providers/bedrock/__init__.py +1 -0
  794. package/agent/plugins/model-providers/copilot-acp/__init__.py +1 -0
  795. package/agent/plugins/model-providers/custom/__init__.py +8 -2
  796. package/agent/plugins/model-providers/kimi-coding/__init__.py +16 -7
  797. package/agent/plugins/model-providers/minimax/__init__.py +60 -8
  798. package/agent/plugins/model-providers/opencode-zen/__init__.py +12 -3
  799. package/agent/plugins/model-providers/openrouter/__init__.py +75 -4
  800. package/agent/plugins/model-providers/xiaomi/__init__.py +2 -0
  801. package/agent/plugins/model-providers/zai/__init__.py +1 -0
  802. package/agent/plugins/observability/langfuse/__init__.py +147 -14
  803. package/agent/plugins/observability/nemo_relay/README.md +559 -0
  804. package/agent/plugins/observability/nemo_relay/__init__.py +962 -0
  805. package/agent/plugins/observability/nemo_relay/plugin.yaml +20 -0
  806. package/agent/plugins/platforms/discord/adapter.py +932 -61
  807. package/agent/plugins/platforms/discord/voice_mixer.py +379 -0
  808. package/agent/plugins/platforms/google_chat/adapter.py +9 -3
  809. package/agent/plugins/platforms/google_chat/oauth.py +1 -1
  810. package/agent/plugins/platforms/homeassistant/__init__.py +3 -0
  811. package/agent/{gateway/platforms/homeassistant.py → plugins/platforms/homeassistant/adapter.py} +128 -0
  812. package/agent/plugins/platforms/homeassistant/plugin.yaml +22 -0
  813. package/agent/plugins/platforms/irc/adapter.py +4 -1
  814. package/agent/plugins/platforms/line/adapter.py +16 -1
  815. package/agent/plugins/platforms/mattermost/adapter.py +100 -24
  816. package/agent/plugins/platforms/photon/README.md +179 -0
  817. package/agent/plugins/platforms/photon/__init__.py +4 -0
  818. package/agent/plugins/platforms/photon/adapter.py +1586 -0
  819. package/agent/plugins/platforms/photon/auth.py +1046 -0
  820. package/agent/plugins/platforms/photon/cli.py +439 -0
  821. package/agent/plugins/platforms/photon/plugin.yaml +88 -0
  822. package/agent/plugins/platforms/photon/sidecar/README.md +52 -0
  823. package/agent/plugins/platforms/photon/sidecar/index.mjs +720 -0
  824. package/agent/plugins/platforms/photon/sidecar/package-lock.json +1730 -0
  825. package/agent/plugins/platforms/photon/sidecar/package.json +25 -0
  826. package/agent/plugins/platforms/photon/sidecar/patch-spectrum-mixed-attachments.mjs +155 -0
  827. package/agent/plugins/platforms/raft/__init__.py +3 -0
  828. package/agent/plugins/platforms/raft/adapter.py +774 -0
  829. package/agent/plugins/platforms/raft/plugin.yaml +19 -0
  830. package/agent/plugins/platforms/simplex/adapter.py +777 -220
  831. package/agent/plugins/platforms/simplex/plugin.yaml +21 -2
  832. package/agent/plugins/platforms/teams/adapter.py +175 -5
  833. package/agent/plugins/plugin_utils.py +135 -0
  834. package/agent/plugins/video_gen/fal/__init__.py +10 -3
  835. package/agent/plugins/web/searxng/provider.py +15 -2
  836. package/agent/plugins/web/xai/provider.py +2 -2
  837. package/agent/providers/base.py +22 -3
  838. package/agent/pyproject.toml +115 -21
  839. package/agent/run_agent.py +733 -39
  840. package/agent/scripts/build_skills_index.py +51 -19
  841. package/agent/scripts/check_subprocess_stdin.py +177 -0
  842. package/agent/scripts/contributor_audit.py +2 -0
  843. package/agent/scripts/docker_config_migrate.py +67 -0
  844. package/agent/scripts/install.cmd +3 -3
  845. package/agent/scripts/install.ps1 +580 -154
  846. package/agent/scripts/install.sh +402 -185
  847. package/agent/scripts/lib/node-bootstrap.sh +39 -4
  848. package/agent/scripts/release.py +183 -0
  849. package/agent/scripts/run_tests.sh +1 -0
  850. package/agent/scripts/run_tests_parallel.py +18 -23
  851. package/agent/scripts/whatsapp-bridge/bridge.js +25 -4
  852. package/agent/setup.py +59 -0
  853. package/agent/skills/autonomous-ai-agents/codex/SKILL.md +19 -0
  854. package/agent/skills/autonomous-ai-agents/hermes-agent/SKILL.md +10 -3
  855. package/agent/skills/{mcp/native-mcp/SKILL.md → autonomous-ai-agents/hermes-agent/references/native-mcp.md} +0 -13
  856. package/agent/skills/{devops/webhook-subscriptions/SKILL.md → autonomous-ai-agents/hermes-agent/references/webhooks.md} +1 -11
  857. package/agent/skills/clawpump/SKILL.md +3 -1
  858. package/agent/skills/devops/kanban-orchestrator/SKILL.md +1 -0
  859. package/agent/skills/devops/kanban-worker/SKILL.md +1 -0
  860. package/agent/skills/github/github-auth/SKILL.md +2 -2
  861. package/agent/skills/github/github-auth/scripts/gh-env.sh +2 -2
  862. package/agent/skills/github/github-code-review/SKILL.md +2 -2
  863. package/agent/skills/github/github-issues/SKILL.md +2 -2
  864. package/agent/skills/github/github-pr-workflow/SKILL.md +2 -2
  865. package/agent/skills/github/github-repo-management/SKILL.md +2 -2
  866. package/agent/skills/media/gif-search/SKILL.md +1 -1
  867. package/agent/skills/media/youtube-content/SKILL.md +10 -7
  868. package/agent/skills/media/youtube-content/scripts/fetch_transcript.py +3 -3
  869. package/agent/skills/note-taking/obsidian/SKILL.md +1 -1
  870. package/agent/skills/productivity/airtable/SKILL.md +2 -2
  871. package/agent/skills/productivity/google-workspace/scripts/setup.py +33 -7
  872. package/agent/skills/productivity/notion/SKILL.md +2 -2
  873. package/agent/skills/productivity/teams-meeting-pipeline/SKILL.md +1 -1
  874. package/agent/skills/research/llm-wiki/SKILL.md +1 -1
  875. package/agent/skills/social-media/xurl/SKILL.md +9 -0
  876. package/agent/skills/software-development/hermes-agent-skill-authoring/SKILL.md +1 -1
  877. package/agent/skills/software-development/plan/SKILL.md +285 -5
  878. package/agent/skills/software-development/requesting-code-review/SKILL.md +2 -2
  879. package/agent/skills/software-development/simplify-code/SKILL.md +212 -0
  880. package/agent/skills/software-development/spike/SKILL.md +2 -2
  881. package/agent/skills/software-development/systematic-debugging/SKILL.md +1 -1
  882. package/agent/skills/software-development/test-driven-development/SKILL.md +1 -1
  883. package/agent/tools/approval.py +302 -4
  884. package/agent/tools/async_delegation.py +386 -0
  885. package/agent/tools/blueprints.py +325 -0
  886. package/agent/tools/browser_cdp_tool.py +3 -3
  887. package/agent/tools/browser_tool.py +34 -6
  888. package/agent/tools/checkpoint_manager.py +31 -1
  889. package/agent/tools/clarify_tool.py +55 -5
  890. package/agent/tools/code_execution_tool.py +31 -14
  891. package/agent/tools/computer_use/cua_backend.py +81 -3
  892. package/agent/tools/computer_use/tool.py +79 -5
  893. package/agent/tools/computer_use/vision_routing.py +55 -3
  894. package/agent/tools/credential_files.py +31 -12
  895. package/agent/tools/cronjob_tools.py +30 -20
  896. package/agent/tools/delegate_tool.py +356 -31
  897. package/agent/tools/env_probe.py +1 -0
  898. package/agent/tools/environments/docker.py +163 -8
  899. package/agent/tools/environments/file_sync.py +2 -1
  900. package/agent/tools/environments/local.py +74 -23
  901. package/agent/tools/environments/singularity.py +4 -1
  902. package/agent/tools/environments/ssh.py +78 -11
  903. package/agent/tools/file_operations.py +277 -41
  904. package/agent/tools/file_tools.py +166 -28
  905. package/agent/tools/image_generation_tool.py +515 -29
  906. package/agent/tools/kanban_tools.py +99 -0
  907. package/agent/tools/lazy_deps.py +33 -2
  908. package/agent/tools/mcp_oauth.py +5 -5
  909. package/agent/tools/mcp_oauth_manager.py +7 -5
  910. package/agent/tools/mcp_tool.py +840 -33
  911. package/agent/tools/memory_tool.py +335 -38
  912. package/agent/tools/osv_check.py +15 -1
  913. package/agent/tools/process_registry.py +155 -11
  914. package/agent/tools/read_extract.py +248 -0
  915. package/agent/tools/read_terminal_tool.py +93 -0
  916. package/agent/tools/schema_sanitizer.py +38 -0
  917. package/agent/tools/send_message_tool.py +163 -49
  918. package/agent/tools/session_search_tool.py +189 -7
  919. package/agent/tools/skill_manager_tool.py +202 -3
  920. package/agent/tools/skill_usage.py +52 -4
  921. package/agent/tools/skills_hub.py +184 -44
  922. package/agent/tools/skills_sync.py +232 -5
  923. package/agent/tools/skills_tool.py +125 -11
  924. package/agent/tools/terminal_tool.py +148 -26
  925. package/agent/tools/tirith_security.py +2 -0
  926. package/agent/tools/todo_tool.py +32 -1
  927. package/agent/tools/transcription_tools.py +13 -5
  928. package/agent/tools/tts_tool.py +332 -38
  929. package/agent/tools/url_safety.py +52 -1
  930. package/agent/tools/vision_tools.py +124 -39
  931. package/agent/tools/voice_mode.py +4 -3
  932. package/agent/tools/web_tools.py +45 -15
  933. package/agent/tools/write_approval.py +493 -0
  934. package/agent/toolsets.py +34 -10
  935. package/agent/trajectory_compressor.py +81 -10
  936. package/agent/tui_gateway/entry.py +43 -6
  937. package/agent/tui_gateway/server.py +3335 -330
  938. package/agent/tui_gateway/slash_worker.py +61 -0
  939. package/agent/tui_gateway/ws.py +67 -9
  940. package/agent/ui-tui/eslint.config.mjs +0 -4
  941. package/agent/ui-tui/package.json +6 -6
  942. package/agent/ui-tui/packages/hermes-ink/package.json +1 -1
  943. package/agent/ui-tui/packages/hermes-ink/src/ink/app-mouse.test.ts +34 -1
  944. package/agent/ui-tui/packages/hermes-ink/src/ink/app-rawmode-mouse.test.ts +91 -0
  945. package/agent/ui-tui/packages/hermes-ink/src/ink/components/App.tsx +35 -2
  946. package/agent/ui-tui/packages/hermes-ink/src/ink/events/input-event.ts +4 -11
  947. package/agent/ui-tui/packages/hermes-ink/src/ink/parse-keypress.test.ts +23 -57
  948. package/agent/ui-tui/packages/hermes-ink/src/ink/parse-keypress.ts +11 -135
  949. package/agent/ui-tui/packages/hermes-ink/src/ink/termio/tokenize.test.ts +185 -0
  950. package/agent/ui-tui/packages/hermes-ink/src/ink/termio/tokenize.ts +37 -3
  951. package/agent/ui-tui/packages/hermes-ink/src/utils/execFileNoThrow.ts +5 -5
  952. package/agent/ui-tui/src/__tests__/appChromeStatusRule.test.tsx +217 -0
  953. package/agent/ui-tui/src/__tests__/appChromeStatusRuleDevCredits.test.tsx +73 -0
  954. package/agent/ui-tui/src/__tests__/approvalAction.test.ts +11 -0
  955. package/agent/ui-tui/src/__tests__/billingCommand.test.ts +301 -0
  956. package/agent/ui-tui/src/__tests__/blockLayout.test.ts +122 -0
  957. package/agent/ui-tui/src/__tests__/brandingMcpCount.test.ts +111 -0
  958. package/agent/ui-tui/src/__tests__/completionApply.test.ts +51 -0
  959. package/agent/ui-tui/src/__tests__/createGatewayEventHandler.test.ts +487 -2
  960. package/agent/ui-tui/src/__tests__/createSlashHandler.test.ts +54 -0
  961. package/agent/ui-tui/src/__tests__/creditsCommand.test.ts +144 -0
  962. package/agent/ui-tui/src/__tests__/gatewayClient.test.ts +120 -99
  963. package/agent/ui-tui/src/__tests__/gracefulExit.test.ts +11 -0
  964. package/agent/ui-tui/src/__tests__/memoryMonitor.test.ts +102 -0
  965. package/agent/ui-tui/src/__tests__/paths.test.ts +41 -1
  966. package/agent/ui-tui/src/__tests__/terminalModes.test.ts +22 -0
  967. package/agent/ui-tui/src/__tests__/text.test.ts +23 -0
  968. package/agent/ui-tui/src/__tests__/textInputFastEcho.test.ts +37 -0
  969. package/agent/ui-tui/src/__tests__/turnControllerNotice.test.ts +43 -0
  970. package/agent/ui-tui/src/__tests__/useInputHandlers.test.ts +38 -1
  971. package/agent/ui-tui/src/__tests__/virtualHeights.test.ts +8 -0
  972. package/agent/ui-tui/src/app/createGatewayEventHandler.ts +102 -7
  973. package/agent/ui-tui/src/app/interfaces.ts +64 -1
  974. package/agent/ui-tui/src/app/overlayStore.ts +18 -2
  975. package/agent/ui-tui/src/app/slash/commands/billing.ts +332 -0
  976. package/agent/ui-tui/src/app/slash/commands/core.ts +31 -2
  977. package/agent/ui-tui/src/app/slash/commands/credits.ts +57 -0
  978. package/agent/ui-tui/src/app/slash/commands/ops.ts +28 -0
  979. package/agent/ui-tui/src/app/slash/commands/session.ts +32 -4
  980. package/agent/ui-tui/src/app/slash/registry.ts +4 -0
  981. package/agent/ui-tui/src/app/turnController.ts +145 -2
  982. package/agent/ui-tui/src/app/uiStore.ts +2 -0
  983. package/agent/ui-tui/src/app/useInputHandlers.ts +42 -4
  984. package/agent/ui-tui/src/app/useMainApp.ts +54 -8
  985. package/agent/ui-tui/src/app/useSessionLifecycle.ts +40 -31
  986. package/agent/ui-tui/src/app/useSubmission.ts +23 -31
  987. package/agent/ui-tui/src/components/appChrome.tsx +112 -5
  988. package/agent/ui-tui/src/components/appLayout.tsx +9 -0
  989. package/agent/ui-tui/src/components/appOverlays.tsx +25 -1
  990. package/agent/ui-tui/src/components/billingOverlay.tsx +684 -0
  991. package/agent/ui-tui/src/components/branding.tsx +15 -3
  992. package/agent/ui-tui/src/components/messageLine.tsx +25 -3
  993. package/agent/ui-tui/src/components/pluginsHub.tsx +238 -0
  994. package/agent/ui-tui/src/components/prompts.tsx +31 -17
  995. package/agent/ui-tui/src/components/streamingAssistant.tsx +63 -55
  996. package/agent/ui-tui/src/components/textInput.tsx +16 -0
  997. package/agent/ui-tui/src/config/env.ts +12 -0
  998. package/agent/ui-tui/src/config/limits.ts +13 -0
  999. package/agent/ui-tui/src/domain/blockLayout.ts +146 -0
  1000. package/agent/ui-tui/src/domain/paths.ts +24 -0
  1001. package/agent/ui-tui/src/domain/slash.ts +40 -0
  1002. package/agent/ui-tui/src/entry.tsx +35 -4
  1003. package/agent/ui-tui/src/gatewayClient.ts +22 -10
  1004. package/agent/ui-tui/src/gatewayTypes.ts +130 -1
  1005. package/agent/ui-tui/src/lib/gracefulExit.ts +24 -4
  1006. package/agent/ui-tui/src/lib/memory.test.ts +162 -0
  1007. package/agent/ui-tui/src/lib/memory.ts +60 -1
  1008. package/agent/ui-tui/src/lib/memoryMonitor.ts +79 -4
  1009. package/agent/ui-tui/src/lib/osc52.ts +1 -1
  1010. package/agent/ui-tui/src/lib/text.test.ts +32 -1
  1011. package/agent/ui-tui/src/lib/text.ts +29 -2
  1012. package/agent/ui-tui/src/lib/virtualHeights.ts +13 -0
  1013. package/agent/ui-tui/src/types.ts +5 -0
  1014. package/agent/ui-tui/tsconfig.build.json +0 -1
  1015. package/agent/ui-tui/tsconfig.json +2 -1
  1016. package/agent/utils.py +66 -2
  1017. package/agent/uv.lock +300 -684
  1018. package/agent/web/index.html +2 -2
  1019. package/agent/web/package.json +11 -6
  1020. package/agent/web/public/claw-bg.webp +0 -0
  1021. package/agent/web/public/claw-logo.webp +0 -0
  1022. package/agent/web/src/App.tsx +138 -48
  1023. package/agent/web/src/components/AutomationBlueprints.tsx +225 -0
  1024. package/agent/web/src/components/Backdrop.tsx +15 -0
  1025. package/agent/web/src/components/ChatSessionList.tsx +260 -0
  1026. package/agent/web/src/components/ChatSidebar.tsx +262 -78
  1027. package/agent/web/src/components/ConfirmDialog.tsx +122 -0
  1028. package/agent/web/src/components/ModelPickerDialog.tsx +111 -16
  1029. package/agent/web/src/components/ModelReloadConfirm.tsx +40 -0
  1030. package/agent/web/src/components/ProfileScopeBanner.tsx +30 -0
  1031. package/agent/web/src/components/ProfileSwitcher.tsx +67 -0
  1032. package/agent/web/src/components/ReasoningPicker.tsx +167 -0
  1033. package/agent/web/src/components/SkillEditorDialog.tsx +215 -0
  1034. package/agent/web/src/components/ThemeSwitcher.tsx +119 -4
  1035. package/agent/web/src/components/ToolsetConfigDrawer.tsx +457 -0
  1036. package/agent/web/src/contexts/PageHeaderProvider.tsx +7 -4
  1037. package/agent/web/src/contexts/ProfileProvider.tsx +137 -0
  1038. package/agent/web/src/contexts/SystemActions.tsx +6 -8
  1039. package/agent/web/src/contexts/profile-context.ts +19 -0
  1040. package/agent/web/src/contexts/useProfileScope.ts +6 -0
  1041. package/agent/web/src/i18n/af.ts +5 -4
  1042. package/agent/web/src/i18n/de.ts +5 -4
  1043. package/agent/web/src/i18n/en.ts +58 -4
  1044. package/agent/web/src/i18n/es.ts +5 -3
  1045. package/agent/web/src/i18n/fr.ts +5 -3
  1046. package/agent/web/src/i18n/ga.ts +5 -4
  1047. package/agent/web/src/i18n/hu.ts +5 -4
  1048. package/agent/web/src/i18n/it.ts +5 -4
  1049. package/agent/web/src/i18n/ja.ts +5 -4
  1050. package/agent/web/src/i18n/ko.ts +5 -4
  1051. package/agent/web/src/i18n/pt.ts +5 -3
  1052. package/agent/web/src/i18n/ru.ts +5 -4
  1053. package/agent/web/src/i18n/tr.ts +5 -4
  1054. package/agent/web/src/i18n/types.ts +59 -1
  1055. package/agent/web/src/i18n/uk.ts +5 -3
  1056. package/agent/web/src/i18n/zh-hant.ts +5 -4
  1057. package/agent/web/src/i18n/zh.ts +5 -4
  1058. package/agent/web/src/index.css +2 -2
  1059. package/agent/web/src/lib/api.ts +819 -52
  1060. package/agent/web/src/lib/dashboard-flags.ts +16 -7
  1061. package/agent/web/src/lib/reasoning-effort.test.ts +48 -0
  1062. package/agent/web/src/lib/reasoning-effort.ts +36 -0
  1063. package/agent/web/src/lib/session-refresh.test.ts +21 -0
  1064. package/agent/web/src/lib/session-refresh.ts +26 -0
  1065. package/agent/web/src/pages/ChannelsPage.tsx +529 -68
  1066. package/agent/web/src/pages/ChatPage.tsx +249 -56
  1067. package/agent/web/src/pages/ConfigPage.tsx +11 -1
  1068. package/agent/web/src/pages/CronPage.tsx +219 -31
  1069. package/agent/web/src/pages/EnvPage.tsx +25 -6
  1070. package/agent/web/src/pages/FilesPage.tsx +525 -0
  1071. package/agent/web/src/pages/McpPage.tsx +80 -3
  1072. package/agent/web/src/pages/ModelsPage.tsx +97 -12
  1073. package/agent/web/src/pages/PluginsPage.tsx +1 -1
  1074. package/agent/web/src/pages/ProfileBuilderPage.tsx +611 -0
  1075. package/agent/web/src/pages/ProfilesPage.tsx +1038 -172
  1076. package/agent/web/src/pages/SessionsPage.tsx +144 -13
  1077. package/agent/web/src/pages/SkillsPage.tsx +851 -70
  1078. package/agent/web/src/pages/SystemPage.tsx +340 -4
  1079. package/agent/web/src/pages/WalletPage.tsx +401 -0
  1080. package/agent/web/src/pages/WebhooksPage.tsx +145 -15
  1081. package/agent/web/src/pages/X402Page.tsx +207 -0
  1082. package/agent/web/src/plugins/registry.ts +28 -11
  1083. package/agent/web/src/plugins/sdk.d.ts +160 -0
  1084. package/agent/web/src/themes/context.tsx +112 -5
  1085. package/agent/web/src/themes/fonts.ts +167 -0
  1086. package/agent/web/src/themes/index.ts +7 -0
  1087. package/agent/web/tsconfig.app.json +0 -1
  1088. package/agent/web/vite.config.ts +1 -8
  1089. package/agent/web/vitest.config.ts +16 -0
  1090. package/package.json +1 -1
  1091. package/agent/apps/desktop/package-lock.json +0 -18363
  1092. package/agent/apps/desktop/src/app/chat/composer/skin-slash-popover.tsx +0 -56
  1093. package/agent/apps/desktop/src/components/assistant-ui/thread-virtualizer.tsx +0 -382
  1094. package/agent/apps/desktop/src/components/assistant-ui/todo-tool.tsx +0 -109
  1095. package/agent/apps/desktop/src/components/chat/generated-image-context.tsx +0 -19
  1096. package/agent/optional-skills/productivity/shop-app/SKILL.md +0 -340
  1097. package/agent/skills/autonomous-ai-agents/kanban-codex-lane/SKILL.md +0 -277
  1098. package/agent/skills/autonomous-ai-agents/kanban-codex-lane/templates/pmb-codex-lane-prompt.md +0 -57
  1099. package/agent/skills/diagramming/DESCRIPTION.md +0 -3
  1100. package/agent/skills/domain/DESCRIPTION.md +0 -24
  1101. package/agent/skills/gifs/DESCRIPTION.md +0 -3
  1102. package/agent/skills/inference-sh/DESCRIPTION.md +0 -19
  1103. package/agent/skills/mcp/DESCRIPTION.md +0 -3
  1104. package/agent/skills/media/spotify/SKILL.md +0 -135
  1105. package/agent/skills/mlops/training/DESCRIPTION.md +0 -3
  1106. package/agent/skills/mlops/vector-databases/DESCRIPTION.md +0 -3
  1107. package/agent/skills/productivity/linear/SKILL.md +0 -380
  1108. package/agent/skills/productivity/linear/scripts/linear_api.py +0 -445
  1109. package/agent/skills/software-development/debugging-hermes-tui-commands/SKILL.md +0 -152
  1110. package/agent/skills/software-development/writing-plans/SKILL.md +0 -297
  1111. package/agent/ui-tui/package-lock.json +0 -7449
  1112. package/agent/ui-tui/packages/hermes-ink/package-lock.json +0 -1289
  1113. package/agent/web/package-lock.json +0 -8887
  1114. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/PORT_NOTES.md +0 -0
  1115. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/SKILL.md +0 -0
  1116. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/prompts/system.md +0 -0
  1117. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/palettes/macaron.md +0 -0
  1118. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/palettes/mono-ink.md +0 -0
  1119. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/palettes/neon.md +0 -0
  1120. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/palettes/warm.md +0 -0
  1121. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/prompt-construction.md +0 -0
  1122. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/style-presets.md +0 -0
  1123. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/blueprint.md +0 -0
  1124. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/chalkboard.md +0 -0
  1125. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/editorial.md +0 -0
  1126. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/elegant.md +0 -0
  1127. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/fantasy-animation.md +0 -0
  1128. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/flat-doodle.md +0 -0
  1129. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/flat.md +0 -0
  1130. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/ink-notes.md +0 -0
  1131. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/intuition-machine.md +0 -0
  1132. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/minimal.md +0 -0
  1133. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/nature.md +0 -0
  1134. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/notion.md +0 -0
  1135. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/pixel-art.md +0 -0
  1136. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/playful.md +0 -0
  1137. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/retro.md +0 -0
  1138. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/scientific.md +0 -0
  1139. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/screen-print.md +0 -0
  1140. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/sketch-notes.md +0 -0
  1141. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/sketch.md +0 -0
  1142. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/vector-illustration.md +0 -0
  1143. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/vintage.md +0 -0
  1144. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/warm.md +0 -0
  1145. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/watercolor.md +0 -0
  1146. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles.md +0 -0
  1147. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/usage.md +0 -0
  1148. /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/workflow.md +0 -0
  1149. /package/agent/{skills → optional-skills}/creative/baoyu-comic/PORT_NOTES.md +0 -0
  1150. /package/agent/{skills → optional-skills}/creative/baoyu-comic/SKILL.md +0 -0
  1151. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/analysis-framework.md +0 -0
  1152. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/chalk.md +0 -0
  1153. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/ink-brush.md +0 -0
  1154. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/ligne-claire.md +0 -0
  1155. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/manga.md +0 -0
  1156. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/minimalist.md +0 -0
  1157. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/realistic.md +0 -0
  1158. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/auto-selection.md +0 -0
  1159. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/base-prompt.md +0 -0
  1160. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/character-template.md +0 -0
  1161. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/cinematic.md +0 -0
  1162. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/dense.md +0 -0
  1163. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/four-panel.md +0 -0
  1164. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/mixed.md +0 -0
  1165. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/splash.md +0 -0
  1166. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/standard.md +0 -0
  1167. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/webtoon.md +0 -0
  1168. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/ohmsha-guide.md +0 -0
  1169. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/partial-workflows.md +0 -0
  1170. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/presets/concept-story.md +0 -0
  1171. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/presets/four-panel.md +0 -0
  1172. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/presets/ohmsha.md +0 -0
  1173. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/presets/shoujo.md +0 -0
  1174. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/presets/wuxia.md +0 -0
  1175. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/storyboard-template.md +0 -0
  1176. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/action.md +0 -0
  1177. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/dramatic.md +0 -0
  1178. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/energetic.md +0 -0
  1179. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/neutral.md +0 -0
  1180. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/romantic.md +0 -0
  1181. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/vintage.md +0 -0
  1182. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/warm.md +0 -0
  1183. /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/workflow.md +0 -0
  1184. /package/agent/{skills → optional-skills}/creative/creative-ideation/SKILL.md +0 -0
  1185. /package/agent/{skills → optional-skills}/creative/creative-ideation/references/full-prompt-library.md +0 -0
  1186. /package/agent/{skills → optional-skills}/creative/pixel-art/ATTRIBUTION.md +0 -0
  1187. /package/agent/{skills → optional-skills}/creative/pixel-art/SKILL.md +0 -0
  1188. /package/agent/{skills → optional-skills}/creative/pixel-art/references/palettes.md +0 -0
  1189. /package/agent/{skills → optional-skills}/creative/pixel-art/scripts/__init__.py +0 -0
  1190. /package/agent/{skills → optional-skills}/creative/pixel-art/scripts/palettes.py +0 -0
  1191. /package/agent/{skills → optional-skills}/creative/pixel-art/scripts/pixel_art.py +0 -0
  1192. /package/agent/{skills → optional-skills}/creative/pixel-art/scripts/pixel_art_video.py +0 -0
  1193. /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/SKILL.md +0 -0
  1194. /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/references/analysis-modules.md +0 -0
  1195. /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/references/methods-guide.md +0 -0
  1196. /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/templates/abliteration-config.yaml +0 -0
  1197. /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/templates/analysis-study.yaml +0 -0
  1198. /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/templates/batch-abliteration.yaml +0 -0
  1199. /package/agent/{skills → optional-skills}/mlops/research/DESCRIPTION.md +0 -0
  1200. /package/agent/{skills → optional-skills}/mlops/research/dspy/SKILL.md +0 -0
  1201. /package/agent/{skills → optional-skills}/mlops/research/dspy/references/examples.md +0 -0
  1202. /package/agent/{skills → optional-skills}/mlops/research/dspy/references/modules.md +0 -0
  1203. /package/agent/{skills → optional-skills}/mlops/research/dspy/references/optimizers.md +0 -0
  1204. /package/agent/{skills/red-teaming → optional-skills/security}/godmode/references/jailbreak-templates.md +0 -0
  1205. /package/agent/{skills/red-teaming → optional-skills/security}/godmode/references/refusal-detection.md +0 -0
  1206. /package/agent/{skills/red-teaming → optional-skills/security}/godmode/scripts/godmode_race.py +0 -0
  1207. /package/agent/{skills/red-teaming → optional-skills/security}/godmode/scripts/load_godmode.py +0 -0
  1208. /package/agent/{skills/red-teaming → optional-skills/security}/godmode/scripts/parseltongue.py +0 -0
  1209. /package/agent/{skills/red-teaming → optional-skills/security}/godmode/templates/prefill-subtle.json +0 -0
  1210. /package/agent/{skills/red-teaming → optional-skills/security}/godmode/templates/prefill.json +0 -0
  1211. /package/agent/{skills → optional-skills}/software-development/subagent-driven-development/references/context-budget-discipline.md +0 -0
  1212. /package/agent/{skills → optional-skills}/software-development/subagent-driven-development/references/gates-taxonomy.md +0 -0
@@ -102,7 +102,7 @@ OpenAI = _OpenAIProxy() # module-level name, resolves lazily on call/isinstance
102
102
  from agent.credential_pool import load_pool
103
103
  from hermes_cli.config import get_hermes_home
104
104
  from hermes_constants import OPENROUTER_BASE_URL
105
- from utils import base_url_host_matches, base_url_hostname, normalize_proxy_env_vars
105
+ from utils import base_url_host_matches, base_url_hostname, model_forces_max_completion_tokens, normalize_proxy_env_vars
106
106
 
107
107
  logger = logging.getLogger(__name__)
108
108
 
@@ -202,6 +202,35 @@ def _is_arcee_trinity_thinking(model: Optional[str]) -> bool:
202
202
  return bare == "trinity-large-thinking"
203
203
 
204
204
 
205
+ # Context window enforced by ChatGPT's Codex OAuth backend for gpt-5.5.
206
+ # The raw OpenAI API and OpenRouter expose 1.05M for the same slug, but the
207
+ # Codex backend hard-caps at 272K (verified live: a ~330K-token request to
208
+ # chatgpt.com/backend-api/codex/responses is rejected with
209
+ # ``context_length_exceeded`` while ~250K succeeds). With a 272K ceiling the
210
+ # default 50% compaction trigger fires at ~136K — wasteful, since the model
211
+ # can hold far more raw context before summarization actually buys anything.
212
+ # We raise the trigger to 85% (~231K) on this exact route so Codex gpt-5.5
213
+ # sessions use the window they actually have.
214
+ _CODEX_GPT55_COMPACTION_THRESHOLD = 0.85
215
+
216
+
217
+ def _is_codex_gpt55(model: Optional[str], provider: Optional[str] = None) -> bool:
218
+ """True for gpt-5.5 accessed through the ChatGPT Codex OAuth backend.
219
+
220
+ Matches only the Codex OAuth route (provider ``openai-codex``), not the
221
+ direct OpenAI API, OpenRouter, or GitHub Copilot paths — those expose a
222
+ larger context window for the same slug and must keep the user's default
223
+ compaction threshold. ``gpt-5.5-pro`` and dated snapshots
224
+ (``gpt-5.5-2026-04-23``) are matched via prefix so the override tracks the
225
+ family without re-listing every variant.
226
+ """
227
+ prov = (provider or "").strip().lower()
228
+ if prov != "openai-codex":
229
+ return False
230
+ bare = (model or "").strip().lower().rsplit("/", 1)[-1]
231
+ return bare == "gpt-5.5" or bare.startswith("gpt-5.5-") or bare.startswith("gpt-5.5.")
232
+
233
+
205
234
  def _fixed_temperature_for_model(
206
235
  model: Optional[str],
207
236
  base_url: Optional[str] = None,
@@ -224,18 +253,32 @@ def _fixed_temperature_for_model(
224
253
  return None
225
254
 
226
255
 
227
- def _compression_threshold_for_model(model: Optional[str]) -> Optional[float]:
256
+ def _compression_threshold_for_model(
257
+ model: Optional[str],
258
+ provider: Optional[str] = None,
259
+ *,
260
+ allow_codex_gpt55_autoraise: bool = True,
261
+ ) -> Optional[float]:
228
262
  """Return a context-compression threshold override for specific models.
229
263
 
230
264
  The threshold is the fraction of the model's context window that must be
231
265
  consumed before Hermes triggers summarization. Higher values delay
232
266
  compression and preserve more raw context.
233
267
 
268
+ Per-model/route overrides:
269
+ - Arcee Trinity Large Thinking → 0.75 (preserve reasoning context).
270
+ - gpt-5.5 on the Codex OAuth route → 0.85, because Codex caps the window
271
+ at 272K and the default 50% trigger would compact at ~136K. Gated by
272
+ ``allow_codex_gpt55_autoraise`` so the user can opt back down to the
273
+ global default (the caller passes the config flag through here).
274
+
234
275
  Returns a float in (0, 1] to override the global ``compression.threshold``
235
276
  config value, or ``None`` to leave the user's config value unchanged.
236
277
  """
237
278
  if _is_arcee_trinity_thinking(model):
238
279
  return 0.75
280
+ if allow_codex_gpt55_autoraise and _is_codex_gpt55(model, provider):
281
+ return _CODEX_GPT55_COMPACTION_THRESHOLD
239
282
  return None
240
283
 
241
284
  # Default auxiliary models for direct API-key providers (cheap/fast for side tasks)
@@ -265,9 +308,6 @@ _API_KEY_PROVIDER_AUX_MODELS_FALLBACK: Dict[str, str] = {
265
308
  "stepfun": "step-3.5-flash",
266
309
  "kimi-coding-cn": "kimi-k2-turbo-preview",
267
310
  "gmi": "google/gemini-3.1-flash-lite-preview",
268
- "minimax": "MiniMax-M2.7",
269
- "minimax-oauth": "MiniMax-M2.7-highspeed",
270
- "minimax-cn": "MiniMax-M2.7",
271
311
  "anthropic": "claude-haiku-4-5-20251001",
272
312
  "opencode-zen": "gemini-3-flash",
273
313
  "opencode-go": "glm-5",
@@ -317,6 +357,35 @@ _OR_HEADERS_BASE = {
317
357
  _TRUTHY_ENV_VALUES = frozenset({"1", "true", "yes", "on"})
318
358
 
319
359
 
360
+ def _apply_user_default_headers(headers: dict | None) -> dict | None:
361
+ """Merge user-configured ``model.default_headers`` onto resolved headers.
362
+
363
+ User values take precedence over provider/SDK defaults, mirroring the main
364
+ agent client (``AIAgent._apply_user_default_headers``). This lets a
365
+ ``custom`` OpenAI-compatible endpoint behind a gateway/WAF that rejects the
366
+ OpenAI SDK's identifying headers (``User-Agent: OpenAI/Python ...``,
367
+ ``X-Stainless-*``) override them for auxiliary calls too — otherwise the
368
+ main turn would succeed but title/compression/vision calls to the same
369
+ endpoint would still fail. (#40033)
370
+
371
+ Returns the merged dict, or the original ``headers`` (possibly ``None``)
372
+ when nothing is configured. No allocation when there are no overrides.
373
+ """
374
+ try:
375
+ from hermes_cli.config import cfg_get, load_config
376
+ user_headers = cfg_get(load_config(), "model", "default_headers")
377
+ except Exception:
378
+ return headers
379
+ if not isinstance(user_headers, dict) or not user_headers:
380
+ return headers
381
+ merged = dict(headers or {})
382
+ for key, value in user_headers.items():
383
+ if value is None:
384
+ continue
385
+ merged[str(key)] = str(value)
386
+ return merged or headers
387
+
388
+
320
389
  def build_or_headers(or_config: dict | None = None) -> dict:
321
390
  """Build OpenRouter headers, optionally including response-cache headers.
322
391
 
@@ -568,54 +637,6 @@ def _pool_runtime_base_url(entry: Any, fallback: str = "") -> str:
568
637
  # calls to the Codex Responses API so callers don't need any changes.
569
638
 
570
639
 
571
- def _convert_content_for_responses(content: Any) -> Any:
572
- """Convert chat.completions content to Responses API format.
573
-
574
- chat.completions uses:
575
- {"type": "text", "text": "..."}
576
- {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}}
577
-
578
- Responses API uses:
579
- {"type": "input_text", "text": "..."}
580
- {"type": "input_image", "image_url": "data:image/png;base64,..."}
581
-
582
- If content is a plain string, it's returned as-is (the Responses API
583
- accepts strings directly for text-only messages).
584
- """
585
- if isinstance(content, str):
586
- return content
587
- if not isinstance(content, list):
588
- return str(content) if content else ""
589
-
590
- converted: List[Dict[str, Any]] = []
591
- for part in content:
592
- if not isinstance(part, dict):
593
- continue
594
- ptype = part.get("type", "")
595
- if ptype == "text":
596
- converted.append({"type": "input_text", "text": part.get("text", "")})
597
- elif ptype == "image_url":
598
- # chat.completions nests the URL: {"image_url": {"url": "..."}}
599
- image_data = part.get("image_url", {})
600
- url = image_data.get("url", "") if isinstance(image_data, dict) else str(image_data)
601
- entry: Dict[str, Any] = {"type": "input_image", "image_url": url}
602
- # Preserve detail if specified
603
- detail = image_data.get("detail") if isinstance(image_data, dict) else None
604
- if detail:
605
- entry["detail"] = detail
606
- converted.append(entry)
607
- elif ptype in {"input_text", "input_image"}:
608
- # Already in Responses format — pass through
609
- converted.append(part)
610
- else:
611
- # Unknown content type — try to preserve as text
612
- text = part.get("text", "")
613
- if text:
614
- converted.append({"type": "input_text", "text": text})
615
-
616
- return converted or ""
617
-
618
-
619
640
  class _CodexCompletionsAdapter:
620
641
  """Drop-in shim that accepts chat.completions.create() kwargs and
621
642
  routes them through the Codex Responses streaming API."""
@@ -628,26 +649,37 @@ class _CodexCompletionsAdapter:
628
649
  messages = kwargs.get("messages", [])
629
650
  model = kwargs.get("model", self._model)
630
651
 
631
- # Separate system/instructions from conversation messages.
632
- # Convert chat.completions multimodal content blocks to Responses
633
- # API format (input_text / input_image instead of text / image_url).
652
+ # Separate system/instructions from replayable conversation messages,
653
+ # then route the rest through the SINGLE shared chat->Responses
654
+ # converter used by the main agent transport
655
+ # (agent/transports/codex.py). Maintaining a private conversion loop
656
+ # here let chat-style messages with role="tool" leak straight into
657
+ # Responses input[] — which the Responses API rejects with
658
+ # "Invalid value: 'tool'. Supported values are: 'assistant', 'system',
659
+ # 'developer', and 'user'." (issue #5709, hit hard by flush_memories()
660
+ # / compression replaying real session history that includes assistant
661
+ # tool_calls + role="tool" results). The shared converter encodes
662
+ # assistant tool calls as `function_call` items and tool results as
663
+ # `function_call_output` items with a valid call_id, so every
664
+ # Responses path normalizes tool history identically and cannot drift.
665
+ from agent.codex_responses_adapter import _chat_messages_to_responses_input
666
+
634
667
  instructions = "You are a helpful assistant."
635
- input_msgs: List[Dict[str, Any]] = []
668
+ replay_messages: List[Dict[str, Any]] = []
636
669
  for msg in messages:
637
670
  role = msg.get("role", "user")
638
671
  content = msg.get("content") or ""
639
672
  if role == "system":
640
673
  instructions = content if isinstance(content, str) else str(content)
641
674
  else:
642
- input_msgs.append({
643
- "role": role,
644
- "content": _convert_content_for_responses(content),
645
- })
675
+ replay_messages.append(msg)
676
+
677
+ input_items = _chat_messages_to_responses_input(replay_messages)
646
678
 
647
679
  resp_kwargs: Dict[str, Any] = {
648
680
  "model": model,
649
681
  "instructions": instructions,
650
- "input": input_msgs or [{"role": "user", "content": ""}],
682
+ "input": input_items or [{"role": "user", "content": ""}],
651
683
  "store": False,
652
684
  }
653
685
 
@@ -965,7 +997,7 @@ class _AnthropicCompletionsAdapter:
965
997
  self._is_oauth = is_oauth
966
998
 
967
999
  def create(self, **kwargs) -> Any:
968
- from agent.anthropic_adapter import build_anthropic_kwargs
1000
+ from agent.anthropic_adapter import build_anthropic_kwargs, create_anthropic_message
969
1001
  from agent.transports import get_transport
970
1002
 
971
1003
  messages = kwargs.get("messages", [])
@@ -1009,7 +1041,7 @@ class _AnthropicCompletionsAdapter:
1009
1041
  if not _forbids_sampling_params(model):
1010
1042
  anthropic_kwargs["temperature"] = temperature
1011
1043
 
1012
- response = self._client.messages.create(**anthropic_kwargs)
1044
+ response = create_anthropic_message(self._client, anthropic_kwargs)
1013
1045
  _transport = get_transport("anthropic_messages")
1014
1046
  _nr = _transport.normalize_response(
1015
1047
  response, strip_tool_prefix=self._is_oauth
@@ -1112,7 +1144,8 @@ def _endpoint_speaks_anthropic_messages(base_url: str) -> bool:
1112
1144
  normalized = (base_url or "").strip().lower().rstrip("/")
1113
1145
  if not normalized:
1114
1146
  return False
1115
- if normalized.endswith("/anthropic"):
1147
+ path = urlparse(normalized).path.rstrip("/")
1148
+ if path.endswith("/anthropic") or path.endswith("/anthropic/v1"):
1116
1149
  return True
1117
1150
  hostname = base_url_hostname(normalized)
1118
1151
  if hostname == "api.anthropic.com":
@@ -1455,6 +1488,9 @@ def _resolve_api_key_provider() -> Tuple[Optional[OpenAI], Optional[str]]:
1455
1488
  extra["default_headers"] = dict(_ph_aux.default_headers)
1456
1489
  except Exception:
1457
1490
  pass
1491
+ _merged_aux = _apply_user_default_headers(extra.get("default_headers"))
1492
+ if _merged_aux:
1493
+ extra["default_headers"] = _merged_aux
1458
1494
  _client = OpenAI(api_key=api_key, base_url=base_url, **extra)
1459
1495
  _client = _maybe_wrap_anthropic(_client, model, api_key, raw_base_url)
1460
1496
  return _client, model
@@ -1492,6 +1528,9 @@ def _resolve_api_key_provider() -> Tuple[Optional[OpenAI], Optional[str]]:
1492
1528
  extra["default_headers"] = dict(_ph_aux2.default_headers)
1493
1529
  except Exception:
1494
1530
  pass
1531
+ _merged_aux2 = _apply_user_default_headers(extra.get("default_headers"))
1532
+ if _merged_aux2:
1533
+ extra["default_headers"] = _merged_aux2
1495
1534
  _client = OpenAI(api_key=api_key, base_url=base_url, **extra)
1496
1535
  _client = _maybe_wrap_anthropic(_client, model, api_key, raw_base_url)
1497
1536
  return _client, model
@@ -1621,6 +1660,47 @@ def _try_nous(vision: bool = False) -> Tuple[Optional[OpenAI], Optional[str]]:
1621
1660
  )
1622
1661
 
1623
1662
 
1663
+ def _refresh_nous_recommended_model(
1664
+ *, vision: bool, stale_model: Optional[str]
1665
+ ) -> Optional[str]:
1666
+ """Re-fetch the Nous Portal's recommended model after a stale-model 404.
1667
+
1668
+ Long-lived processes (gateway, watchers) cache the Portal's
1669
+ ``recommended-models`` payload for 10 minutes and, in practice, can pin a
1670
+ model for the whole process lifetime. When that model is later dropped from
1671
+ the Nous → OpenRouter catalog, every auxiliary call 404s with
1672
+ "model does not exist". This forces a fresh Portal fetch and returns a
1673
+ model name to retry with:
1674
+
1675
+ * the Portal's current recommendation for the task, if it differs from
1676
+ the model that just failed; otherwise
1677
+ * ``_NOUS_MODEL`` (google/gemini-3-flash-preview), the known-good default,
1678
+ if it too differs from the failed model.
1679
+
1680
+ Returns ``None`` when no usable alternative is available (e.g. the Portal
1681
+ still recommends the exact model that just 404'd and the default also
1682
+ matches it) — callers should then let the original error propagate.
1683
+ """
1684
+ stale = (stale_model or "").strip().lower()
1685
+ fresh: Optional[str] = None
1686
+ try:
1687
+ from hermes_cli.models import get_nous_recommended_aux_model
1688
+
1689
+ fresh = get_nous_recommended_aux_model(vision=vision, force_refresh=True)
1690
+ except Exception as exc:
1691
+ logger.debug(
1692
+ "Nous recommended-model refresh failed (%s); using default %s",
1693
+ exc, _NOUS_MODEL,
1694
+ )
1695
+ if fresh and fresh.strip().lower() != stale:
1696
+ return fresh
1697
+ # Portal recommendation unchanged or unavailable — fall back to the
1698
+ # hardcoded known-good default, but only if it's actually different.
1699
+ if _NOUS_MODEL.strip().lower() != stale:
1700
+ return _NOUS_MODEL
1701
+ return None
1702
+
1703
+
1624
1704
  def _read_main_model() -> str:
1625
1705
  """Read the user's configured main model from config.yaml.
1626
1706
 
@@ -1841,6 +1921,13 @@ def _try_custom_endpoint() -> Tuple[Optional[Any], Optional[str]]:
1841
1921
  logger.debug("Auxiliary client: custom endpoint (%s, api_mode=%s)", model, custom_mode or "chat_completions")
1842
1922
  _clean_base, _dq = _extract_url_query_params(custom_base)
1843
1923
  _extra = {"default_query": _dq} if _dq else {}
1924
+ # User-configured model.default_headers override the SDK's identifying
1925
+ # headers (User-Agent: OpenAI/Python ..., X-Stainless-*) on this custom
1926
+ # endpoint's auxiliary calls too — matching the main agent client so the
1927
+ # whole session reaches a gateway/WAF that rejects the SDK fingerprint. (#40033)
1928
+ _custom_headers = _apply_user_default_headers(None)
1929
+ if _custom_headers:
1930
+ _extra["default_headers"] = _custom_headers
1844
1931
  if custom_mode == "codex_responses":
1845
1932
  real_client = OpenAI(api_key=custom_key, base_url=_clean_base, **_extra)
1846
1933
  return CodexAuxiliaryClient(real_client, model), model
@@ -2390,6 +2477,25 @@ def _is_connection_error(exc: Exception) -> bool:
2390
2477
  return False
2391
2478
 
2392
2479
 
2480
+ def _is_transient_transport_error(exc: Exception) -> bool:
2481
+ """Return True for a one-off transport blip worth retrying ONCE on the
2482
+ same provider before any provider/model fallback.
2483
+
2484
+ Covers connection/streaming-close errors (via the canonical
2485
+ ``_is_connection_error`` detector, shared so the two cannot drift) plus a
2486
+ pure 5xx/408 HTTP status. Deliberately narrow: this is the "retry the
2487
+ same target once" gate, distinct from ``_is_payment_error`` /
2488
+ ``_is_auth_error`` / ``_is_rate_limit_error`` which the except-chain
2489
+ handles by switching provider, refreshing creds, or rotating the pool.
2490
+ """
2491
+ if _is_connection_error(exc):
2492
+ return True
2493
+ status = getattr(exc, "status_code", None) or getattr(
2494
+ getattr(exc, "response", None), "status_code", None
2495
+ )
2496
+ return isinstance(status, int) and (status == 408 or 500 <= status < 600)
2497
+
2498
+
2393
2499
  def _is_auth_error(exc: Exception) -> bool:
2394
2500
  """Detect auth failures that should trigger provider-specific refresh."""
2395
2501
  status = getattr(exc, "status_code", None)
@@ -2451,6 +2557,46 @@ def _is_unsupported_temperature_error(exc: Exception) -> bool:
2451
2557
  return _is_unsupported_parameter_error(exc, "temperature")
2452
2558
 
2453
2559
 
2560
+ def _is_model_not_found_error(exc: Exception) -> bool:
2561
+ """Detect "the requested model doesn't exist" errors (404 / invalid model).
2562
+
2563
+ This fires when a resolved model name is no longer served by the endpoint
2564
+ — most commonly when a long-lived process pinned a Portal-recommended model
2565
+ that has since been dropped from the Nous → OpenRouter catalog. The Nous
2566
+ proxy returns 404 with a body like::
2567
+
2568
+ Model 'gpt-5.4-mini' not found. The requested model does not exist
2569
+ in our configuration or OpenRouter catalog.
2570
+
2571
+ Distinct from :func:`_is_payment_error` (which also matches some 404s for
2572
+ free-tier/credit language) — this one keys on "does not exist / not found /
2573
+ not a valid model" phrasing, and explicitly excludes the billing keywords
2574
+ that the payment path already owns so the two predicates don't overlap.
2575
+ """
2576
+ status = getattr(exc, "status_code", None)
2577
+ err_lower = str(exc).lower()
2578
+ # Billing/quota 404s belong to _is_payment_error — don't claim them here.
2579
+ if any(kw in err_lower for kw in (
2580
+ "credits", "insufficient funds", "billing", "out of funds",
2581
+ "balance_depleted", "no usable credits", "free tier", "free-tier",
2582
+ "not available on the free tier",
2583
+ )):
2584
+ return False
2585
+ if status not in {404, 400, None}:
2586
+ return False
2587
+ return any(kw in err_lower for kw in (
2588
+ "model does not exist",
2589
+ "does not exist in our configuration",
2590
+ "openrouter catalog",
2591
+ "is not a valid model",
2592
+ "no such model",
2593
+ "model not found",
2594
+ "the model `", # OpenAI-style: "The model `X` does not exist"
2595
+ "model_not_found",
2596
+ "unknown model",
2597
+ ))
2598
+
2599
+
2454
2600
  def _evict_cached_clients(provider: str) -> None:
2455
2601
  """Drop cached auxiliary clients for a provider so fresh creds are used."""
2456
2602
  normalized = _normalize_aux_provider(provider)
@@ -2933,23 +3079,20 @@ def _try_configured_fallback_chain(
2933
3079
  if not fb_provider or fb_provider.lower() == skip:
2934
3080
  continue
2935
3081
  fb_model = str(entry.get("model", "")).strip() or None
2936
- fb_base_url = str(entry.get("base_url", "")).strip() or None
2937
- fb_api_key = str(entry.get("api_key", "")).strip() or None
2938
3082
 
2939
3083
  label = f"fallback_chain[{i}]({fb_provider})"
2940
3084
 
2941
3085
  try:
2942
- fb_client = _resolve_single_provider(
2943
- fb_provider, fb_model, fb_base_url, fb_api_key)
3086
+ fb_client, resolved_model = _resolve_fallback_entry(entry)
2944
3087
  except Exception:
2945
- fb_client = None
3088
+ fb_client, resolved_model = None, None
2946
3089
 
2947
3090
  if fb_client is not None:
2948
3091
  logger.info(
2949
3092
  "Auxiliary %s: %s on %s — configured fallback to %s (%s)",
2950
- task, reason, failed_provider, label, fb_model or "default",
3093
+ task, reason, failed_provider, label, resolved_model or fb_model or "default",
2951
3094
  )
2952
- return fb_client, fb_model, label
3095
+ return fb_client, resolved_model or fb_model, label
2953
3096
  tried.append(label)
2954
3097
 
2955
3098
  if tried:
@@ -2960,6 +3103,103 @@ def _try_configured_fallback_chain(
2960
3103
  return None, None, ""
2961
3104
 
2962
3105
 
3106
+ def _fallback_entry_api_key(entry: Dict[str, Any]) -> Optional[str]:
3107
+ """Resolve inline or env-backed API key from a fallback-chain entry."""
3108
+ explicit = str(entry.get("api_key") or "").strip()
3109
+ if explicit:
3110
+ return explicit
3111
+ key_env = str(entry.get("key_env") or entry.get("api_key_env") or "").strip()
3112
+ if key_env:
3113
+ return os.getenv(key_env, "").strip() or None
3114
+ return None
3115
+
3116
+
3117
+ def _resolve_fallback_entry(entry: Dict[str, Any]) -> Tuple[Optional[Any], Optional[str]]:
3118
+ """Resolve one fallback entry through the central provider router."""
3119
+ provider = str(entry.get("provider") or "").strip()
3120
+ model = str(entry.get("model") or "").strip() or None
3121
+ if not provider or not model:
3122
+ return None, None
3123
+ base_url = str(entry.get("base_url") or "").strip() or None
3124
+ api_key = _fallback_entry_api_key(entry)
3125
+ api_mode = str(entry.get("api_mode") or entry.get("transport") or "").strip() or None
3126
+ return resolve_provider_client(
3127
+ provider,
3128
+ model=model,
3129
+ explicit_base_url=base_url,
3130
+ explicit_api_key=api_key,
3131
+ api_mode=api_mode,
3132
+ )
3133
+
3134
+
3135
+ def _try_main_fallback_chain(
3136
+ task: Optional[str],
3137
+ failed_provider: str = "",
3138
+ reason: str = "error",
3139
+ ) -> Tuple[Optional[Any], Optional[str], str]:
3140
+ """Try the top-level main-agent fallback chain for an auxiliary call.
3141
+
3142
+ ``provider: auto`` auxiliary tasks should respect the user's declared
3143
+ main fallback policy before dropping into Hermes' built-in discovery
3144
+ chain. The top-level chain is read through ``get_fallback_chain`` so
3145
+ both modern ``fallback_providers`` and legacy ``fallback_model`` entries
3146
+ participate in the same order as the main agent.
3147
+ """
3148
+ try:
3149
+ from hermes_cli.config import load_config
3150
+ from hermes_cli.fallback_config import get_fallback_chain
3151
+
3152
+ chain = get_fallback_chain(load_config())
3153
+ except Exception as exc:
3154
+ logger.debug("Auxiliary %s: could not load main fallback chain: %s", task or "call", exc)
3155
+ return None, None, ""
3156
+
3157
+ if not chain:
3158
+ return None, None, ""
3159
+
3160
+ failed_norm = (failed_provider or "").strip().lower()
3161
+ main_norm = (_read_main_provider() or "").strip().lower()
3162
+ skip = {p for p in (failed_norm, main_norm, "auto") if p}
3163
+ tried: List[str] = []
3164
+
3165
+ for i, entry in enumerate(chain):
3166
+ if not isinstance(entry, dict):
3167
+ continue
3168
+ fb_provider = str(entry.get("provider") or "").strip()
3169
+ fb_model = str(entry.get("model") or "").strip()
3170
+ if not fb_provider or not fb_model:
3171
+ continue
3172
+ fb_norm = fb_provider.lower()
3173
+ label = f"fallback_providers[{i}]({fb_provider})"
3174
+ if fb_norm in skip:
3175
+ tried.append(f"{label} (skipped)")
3176
+ continue
3177
+ if _is_provider_unhealthy(fb_norm):
3178
+ _log_skip_unhealthy(fb_norm, task)
3179
+ tried.append(f"{label} (unhealthy)")
3180
+ continue
3181
+ try:
3182
+ fb_client, resolved_model = _resolve_fallback_entry(entry)
3183
+ except Exception as exc:
3184
+ logger.debug("Auxiliary %s: main fallback %s failed to resolve: %s", task or "call", label, exc)
3185
+ fb_client, resolved_model = None, None
3186
+ if fb_client is not None:
3187
+ logger.info(
3188
+ "Auxiliary %s: %s on %s — main fallback chain to %s (%s)",
3189
+ task or "call", reason, failed_provider or "auto", label,
3190
+ resolved_model or fb_model,
3191
+ )
3192
+ return fb_client, resolved_model or fb_model, fb_provider
3193
+ tried.append(label)
3194
+
3195
+ if tried:
3196
+ logger.debug(
3197
+ "Auxiliary %s: main fallback chain exhausted (tried: %s)",
3198
+ task or "call", ", ".join(tried),
3199
+ )
3200
+ return None, None, ""
3201
+
3202
+
2963
3203
  def _resolve_single_provider(
2964
3204
  provider: str,
2965
3205
  model: Optional[str] = None,
@@ -2970,16 +3210,19 @@ def _resolve_single_provider(
2970
3210
 
2971
3211
  Uses the existing provider resolution infrastructure where possible.
2972
3212
  """
2973
- # Reuse resolve_provider_client which handles provider→client mapping
3213
+ # Reuse resolve_provider_client which handles provider→client mapping.
2974
3214
  client, resolved_model = resolve_provider_client(
2975
3215
  provider=provider,
2976
3216
  model=model,
2977
- base_url=base_url,
2978
- api_key=api_key,
3217
+ explicit_base_url=base_url,
3218
+ explicit_api_key=api_key,
2979
3219
  )
2980
3220
  return client
2981
3221
 
2982
- def _resolve_auto(main_runtime: Optional[Dict[str, Any]] = None) -> Tuple[Optional[OpenAI], Optional[str]]:
3222
+ def _resolve_auto(
3223
+ main_runtime: Optional[Dict[str, Any]] = None,
3224
+ task: Optional[str] = None,
3225
+ ) -> Tuple[Optional[OpenAI], Optional[str]]:
2983
3226
  """Full auto-detection chain.
2984
3227
 
2985
3228
  Priority:
@@ -3045,7 +3288,7 @@ def _resolve_auto(main_runtime: Optional[Dict[str, Any]] = None) -> Tuple[Option
3045
3288
  if (main_provider and main_model
3046
3289
  and main_provider not in {"auto", ""}):
3047
3290
  resolved_provider = main_provider
3048
- explicit_base_url = None
3291
+ explicit_base_url = runtime_base_url or None
3049
3292
  explicit_api_key = None
3050
3293
  if runtime_base_url and (main_provider == "custom" or main_provider.startswith("custom:")):
3051
3294
  resolved_provider = "custom"
@@ -3077,7 +3320,22 @@ def _resolve_auto(main_runtime: Optional[Dict[str, Any]] = None) -> Tuple[Option
3077
3320
  main_provider, resolved or main_model)
3078
3321
  return client, resolved or main_model
3079
3322
 
3080
- # ── Step 2: aggregator / fallback chain ──────────────────────────────
3323
+ # ── Step 2: user-configured fallback policy ─────────────────────────
3324
+ # In auto mode, respect the task-specific fallback chain first, then the
3325
+ # main agent's top-level fallback_providers/fallback_model chain. The
3326
+ # hardcoded provider discovery chain below is only the convenience default
3327
+ # for users who have not declared a fallback policy.
3328
+ if task:
3329
+ fb_client, fb_model, _fb_label = _try_configured_fallback_chain(
3330
+ task, main_provider or "auto", reason="main provider unavailable")
3331
+ if fb_client is not None:
3332
+ return fb_client, fb_model
3333
+ fb_client, fb_model, _fb_label = _try_main_fallback_chain(
3334
+ task, main_provider or "auto", reason="main provider unavailable")
3335
+ if fb_client is not None:
3336
+ return fb_client, fb_model
3337
+
3338
+ # ── Step 3: aggregator / fallback chain ──────────────────────────────
3081
3339
  tried = []
3082
3340
  for label, try_fn in _get_provider_chain():
3083
3341
  if _is_provider_unhealthy(label):
@@ -3170,6 +3428,9 @@ def _to_async_client(sync_client, model: str, is_vision: bool = False):
3170
3428
  async_kwargs["default_headers"] = dict(_ph_async.default_headers)
3171
3429
  except Exception:
3172
3430
  pass
3431
+ _merged_async = _apply_user_default_headers(async_kwargs.get("default_headers"))
3432
+ if _merged_async:
3433
+ async_kwargs["default_headers"] = _merged_async
3173
3434
  return AsyncOpenAI(**async_kwargs), model
3174
3435
 
3175
3436
 
@@ -3195,6 +3456,7 @@ def resolve_provider_client(
3195
3456
  api_mode: str = None,
3196
3457
  main_runtime: Optional[Dict[str, Any]] = None,
3197
3458
  is_vision: bool = False,
3459
+ task: Optional[str] = None,
3198
3460
  ) -> Tuple[Optional[Any], Optional[str]]:
3199
3461
  """Central router: given a provider name and optional model, return a
3200
3462
  configured client with the correct auth, base URL, and API format.
@@ -3315,7 +3577,7 @@ def resolve_provider_client(
3315
3577
 
3316
3578
  # ── Auto: try all providers in priority order ────────────────────
3317
3579
  if provider == "auto":
3318
- client, resolved = _resolve_auto(main_runtime=main_runtime)
3580
+ client, resolved = _resolve_auto(main_runtime=main_runtime, task=task)
3319
3581
  if client is None:
3320
3582
  return None, None
3321
3583
  # When auto-detection lands on a non-OpenRouter provider (e.g. a
@@ -3457,6 +3719,9 @@ def resolve_provider_client(
3457
3719
  extra["default_headers"] = dict(_ph_custom.default_headers)
3458
3720
  except Exception:
3459
3721
  pass
3722
+ _merged_custom = _apply_user_default_headers(extra.get("default_headers"))
3723
+ if _merged_custom:
3724
+ extra["default_headers"] = _merged_custom
3460
3725
  client = OpenAI(api_key=custom_key, base_url=_clean_base, **extra)
3461
3726
  client = _wrap_if_needed(client, final_model, custom_base, custom_key)
3462
3727
  return (_to_async_client(client, final_model, is_vision=is_vision) if async_mode
@@ -3533,6 +3798,9 @@ def resolve_provider_client(
3533
3798
  raw_base_for_wrap = custom_base
3534
3799
  _clean_base2, _dq2 = _extract_url_query_params(openai_base)
3535
3800
  _extra2 = {"default_query": _dq2} if _dq2 else {}
3801
+ _headers2 = _apply_user_default_headers(_extra2.get("default_headers"))
3802
+ if _headers2:
3803
+ _extra2["default_headers"] = _headers2
3536
3804
  logger.debug(
3537
3805
  "resolve_provider_client: named custom provider %r (%s, api_mode=%s)",
3538
3806
  provider, final_model, entry_api_mode or "chat_completions")
@@ -3555,6 +3823,9 @@ def resolve_provider_client(
3555
3823
  _fallback_base = _to_openai_base_url(custom_base)
3556
3824
  _fb_clean, _fb_dq = _extract_url_query_params(_fallback_base)
3557
3825
  _fb_extra = {"default_query": _fb_dq} if _fb_dq else {}
3826
+ _fb_headers = _apply_user_default_headers(_fb_extra.get("default_headers"))
3827
+ if _fb_headers:
3828
+ _fb_extra["default_headers"] = _fb_headers
3558
3829
  client = OpenAI(api_key=custom_key, base_url=_fb_clean, **_fb_extra)
3559
3830
  return (_to_async_client(client, final_model, is_vision=is_vision) if async_mode
3560
3831
  else (client, final_model))
@@ -3703,6 +3974,9 @@ def resolve_provider_client(
3703
3974
  headers.update(_ph_main.default_headers)
3704
3975
  except Exception:
3705
3976
  pass
3977
+ _merged_main = _apply_user_default_headers(headers)
3978
+ if _merged_main:
3979
+ headers = _merged_main
3706
3980
  client = OpenAI(api_key=api_key, base_url=base_url,
3707
3981
  **({"default_headers": headers} if headers else {}))
3708
3982
 
@@ -4140,13 +4414,15 @@ def get_auxiliary_extra_body() -> dict:
4140
4414
  return _nous_extra_body() if auxiliary_is_nous else {}
4141
4415
 
4142
4416
 
4143
- def auxiliary_max_tokens_param(value: int) -> dict:
4417
+ def auxiliary_max_tokens_param(value: int, *, model: Optional[str] = None) -> dict:
4144
4418
  """Return the correct max tokens kwarg for the auxiliary client's provider.
4145
-
4419
+
4146
4420
  OpenRouter and local models use 'max_tokens'. Direct OpenAI with newer
4147
- models (gpt-4o, o-series, gpt-5+) requires 'max_completion_tokens'.
4421
+ models (gpt-4o, gpt-4.1, gpt-5+, o-series) requires 'max_completion_tokens'.
4148
4422
  The Codex adapter translates max_tokens internally, so we use max_tokens
4149
- for it as well.
4423
+ for it as well. Pass ``model`` so third-party OpenAI-compatible endpoints
4424
+ fronting the newer families are also recognised — URL-only detection
4425
+ misses the case where a custom base URL serves e.g. ``gpt-5.4``.
4150
4426
  """
4151
4427
  custom_base = _current_custom_base_url()
4152
4428
  or_key = os.getenv("OPENROUTER_API_KEY")
@@ -4156,6 +4432,9 @@ def auxiliary_max_tokens_param(value: int) -> dict:
4156
4432
  and _read_nous_auth() is None
4157
4433
  and base_url_hostname(custom_base) in {"api.openai.com", "api.githubcopilot.com"}):
4158
4434
  return {"max_completion_tokens": value}
4435
+ # ...and for any caller serving a newer OpenAI-family model by name.
4436
+ if model_forces_max_completion_tokens(model):
4437
+ return {"max_completion_tokens": value}
4159
4438
  return {"max_tokens": value}
4160
4439
 
4161
4440
 
@@ -4191,11 +4470,16 @@ def _client_cache_key(
4191
4470
  api_mode: Optional[str] = None,
4192
4471
  main_runtime: Optional[Dict[str, Any]] = None,
4193
4472
  is_vision: bool = False,
4473
+ task: Optional[str] = None,
4194
4474
  ) -> tuple:
4195
4475
  runtime = _normalize_main_runtime(main_runtime)
4196
4476
  runtime_key = tuple(runtime.get(field, "") for field in _MAIN_RUNTIME_FIELDS) if provider == "auto" else ()
4477
+ # `auto` can now resolve through task-specific or main fallback policy,
4478
+ # so the task participates in the cache key. Non-auto providers keep the
4479
+ # old cache shape because the explicit provider/model tuple is sufficient.
4480
+ task_key = (task or "") if provider == "auto" else ""
4197
4481
  pool_hint = _pool_cache_hint(provider, main_runtime=main_runtime)
4198
- return (provider, async_mode, base_url or "", api_key or "", api_mode or "", runtime_key, is_vision, pool_hint)
4482
+ return (provider, async_mode, base_url or "", api_key or "", api_mode or "", runtime_key, is_vision, task_key, pool_hint)
4199
4483
 
4200
4484
 
4201
4485
  def _store_cached_client(cache_key: tuple, client: Any, default_model: Optional[str], *, bound_loop: Any = None) -> None:
@@ -4388,6 +4672,7 @@ def _get_cached_client(
4388
4672
  api_mode: str = None,
4389
4673
  main_runtime: Optional[Dict[str, Any]] = None,
4390
4674
  is_vision: bool = False,
4675
+ task: Optional[str] = None,
4391
4676
  ) -> Tuple[Optional[Any], Optional[str]]:
4392
4677
  """Get or create a cached client for the given provider.
4393
4678
 
@@ -4425,6 +4710,7 @@ def _get_cached_client(
4425
4710
  api_mode=api_mode,
4426
4711
  main_runtime=main_runtime,
4427
4712
  is_vision=is_vision,
4713
+ task=task,
4428
4714
  )
4429
4715
  with _client_cache_lock:
4430
4716
  if cache_key in _client_cache:
@@ -4469,6 +4755,7 @@ def _get_cached_client(
4469
4755
  api_mode=api_mode,
4470
4756
  main_runtime=runtime,
4471
4757
  is_vision=is_vision,
4758
+ task=task,
4472
4759
  )
4473
4760
  if client is not None:
4474
4761
  # For async clients, remember which loop they were created on so we
@@ -4675,10 +4962,14 @@ def _is_anthropic_compat_endpoint(provider: str, base_url: str) -> bool:
4675
4962
 
4676
4963
 
4677
4964
  def _convert_openai_images_to_anthropic(messages: list) -> list:
4678
- """Convert OpenAI ``image_url`` content blocks to Anthropic ``image`` blocks.
4965
+ """Convert OpenAI ``image_url``/``video_url`` blocks to Anthropic format.
4679
4966
 
4680
- Only touches messages that have list-type content with ``image_url`` blocks;
4681
- plain text messages pass through unchanged.
4967
+ Converts:
4968
+ - ``image_url`` blocks to Anthropic ``image`` blocks
4969
+ - ``video_url`` blocks to Anthropic ``video`` blocks (MiniMax M3 compat)
4970
+
4971
+ Only touches messages that have list-type content with ``image_url`` or
4972
+ ``video_url`` blocks; plain text messages pass through unchanged.
4682
4973
  """
4683
4974
  converted = []
4684
4975
  for msg in messages:
@@ -4715,6 +5006,39 @@ def _convert_openai_images_to_anthropic(messages: list) -> list:
4715
5006
  },
4716
5007
  })
4717
5008
  changed = True
5009
+ elif block.get("type") == "video_url":
5010
+ # MiniMax's Anthropic-compatible endpoint expects a "video"
5011
+ # block (not OpenAI's "video_url", and not "input_video").
5012
+ # See https://platform.minimax.io/docs/api-reference/text-anthropic-api
5013
+ # — the Messages-field table lists type="video" (M3 only,
5014
+ # URL/base64/mm_file://). The source shape mirrors the "image"
5015
+ # block: base64 → {type:"base64", media_type, data}, URL →
5016
+ # {type:"url", url}.
5017
+ video_url_val = (block.get("video_url") or {}).get("url", "")
5018
+ if video_url_val.startswith("data:"):
5019
+ # Parse data URI: data:<media_type>;base64,<data>
5020
+ header, _, b64data = video_url_val.partition(",")
5021
+ media_type = "video/mp4"
5022
+ if ":" in header and ";" in header:
5023
+ media_type = header.split(":", 1)[1].split(";", 1)[0]
5024
+ new_content.append({
5025
+ "type": "video",
5026
+ "source": {
5027
+ "type": "base64",
5028
+ "media_type": media_type,
5029
+ "data": b64data,
5030
+ },
5031
+ })
5032
+ else:
5033
+ # URL-based video
5034
+ new_content.append({
5035
+ "type": "video",
5036
+ "source": {
5037
+ "type": "url",
5038
+ "url": video_url_val,
5039
+ },
5040
+ })
5041
+ changed = True
4718
5042
  else:
4719
5043
  new_content.append(block)
4720
5044
  converted.append({**msg, "content": new_content} if changed else msg)
@@ -4802,7 +5126,7 @@ def _build_call_kwargs(
4802
5126
 
4803
5127
  # Provider-specific extra_body
4804
5128
  merged_extra = dict(extra_body or {})
4805
- if provider == "nous" or auxiliary_is_nous:
5129
+ if provider == "nous":
4806
5130
  merged_extra.setdefault("tags", []).extend(_nous_portal_tags())
4807
5131
  if merged_extra:
4808
5132
  kwargs["extra_body"] = merged_extra
@@ -4937,7 +5261,7 @@ def call_llm(
4937
5261
  if not resolved_base_url:
4938
5262
  logger.info("Auxiliary %s: provider %s unavailable, trying auto-detection chain",
4939
5263
  task or "call", resolved_provider)
4940
- client, final_model = _get_cached_client("auto", main_runtime=main_runtime)
5264
+ client, final_model = _get_cached_client("auto", main_runtime=main_runtime, task=task)
4941
5265
  if client is None:
4942
5266
  raise RuntimeError(
4943
5267
  f"No LLM provider configured for task={task} provider={resolved_provider}. "
@@ -4969,8 +5293,28 @@ def call_llm(
4969
5293
  # Handle unsupported temperature, max_tokens vs max_completion_tokens retry,
4970
5294
  # then payment fallback.
4971
5295
  try:
4972
- return _validate_llm_response(
4973
- client.chat.completions.create(**kwargs), task)
5296
+ # Retry ONCE on the same provider for a one-off transient transport
5297
+ # blip (streaming-close / incomplete chunked read / 5xx / 408) before
5298
+ # the except-chain below escalates to provider/model fallback. A
5299
+ # single dropped connection shouldn't abandon an otherwise-healthy
5300
+ # provider. A second failure (or any non-transient error) falls
5301
+ # through to ``first_err`` and the existing fallback handling
5302
+ # unchanged. This is the unified home for the transient retry that
5303
+ # every auxiliary task (compression, memory flush, title-gen,
5304
+ # session-search, vision) shares. (PR #16587)
5305
+ try:
5306
+ return _validate_llm_response(
5307
+ client.chat.completions.create(**kwargs), task)
5308
+ except Exception as transient_err:
5309
+ if not _is_transient_transport_error(transient_err):
5310
+ raise
5311
+ logger.info(
5312
+ "Auxiliary %s: transient transport error; retrying once on "
5313
+ "the same provider before fallback: %s",
5314
+ task or "call", transient_err,
5315
+ )
5316
+ return _validate_llm_response(
5317
+ client.chat.completions.create(**kwargs), task)
4974
5318
  except Exception as first_err:
4975
5319
  if "temperature" in kwargs and _is_unsupported_temperature_error(first_err):
4976
5320
  retry_kwargs = dict(kwargs)
@@ -5027,6 +5371,32 @@ def call_llm(
5027
5371
  raise
5028
5372
  first_err = retry_err
5029
5373
 
5374
+ # ── Stale-model self-heal (Nous Portal recommendation drift) ───
5375
+ # A long-lived process can pin a Portal-recommended model that has
5376
+ # since been dropped from the Nous → OpenRouter catalog, so every
5377
+ # auxiliary call 404s with "model does not exist". Force a fresh
5378
+ # Portal fetch and retry once with the current recommendation (or the
5379
+ # known-good default). Only applies to Nous-routed calls.
5380
+ _heal_is_nous = (
5381
+ resolved_provider == "nous"
5382
+ or base_url_host_matches(_base_info, "inference-api.nousresearch.com")
5383
+ )
5384
+ if _is_model_not_found_error(first_err) and _heal_is_nous:
5385
+ healed_model = _refresh_nous_recommended_model(
5386
+ vision=(task == "vision"), stale_model=kwargs.get("model"))
5387
+ if healed_model and healed_model != kwargs.get("model"):
5388
+ logger.warning(
5389
+ "Auxiliary %s: model %r no longer in Nous catalog; "
5390
+ "retrying with refreshed recommendation %r",
5391
+ task or "call", kwargs.get("model"), healed_model,
5392
+ )
5393
+ kwargs["model"] = healed_model
5394
+ try:
5395
+ return _validate_llm_response(
5396
+ client.chat.completions.create(**kwargs), task)
5397
+ except Exception as retry_err:
5398
+ first_err = retry_err
5399
+
5030
5400
  # ── Nous auth refresh parity with main agent ──────────────────
5031
5401
  client_is_nous = (
5032
5402
  resolved_provider == "nous"
@@ -5217,14 +5587,19 @@ def call_llm(
5217
5587
 
5218
5588
  # Fallback order (#26882, #26803):
5219
5589
  # 1. User-configured fallback_chain (per-task) if set
5220
- # 2. Main agent model (last-resort safety net)
5221
- # For auto users (no explicit aux provider), use the full
5222
- # auto-detection chain instead its Step 1 IS the main agent
5223
- # model, so users on `auto` already get main-model fallback.
5590
+ # 2. For auto: top-level main fallback_providers/fallback_model
5591
+ # 3. For auto: built-in auxiliary discovery chain
5592
+ # 4. For explicit aux providers: main agent model safety net
5224
5593
  fb_client, fb_model, fb_label = (None, None, "")
5225
5594
  if is_auto:
5226
- fb_client, fb_model, fb_label = _try_payment_fallback(
5227
- resolved_provider, task, reason=reason)
5595
+ fb_client, fb_model, fb_label = _try_configured_fallback_chain(
5596
+ task, resolved_provider or "auto", reason=reason)
5597
+ if fb_client is None:
5598
+ fb_client, fb_model, fb_label = _try_main_fallback_chain(
5599
+ task, resolved_provider or "auto", reason=reason)
5600
+ if fb_client is None:
5601
+ fb_client, fb_model, fb_label = _try_payment_fallback(
5602
+ resolved_provider, task, reason=reason)
5228
5603
  else:
5229
5604
  fb_client, fb_model, fb_label = _try_configured_fallback_chain(
5230
5605
  task, resolved_provider or "auto", reason=reason)
@@ -5387,7 +5762,7 @@ async def async_call_llm(
5387
5762
  if not resolved_base_url:
5388
5763
  logger.info("Auxiliary %s: provider %s unavailable, trying auto-detection chain",
5389
5764
  task or "call", resolved_provider)
5390
- client, final_model = _get_cached_client("auto", async_mode=True)
5765
+ client, final_model = _get_cached_client("auto", async_mode=True, main_runtime=main_runtime, task=task)
5391
5766
  if client is None:
5392
5767
  raise RuntimeError(
5393
5768
  f"No LLM provider configured for task={task} provider={resolved_provider}. "
@@ -5410,8 +5785,22 @@ async def async_call_llm(
5410
5785
  kwargs["messages"] = _convert_openai_images_to_anthropic(kwargs["messages"])
5411
5786
 
5412
5787
  try:
5413
- return _validate_llm_response(
5414
- await client.chat.completions.create(**kwargs), task)
5788
+ # Retry ONCE on the same provider for a transient transport blip
5789
+ # before the except-chain escalates to fallback — see call_llm()
5790
+ # for the rationale. (PR #16587)
5791
+ try:
5792
+ return _validate_llm_response(
5793
+ await client.chat.completions.create(**kwargs), task)
5794
+ except Exception as transient_err:
5795
+ if not _is_transient_transport_error(transient_err):
5796
+ raise
5797
+ logger.info(
5798
+ "Auxiliary %s (async): transient transport error; retrying "
5799
+ "once on the same provider before fallback: %s",
5800
+ task or "call", transient_err,
5801
+ )
5802
+ return _validate_llm_response(
5803
+ await client.chat.completions.create(**kwargs), task)
5415
5804
  except Exception as first_err:
5416
5805
  if "temperature" in kwargs and _is_unsupported_temperature_error(first_err):
5417
5806
  retry_kwargs = dict(kwargs)
@@ -5464,6 +5853,31 @@ async def async_call_llm(
5464
5853
  raise
5465
5854
  first_err = retry_err
5466
5855
 
5856
+ # ── Stale-model self-heal (Nous Portal recommendation drift) ───
5857
+ # See the sync call_llm() path for the rationale: a long-lived process
5858
+ # can pin a Portal-recommended model that has since been dropped from
5859
+ # the Nous → OpenRouter catalog, 404'ing every auxiliary call. Force a
5860
+ # fresh Portal fetch and retry once with the current recommendation.
5861
+ _heal_is_nous = (
5862
+ resolved_provider == "nous"
5863
+ or base_url_host_matches(_client_base, "inference-api.nousresearch.com")
5864
+ )
5865
+ if _is_model_not_found_error(first_err) and _heal_is_nous:
5866
+ healed_model = _refresh_nous_recommended_model(
5867
+ vision=(task == "vision"), stale_model=kwargs.get("model"))
5868
+ if healed_model and healed_model != kwargs.get("model"):
5869
+ logger.warning(
5870
+ "Auxiliary %s (async): model %r no longer in Nous catalog; "
5871
+ "retrying with refreshed recommendation %r",
5872
+ task or "call", kwargs.get("model"), healed_model,
5873
+ )
5874
+ kwargs["model"] = healed_model
5875
+ try:
5876
+ return _validate_llm_response(
5877
+ await client.chat.completions.create(**kwargs), task)
5878
+ except Exception as retry_err:
5879
+ first_err = retry_err
5880
+
5467
5881
  # ── Nous auth refresh parity with main agent ──────────────────
5468
5882
  client_is_nous = (
5469
5883
  resolved_provider == "nous"
@@ -5616,13 +6030,19 @@ async def async_call_llm(
5616
6030
 
5617
6031
  # Fallback order (#26882, #26803):
5618
6032
  # 1. User-configured fallback_chain (per-task) if set
5619
- # 2. Main agent model (last-resort safety net)
5620
- # Auto users get the full auto-detection chain instead — its
5621
- # Step 1 IS the main agent model.
6033
+ # 2. For auto: top-level main fallback_providers/fallback_model
6034
+ # 3. For auto: built-in auxiliary discovery chain
6035
+ # 4. For explicit aux providers: main agent model safety net
5622
6036
  fb_client, fb_model, fb_label = (None, None, "")
5623
6037
  if is_auto:
5624
- fb_client, fb_model, fb_label = _try_payment_fallback(
5625
- resolved_provider, task, reason=reason)
6038
+ fb_client, fb_model, fb_label = _try_configured_fallback_chain(
6039
+ task, resolved_provider or "auto", reason=reason)
6040
+ if fb_client is None:
6041
+ fb_client, fb_model, fb_label = _try_main_fallback_chain(
6042
+ task, resolved_provider or "auto", reason=reason)
6043
+ if fb_client is None:
6044
+ fb_client, fb_model, fb_label = _try_payment_fallback(
6045
+ resolved_provider, task, reason=reason)
5626
6046
  else:
5627
6047
  fb_client, fb_model, fb_label = _try_configured_fallback_chain(
5628
6048
  task, resolved_provider or "auto", reason=reason)