@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.
- package/agent/.dockerignore +67 -0
- package/agent/.envrc +1 -1
- package/agent/.gitattributes +8 -0
- package/agent/AGENTS.md +216 -4
- package/agent/CONTRIBUTING.md +46 -8
- package/agent/Dockerfile +78 -35
- package/agent/MANIFEST.in +2 -0
- package/agent/README.md +12 -5
- package/agent/README.ur-pk.md +261 -0
- package/agent/README.zh-CN.md +11 -8
- package/agent/SECURITY.md +5 -4
- package/agent/acp_adapter/provenance.py +127 -0
- package/agent/acp_adapter/server.py +112 -5
- package/agent/acp_adapter/session.py +1 -6
- package/agent/acp_registry/agent.json +2 -2
- package/agent/agent/account_usage.py +313 -1
- package/agent/agent/agent_init.py +140 -37
- package/agent/agent/agent_runtime_helpers.py +342 -83
- package/agent/agent/anthropic_adapter.py +320 -33
- package/agent/agent/auxiliary_client.py +525 -105
- package/agent/agent/background_review.py +157 -19
- package/agent/agent/bedrock_adapter.py +71 -6
- package/agent/agent/billing_view.py +295 -0
- package/agent/agent/chat_completion_helpers.py +229 -4
- package/agent/agent/codex_responses_adapter.py +86 -10
- package/agent/agent/codex_runtime.py +153 -1
- package/agent/agent/coding_context.py +738 -0
- package/agent/agent/context_compressor.py +392 -44
- package/agent/agent/context_references.py +34 -1
- package/agent/agent/conversation_compression.py +159 -22
- package/agent/agent/conversation_loop.py +643 -908
- package/agent/agent/copilot_acp_client.py +4 -11
- package/agent/agent/credential_pool.py +5 -3
- package/agent/agent/credits_tracker.py +794 -0
- package/agent/agent/curator.py +91 -18
- package/agent/agent/curator_backup.py +26 -10
- package/agent/agent/display.py +42 -1
- package/agent/agent/error_classifier.py +52 -3
- package/agent/agent/errors.py +3 -0
- package/agent/agent/file_safety.py +0 -17
- package/agent/agent/gemini_native_adapter.py +31 -1
- package/agent/agent/i18n.py +48 -4
- package/agent/agent/image_gen_provider.py +74 -5
- package/agent/agent/image_routing.py +29 -0
- package/agent/agent/insights.py +8 -17
- package/agent/agent/lsp/install.py +3 -0
- package/agent/agent/memory_manager.py +326 -31
- package/agent/agent/message_content.py +50 -0
- package/agent/agent/model_metadata.py +214 -3
- package/agent/agent/moonshot_schema.py +8 -1
- package/agent/agent/onboarding.py +60 -0
- package/agent/agent/prompt_builder.py +327 -37
- package/agent/agent/redact.py +1 -0
- package/agent/agent/runtime_cwd.py +34 -5
- package/agent/agent/secret_scope.py +205 -0
- package/agent/agent/secret_sources/bitwarden.py +34 -2
- package/agent/agent/skill_commands.py +90 -1
- package/agent/agent/skill_preprocessing.py +1 -0
- package/agent/agent/skill_utils.py +209 -36
- package/agent/agent/ssl_guard.py +94 -0
- package/agent/agent/system_prompt.py +133 -5
- package/agent/agent/tool_executor.py +496 -70
- package/agent/agent/transports/anthropic.py +83 -21
- package/agent/agent/transports/chat_completions.py +94 -5
- package/agent/agent/transports/codex.py +67 -2
- package/agent/agent/transports/codex_app_server.py +1 -0
- package/agent/agent/transports/codex_app_server_session.py +30 -0
- package/agent/agent/transports/types.py +12 -0
- package/agent/agent/turn_context.py +408 -0
- package/agent/agent/turn_finalizer.py +428 -0
- package/agent/agent/turn_retry_state.py +68 -0
- package/agent/agent/usage_pricing.py +3 -0
- package/agent/apps/bootstrap-installer/package.json +6 -5
- package/agent/apps/bootstrap-installer/src/routes/failure.tsx +12 -5
- package/agent/apps/bootstrap-installer/src/routes/progress.tsx +1 -3
- package/agent/apps/bootstrap-installer/src/store.ts +3 -2
- package/agent/apps/bootstrap-installer/src-tauri/src/bootstrap.rs +172 -7
- package/agent/apps/bootstrap-installer/src-tauri/src/events.rs +14 -1
- package/agent/apps/bootstrap-installer/src-tauri/src/paths.rs +29 -0
- package/agent/apps/bootstrap-installer/src-tauri/src/powershell.rs +93 -3
- package/agent/apps/bootstrap-installer/src-tauri/src/update.rs +695 -39
- package/agent/apps/bootstrap-installer/tsconfig.json +3 -4
- package/agent/apps/desktop/DESIGN.md +167 -0
- package/agent/apps/desktop/README.md +20 -16
- package/agent/apps/desktop/assets/icon.icns +0 -0
- package/agent/apps/desktop/assets/icon.ico +0 -0
- package/agent/apps/desktop/assets/icon.png +0 -0
- package/agent/apps/desktop/electron/backend-env.cjs +112 -0
- package/agent/apps/desktop/electron/backend-env.test.cjs +111 -0
- package/agent/apps/desktop/electron/backend-probes.test.cjs +3 -1
- package/agent/apps/desktop/electron/backend-ready.cjs +66 -0
- package/agent/apps/desktop/electron/bootstrap-platform.cjs +52 -0
- package/agent/apps/desktop/electron/bootstrap-platform.test.cjs +59 -1
- package/agent/apps/desktop/electron/bootstrap-runner.cjs +176 -38
- package/agent/apps/desktop/electron/bootstrap-runner.test.cjs +112 -1
- package/agent/apps/desktop/electron/connection-config.cjs +288 -0
- package/agent/apps/desktop/electron/connection-config.test.cjs +396 -0
- package/agent/apps/desktop/electron/dashboard-token.cjs +99 -0
- package/agent/apps/desktop/electron/dashboard-token.test.cjs +142 -0
- package/agent/apps/desktop/electron/desktop-uninstall.cjs +232 -0
- package/agent/apps/desktop/electron/desktop-uninstall.test.cjs +246 -0
- package/agent/apps/desktop/electron/entitlements.mac.inherit.plist +2 -0
- package/agent/apps/desktop/electron/fs-read-dir.cjs +109 -0
- package/agent/apps/desktop/electron/fs-read-dir.test.cjs +364 -0
- package/agent/apps/desktop/electron/gateway-ws-probe.cjs +188 -0
- package/agent/apps/desktop/electron/gateway-ws-probe.test.cjs +122 -0
- package/agent/apps/desktop/electron/git-root.cjs +54 -0
- package/agent/apps/desktop/electron/git-root.test.cjs +40 -0
- package/agent/apps/desktop/electron/git-worktrees.cjs +174 -0
- package/agent/apps/desktop/electron/hardening.cjs +123 -28
- package/agent/apps/desktop/electron/hardening.test.cjs +163 -0
- package/agent/apps/desktop/electron/main.cjs +3121 -331
- package/agent/apps/desktop/electron/oauth-net-request.cjs +20 -0
- package/agent/apps/desktop/electron/oauth-net-request.test.cjs +34 -0
- package/agent/apps/desktop/electron/preload.cjs +52 -2
- package/agent/apps/desktop/electron/session-windows.cjs +124 -0
- package/agent/apps/desktop/electron/session-windows.test.cjs +199 -0
- package/agent/apps/desktop/electron/update-rebuild.cjs +29 -0
- package/agent/apps/desktop/electron/update-rebuild.test.cjs +55 -0
- package/agent/apps/desktop/electron/update-remote.cjs +56 -0
- package/agent/apps/desktop/electron/update-remote.test.cjs +78 -0
- package/agent/apps/desktop/electron/vscode-marketplace.cjs +331 -0
- package/agent/apps/desktop/electron/vscode-marketplace.test.cjs +113 -0
- package/agent/apps/desktop/electron/windows-child-process.test.cjs +57 -0
- package/agent/apps/desktop/electron/windows-user-env.cjs +76 -0
- package/agent/apps/desktop/electron/windows-user-env.test.cjs +90 -0
- package/agent/apps/desktop/electron/workspace-cwd.cjs +38 -0
- package/agent/apps/desktop/electron/workspace-cwd.test.cjs +45 -0
- package/agent/apps/desktop/eslint.config.mjs +0 -3
- package/agent/apps/desktop/index.html +27 -2
- package/agent/apps/desktop/package.json +31 -11
- package/agent/apps/desktop/pr-assets/session-source-folders.png +0 -0
- package/agent/apps/desktop/public/apple-touch-icon.png +0 -0
- package/agent/apps/desktop/public/nous-girl.jpg +0 -0
- package/agent/apps/desktop/scripts/assert-dist-built.cjs +70 -0
- package/agent/apps/desktop/scripts/assert-dist-built.test.cjs +84 -0
- package/agent/apps/desktop/scripts/before-pack.cjs +78 -0
- package/agent/apps/desktop/scripts/before-pack.test.cjs +53 -0
- package/agent/apps/desktop/scripts/diag-scroll-reset.mjs +229 -0
- package/agent/apps/desktop/scripts/patch-electron-builder-mac-binary.cjs +64 -0
- package/agent/apps/desktop/scripts/run-electron-builder.cjs +57 -0
- package/agent/apps/desktop/src/app/agents/index.tsx +53 -45
- package/agent/apps/desktop/src/app/artifacts/index.tsx +102 -83
- package/agent/apps/desktop/src/app/chat/chat-drop-overlay.tsx +29 -8
- package/agent/apps/desktop/src/app/chat/chat-swap-overlay.tsx +47 -0
- package/agent/apps/desktop/src/app/chat/composer/attachments.tsx +81 -45
- package/agent/apps/desktop/src/app/chat/composer/completion-drawer.tsx +13 -24
- package/agent/apps/desktop/src/app/chat/composer/context-menu.tsx +138 -88
- package/agent/apps/desktop/src/app/chat/composer/controls.tsx +138 -90
- package/agent/apps/desktop/src/app/chat/composer/enter-submit-dom-race.test.tsx +218 -0
- package/agent/apps/desktop/src/app/chat/composer/focus.ts +32 -0
- package/agent/apps/desktop/src/app/chat/composer/help-hint.tsx +38 -25
- package/agent/apps/desktop/src/app/chat/composer/hooks/use-live-completion-adapter.ts +7 -0
- package/agent/apps/desktop/src/app/chat/composer/hooks/use-mic-recorder.ts +22 -12
- package/agent/apps/desktop/src/app/chat/composer/hooks/use-slash-completions.ts +142 -14
- package/agent/apps/desktop/src/app/chat/composer/hooks/use-voice-conversation.ts +14 -11
- package/agent/apps/desktop/src/app/chat/composer/hooks/use-voice-recorder.ts +9 -6
- package/agent/apps/desktop/src/app/chat/composer/ime-composition-dom-repro.test.tsx +108 -0
- package/agent/apps/desktop/src/app/chat/composer/index.tsx +930 -180
- package/agent/apps/desktop/src/app/chat/composer/inline-refs.ts +136 -32
- package/agent/apps/desktop/src/app/chat/composer/model-pill.tsx +86 -0
- package/agent/apps/desktop/src/app/chat/composer/queue-panel.tsx +54 -75
- package/agent/apps/desktop/src/app/chat/composer/rich-editor.test.ts +117 -1
- package/agent/apps/desktop/src/app/chat/composer/rich-editor.ts +117 -6
- package/agent/apps/desktop/src/app/chat/composer/slash-nav-dom-repro.test.tsx +186 -0
- package/agent/apps/desktop/src/app/chat/composer/status-stack/index.tsx +202 -0
- package/agent/apps/desktop/src/app/chat/composer/status-stack/status-row.tsx +155 -0
- package/agent/apps/desktop/src/app/chat/composer/text-utils.test.ts +104 -0
- package/agent/apps/desktop/src/app/chat/composer/text-utils.ts +37 -9
- package/agent/apps/desktop/src/app/chat/composer/trigger-popover.test.tsx +50 -0
- package/agent/apps/desktop/src/app/chat/composer/trigger-popover.tsx +105 -40
- package/agent/apps/desktop/src/app/chat/composer/types.ts +5 -0
- package/agent/apps/desktop/src/app/chat/composer/url-dialog.tsx +11 -15
- package/agent/apps/desktop/src/app/chat/composer/voice-activity.tsx +8 -4
- package/agent/apps/desktop/src/app/chat/hooks/use-composer-actions.test.ts +57 -0
- package/agent/apps/desktop/src/app/chat/hooks/use-composer-actions.ts +70 -16
- package/agent/apps/desktop/src/app/chat/hooks/use-file-drop-zone.ts +52 -16
- package/agent/apps/desktop/src/app/chat/index.tsx +234 -81
- package/agent/apps/desktop/src/app/chat/perf-probe.tsx +69 -21
- package/agent/apps/desktop/src/app/chat/right-rail/preview-console.tsx +44 -40
- package/agent/apps/desktop/src/app/chat/right-rail/preview-file.tsx +71 -25
- package/agent/apps/desktop/src/app/chat/right-rail/preview-pane.test.tsx +40 -1
- package/agent/apps/desktop/src/app/chat/right-rail/preview-pane.tsx +55 -53
- package/agent/apps/desktop/src/app/chat/right-rail/preview.tsx +35 -17
- package/agent/apps/desktop/src/app/chat/scroll-to-bottom-button.test.tsx +67 -0
- package/agent/apps/desktop/src/app/chat/scroll-to-bottom-button.tsx +74 -0
- package/agent/apps/desktop/src/app/chat/sidebar/cron-jobs-section.tsx +356 -0
- package/agent/apps/desktop/src/app/chat/sidebar/index.tsx +1189 -364
- package/agent/apps/desktop/src/app/chat/sidebar/load-more-row.tsx +30 -0
- package/agent/apps/desktop/src/app/chat/sidebar/order.test.ts +21 -0
- package/agent/apps/desktop/src/app/chat/sidebar/order.ts +17 -0
- package/agent/apps/desktop/src/app/chat/sidebar/profile-switcher.tsx +524 -0
- package/agent/apps/desktop/src/app/chat/sidebar/session-actions-menu.tsx +80 -45
- package/agent/apps/desktop/src/app/chat/sidebar/session-row.tsx +120 -25
- package/agent/apps/desktop/src/app/chat/sidebar/virtual-session-list.tsx +7 -13
- package/agent/apps/desktop/src/app/chat/sidebar/workspace-groups.test.ts +149 -0
- package/agent/apps/desktop/src/app/chat/sidebar/workspace-groups.ts +326 -0
- package/agent/apps/desktop/src/app/chat/thread-loading.ts +7 -2
- package/agent/apps/desktop/src/app/command-center/index.tsx +320 -581
- package/agent/apps/desktop/src/app/command-palette/index.tsx +681 -0
- package/agent/apps/desktop/src/app/command-palette/marketplace-theme-page.tsx +157 -0
- package/agent/apps/desktop/src/app/cron/index.tsx +392 -324
- package/agent/apps/desktop/src/app/cron/job-state.ts +29 -0
- package/agent/apps/desktop/src/app/desktop-controller.tsx +618 -123
- package/agent/apps/desktop/src/app/floating-hud.ts +22 -0
- package/agent/apps/desktop/src/app/gateway/hooks/use-gateway-boot.test.tsx +265 -0
- package/agent/apps/desktop/src/app/gateway/hooks/use-gateway-boot.ts +260 -14
- package/agent/apps/desktop/src/app/gateway/hooks/use-gateway-request.ts +48 -4
- package/agent/apps/desktop/src/app/hooks/use-keybinds.ts +270 -0
- package/agent/apps/desktop/src/app/hooks/use-refresh-hotkey.ts +45 -0
- package/agent/apps/desktop/src/app/layout-constants.ts +19 -0
- package/agent/apps/desktop/src/app/messaging/index.tsx +136 -241
- package/agent/apps/desktop/src/app/messaging/platform-icon.tsx +95 -0
- package/agent/apps/desktop/src/app/model-visibility-overlay.tsx +31 -0
- package/agent/apps/desktop/src/app/overlays/overlay-search-input.tsx +18 -62
- package/agent/apps/desktop/src/app/overlays/overlay-split-layout.tsx +59 -7
- package/agent/apps/desktop/src/app/overlays/overlay-view.tsx +9 -5
- package/agent/apps/desktop/src/app/page-search-shell.tsx +42 -20
- package/agent/apps/desktop/src/app/profiles/create-profile-dialog.tsx +165 -0
- package/agent/apps/desktop/src/app/profiles/delete-profile-dialog.tsx +65 -0
- package/agent/apps/desktop/src/app/profiles/index.tsx +174 -199
- package/agent/apps/desktop/src/app/profiles/rename-profile-dialog.tsx +125 -0
- package/agent/apps/desktop/src/app/right-sidebar/files/dnd-manager.ts +27 -0
- package/agent/apps/desktop/src/app/right-sidebar/files/ipc.test.ts +100 -0
- package/agent/apps/desktop/src/app/right-sidebar/files/ipc.ts +12 -18
- package/agent/apps/desktop/src/app/right-sidebar/files/remote-picker.tsx +177 -0
- package/agent/apps/desktop/src/app/right-sidebar/files/tree.tsx +35 -21
- package/agent/apps/desktop/src/app/right-sidebar/files/use-project-tree.test.ts +75 -3
- package/agent/apps/desktop/src/app/right-sidebar/files/use-project-tree.ts +152 -5
- package/agent/apps/desktop/src/app/right-sidebar/index.test.tsx +75 -0
- package/agent/apps/desktop/src/app/right-sidebar/index.tsx +166 -129
- package/agent/apps/desktop/src/app/right-sidebar/store.ts +19 -4
- package/agent/apps/desktop/src/app/right-sidebar/terminal/buffer.ts +65 -0
- package/agent/apps/desktop/src/app/right-sidebar/terminal/index.tsx +29 -34
- package/agent/apps/desktop/src/app/right-sidebar/terminal/persistent.tsx +18 -6
- package/agent/apps/desktop/src/app/right-sidebar/terminal/selection.ts +93 -32
- package/agent/apps/desktop/src/app/right-sidebar/terminal/use-terminal-session.ts +381 -119
- package/agent/apps/desktop/src/app/routes.ts +9 -0
- package/agent/apps/desktop/src/app/session/hooks/use-cwd-actions.ts +17 -7
- package/agent/apps/desktop/src/app/session/hooks/use-message-stream.ts +365 -47
- package/agent/apps/desktop/src/app/session/hooks/use-model-controls.test.tsx +198 -0
- package/agent/apps/desktop/src/app/session/hooks/use-model-controls.ts +70 -34
- package/agent/apps/desktop/src/app/session/hooks/use-prompt-actions.test.tsx +1061 -0
- package/agent/apps/desktop/src/app/session/hooks/use-prompt-actions.ts +1143 -165
- package/agent/apps/desktop/src/app/session/hooks/use-route-resume.test.tsx +341 -2
- package/agent/apps/desktop/src/app/session/hooks/use-route-resume.ts +176 -5
- package/agent/apps/desktop/src/app/session/hooks/use-session-actions.test.tsx +259 -0
- package/agent/apps/desktop/src/app/session/hooks/use-session-actions.ts +452 -149
- package/agent/apps/desktop/src/app/session/hooks/use-session-state-cache.test.tsx +327 -0
- package/agent/apps/desktop/src/app/session/hooks/use-session-state-cache.ts +133 -4
- package/agent/apps/desktop/src/app/session-picker-overlay.tsx +32 -0
- package/agent/apps/desktop/src/app/session-switcher.tsx +107 -0
- package/agent/apps/desktop/src/app/settings/about-settings.tsx +45 -36
- package/agent/apps/desktop/src/app/settings/appearance-settings.tsx +243 -162
- package/agent/apps/desktop/src/app/settings/config-settings.tsx +86 -66
- package/agent/apps/desktop/src/app/settings/constants.ts +459 -122
- package/agent/apps/desktop/src/app/settings/credential-key-ui.tsx +373 -0
- package/agent/apps/desktop/src/app/settings/env-credentials.tsx +198 -0
- package/agent/apps/desktop/src/app/settings/env-var-actions-menu.tsx +136 -0
- package/agent/apps/desktop/src/app/settings/field-copy.ts +56 -0
- package/agent/apps/desktop/src/app/settings/gateway-settings.tsx +385 -72
- package/agent/apps/desktop/src/app/settings/helpers.test.ts +156 -1
- package/agent/apps/desktop/src/app/settings/helpers.ts +30 -2
- package/agent/apps/desktop/src/app/settings/index.tsx +118 -84
- package/agent/apps/desktop/src/app/settings/keys-settings.tsx +62 -419
- package/agent/apps/desktop/src/app/settings/mcp-settings.tsx +65 -60
- package/agent/apps/desktop/src/app/settings/model-settings.test.tsx +129 -5
- package/agent/apps/desktop/src/app/settings/model-settings.tsx +370 -65
- package/agent/apps/desktop/src/app/settings/notifications-settings.tsx +150 -0
- package/agent/apps/desktop/src/app/settings/primitives.tsx +5 -11
- package/agent/apps/desktop/src/app/settings/provider-config-panel.test.tsx +142 -0
- package/agent/apps/desktop/src/app/settings/provider-config-panel.tsx +182 -0
- package/agent/apps/desktop/src/app/settings/providers-settings.test.tsx +171 -0
- package/agent/apps/desktop/src/app/settings/providers-settings.tsx +471 -0
- package/agent/apps/desktop/src/app/settings/sessions-settings.tsx +183 -71
- package/agent/apps/desktop/src/app/settings/toolset-config-panel.test.tsx +135 -1
- package/agent/apps/desktop/src/app/settings/toolset-config-panel.tsx +180 -57
- package/agent/apps/desktop/src/app/settings/types.ts +9 -6
- package/agent/apps/desktop/src/app/settings/uninstall-section.tsx +185 -0
- package/agent/apps/desktop/src/app/settings/use-deep-link-highlight.ts +60 -0
- package/agent/apps/desktop/src/app/shell/app-shell.tsx +59 -13
- package/agent/apps/desktop/src/app/shell/gateway-menu-panel.tsx +37 -32
- package/agent/apps/desktop/src/app/shell/hooks/use-overlay-routing.ts +6 -3
- package/agent/apps/desktop/src/app/shell/hooks/use-statusbar-items.tsx +212 -53
- package/agent/apps/desktop/src/app/shell/keybind-panel.tsx +215 -0
- package/agent/apps/desktop/src/app/shell/model-edit-submenu.test.tsx +84 -0
- package/agent/apps/desktop/src/app/shell/model-edit-submenu.tsx +244 -0
- package/agent/apps/desktop/src/app/shell/model-menu-panel.tsx +392 -0
- package/agent/apps/desktop/src/app/shell/statusbar-controls.tsx +23 -33
- package/agent/apps/desktop/src/app/shell/titlebar-controls.tsx +79 -95
- package/agent/apps/desktop/src/app/shell/titlebar.ts +8 -2
- package/agent/apps/desktop/src/app/skills/index.test.tsx +11 -0
- package/agent/apps/desktop/src/app/skills/index.tsx +79 -64
- package/agent/apps/desktop/src/app/types.ts +85 -0
- package/agent/apps/desktop/src/app/updates-overlay.tsx +110 -105
- package/agent/apps/desktop/src/components/assistant-ui/ansi-text.tsx +34 -0
- package/agent/apps/desktop/src/components/assistant-ui/block-direction.test.tsx +129 -0
- package/agent/apps/desktop/src/components/assistant-ui/clarify-tool.tsx +102 -81
- package/agent/apps/desktop/src/components/assistant-ui/directive-text.tsx +92 -15
- package/agent/apps/desktop/src/components/assistant-ui/markdown-text.test.ts +38 -0
- package/agent/apps/desktop/src/components/assistant-ui/markdown-text.tsx +304 -45
- package/agent/apps/desktop/src/components/assistant-ui/message-render-boundary.test.tsx +80 -0
- package/agent/apps/desktop/src/components/assistant-ui/message-render-boundary.tsx +48 -0
- package/agent/apps/desktop/src/components/assistant-ui/streaming.test.tsx +142 -90
- package/agent/apps/desktop/src/components/assistant-ui/thread-list.tsx +337 -0
- package/agent/apps/desktop/src/components/assistant-ui/thread.tsx +667 -190
- package/agent/apps/desktop/src/components/assistant-ui/tool-approval-group.test.tsx +299 -0
- package/agent/apps/desktop/src/components/assistant-ui/tool-approval.test.tsx +133 -0
- package/agent/apps/desktop/src/components/assistant-ui/tool-approval.tsx +239 -0
- package/agent/apps/desktop/src/components/assistant-ui/tool-fallback-model.test.ts +31 -0
- package/agent/apps/desktop/src/components/assistant-ui/tool-fallback-model.ts +152 -134
- package/agent/apps/desktop/src/components/assistant-ui/tool-fallback.tsx +142 -150
- package/agent/apps/desktop/src/components/assistant-ui/tooltip-icon-button.tsx +14 -12
- package/agent/apps/desktop/src/components/assistant-ui/user-message-edit.test.tsx +141 -0
- package/agent/apps/desktop/src/components/assistant-ui/user-message-text.tsx +152 -0
- package/agent/apps/desktop/src/components/boot-failure-overlay.tsx +150 -33
- package/agent/apps/desktop/src/components/boot-failure-reauth.test.ts +100 -0
- package/agent/apps/desktop/src/components/boot-failure-reauth.ts +81 -0
- package/agent/apps/desktop/src/components/brand-mark.tsx +19 -0
- package/agent/apps/desktop/src/components/chat/code-card.tsx +1 -1
- package/agent/apps/desktop/src/components/chat/composer-dock.ts +31 -0
- package/agent/apps/desktop/src/components/chat/diff-lines.tsx +1 -1
- package/agent/apps/desktop/src/components/chat/disclosure-row.tsx +13 -3
- package/agent/apps/desktop/src/components/chat/expandable-block.tsx +52 -0
- package/agent/apps/desktop/src/components/chat/generated-image-result.tsx +174 -0
- package/agent/apps/desktop/src/components/chat/image-generation-placeholder.tsx +70 -37
- package/agent/apps/desktop/src/components/chat/intro.tsx +8 -7
- package/agent/apps/desktop/src/components/chat/preview-attachment.tsx +4 -2
- package/agent/apps/desktop/src/components/chat/shiki-highlighter.test.ts +37 -0
- package/agent/apps/desktop/src/components/chat/shiki-highlighter.tsx +96 -22
- package/agent/apps/desktop/src/components/chat/status-row.tsx +70 -0
- package/agent/apps/desktop/src/components/chat/status-section.tsx +42 -0
- package/agent/apps/desktop/src/components/chat/terminal-output.tsx +54 -0
- package/agent/apps/desktop/src/components/chat/zoomable-image.tsx +70 -109
- package/agent/apps/desktop/src/components/desktop-install-overlay.tsx +154 -84
- package/agent/apps/desktop/src/components/desktop-onboarding-overlay.test.tsx +38 -8
- package/agent/apps/desktop/src/components/desktop-onboarding-overlay.tsx +789 -233
- package/agent/apps/desktop/src/components/error-boundary.tsx +77 -0
- package/agent/apps/desktop/src/components/gateway-connecting-overlay.test.tsx +144 -0
- package/agent/apps/desktop/src/components/gateway-connecting-overlay.tsx +7 -1
- package/agent/apps/desktop/src/components/haptics-provider.tsx +24 -0
- package/agent/apps/desktop/src/components/language-switcher.test.tsx +53 -0
- package/agent/apps/desktop/src/components/language-switcher.tsx +175 -0
- package/agent/apps/desktop/src/components/model-picker.tsx +42 -40
- package/agent/apps/desktop/src/components/model-visibility-dialog.tsx +166 -0
- package/agent/apps/desktop/src/components/notifications.tsx +48 -27
- package/agent/apps/desktop/src/components/pane-shell/index.ts +1 -1
- package/agent/apps/desktop/src/components/pane-shell/pane-shell.tsx +146 -9
- package/agent/apps/desktop/src/components/prompt-overlays.tsx +234 -0
- package/agent/apps/desktop/src/components/session-picker.tsx +108 -0
- package/agent/apps/desktop/src/components/ui/action-status.tsx +25 -0
- package/agent/apps/desktop/src/components/ui/badge.tsx +35 -0
- package/agent/apps/desktop/src/components/ui/button.tsx +37 -13
- package/agent/apps/desktop/src/components/ui/confirm-dialog.tsx +109 -0
- package/agent/apps/desktop/src/components/ui/control.ts +25 -0
- package/agent/apps/desktop/src/components/ui/copy-button.test.tsx +36 -0
- package/agent/apps/desktop/src/components/ui/copy-button.tsx +38 -27
- package/agent/apps/desktop/src/components/ui/dialog.tsx +39 -11
- package/agent/apps/desktop/src/components/ui/dropdown-menu.tsx +98 -24
- package/agent/apps/desktop/src/components/ui/error-state.tsx +50 -0
- package/agent/apps/desktop/src/components/ui/fade-text.tsx +9 -2
- package/agent/apps/desktop/src/components/ui/{braille-spinner.tsx → glyph-spinner.tsx} +15 -13
- package/agent/apps/desktop/src/components/ui/input.tsx +5 -2
- package/agent/apps/desktop/src/components/ui/kbd.tsx +83 -12
- package/agent/apps/desktop/src/components/ui/log-view.tsx +19 -0
- package/agent/apps/desktop/src/components/ui/pagination.tsx +12 -5
- package/agent/apps/desktop/src/components/ui/popover.tsx +44 -0
- package/agent/apps/desktop/src/components/ui/search-field.tsx +80 -0
- package/agent/apps/desktop/src/components/ui/segmented-control.tsx +51 -0
- package/agent/apps/desktop/src/components/ui/select.tsx +10 -3
- package/agent/apps/desktop/src/components/ui/sheet.tsx +8 -2
- package/agent/apps/desktop/src/components/ui/sidebar.tsx +18 -25
- package/agent/apps/desktop/src/components/ui/switch.tsx +38 -15
- package/agent/apps/desktop/src/components/ui/textarea.tsx +4 -11
- package/agent/apps/desktop/src/components/ui/tool-icon.tsx +65 -0
- package/agent/apps/desktop/src/components/ui/tooltip.tsx +31 -4
- package/agent/apps/desktop/src/fonts/JetBrainsMono-Bold.woff2 +0 -0
- package/agent/apps/desktop/src/fonts/JetBrainsMono-Italic.woff2 +0 -0
- package/agent/apps/desktop/src/fonts/JetBrainsMono-Regular.woff2 +0 -0
- package/agent/apps/desktop/src/global.d.ts +181 -4
- package/agent/apps/desktop/src/hermes.test.ts +60 -0
- package/agent/apps/desktop/src/hermes.ts +190 -13
- package/agent/apps/desktop/src/hooks/use-image-download.ts +85 -0
- package/agent/apps/desktop/src/hooks/use-resize-observer.ts +13 -4
- package/agent/apps/desktop/src/hooks/use-worktree-info.ts +68 -0
- package/agent/apps/desktop/src/i18n/catalog.ts +12 -0
- package/agent/apps/desktop/src/i18n/context.test.tsx +232 -0
- package/agent/apps/desktop/src/i18n/context.tsx +183 -0
- package/agent/apps/desktop/src/i18n/define-locale.ts +41 -0
- package/agent/apps/desktop/src/i18n/en.ts +1921 -0
- package/agent/apps/desktop/src/i18n/index.ts +20 -0
- package/agent/apps/desktop/src/i18n/ja.ts +2053 -0
- package/agent/apps/desktop/src/i18n/languages.test.ts +43 -0
- package/agent/apps/desktop/src/i18n/languages.ts +86 -0
- package/agent/apps/desktop/src/i18n/runtime.test.ts +75 -0
- package/agent/apps/desktop/src/i18n/runtime.ts +53 -0
- package/agent/apps/desktop/src/i18n/types.ts +1559 -0
- package/agent/apps/desktop/src/i18n/zh-hant.ts +1992 -0
- package/agent/apps/desktop/src/i18n/zh.ts +2099 -0
- package/agent/apps/desktop/src/lib/ansi.test.ts +123 -0
- package/agent/apps/desktop/src/lib/ansi.ts +186 -0
- package/agent/apps/desktop/src/lib/chat-messages.test.ts +79 -0
- package/agent/apps/desktop/src/lib/chat-messages.ts +68 -29
- package/agent/apps/desktop/src/lib/chat-runtime.test.ts +65 -1
- package/agent/apps/desktop/src/lib/chat-runtime.ts +39 -3
- package/agent/apps/desktop/src/lib/completion-sound.ts +519 -0
- package/agent/apps/desktop/src/lib/desktop-fs.test.ts +116 -0
- package/agent/apps/desktop/src/lib/desktop-fs.ts +113 -0
- package/agent/apps/desktop/src/lib/desktop-slash-commands.test.ts +89 -6
- package/agent/apps/desktop/src/lib/desktop-slash-commands.ts +270 -131
- package/agent/apps/desktop/src/lib/external-link.test.tsx +27 -0
- package/agent/apps/desktop/src/lib/external-link.tsx +9 -2
- package/agent/apps/desktop/src/lib/gateway-events.test.ts +27 -0
- package/agent/apps/desktop/src/lib/gateway-events.ts +16 -0
- package/agent/apps/desktop/src/lib/gateway-ws-url.test.ts +78 -0
- package/agent/apps/desktop/src/lib/gateway-ws-url.ts +91 -0
- package/agent/apps/desktop/src/lib/generated-images.test.ts +97 -0
- package/agent/apps/desktop/src/lib/generated-images.ts +116 -0
- package/agent/apps/desktop/src/lib/haptics.ts +17 -0
- package/agent/apps/desktop/src/lib/icons.ts +10 -2
- package/agent/apps/desktop/src/lib/keybinds/actions.ts +137 -0
- package/agent/apps/desktop/src/lib/keybinds/combo.test.ts +86 -0
- package/agent/apps/desktop/src/lib/keybinds/combo.ts +195 -0
- package/agent/apps/desktop/src/lib/local-preview.ts +23 -2
- package/agent/apps/desktop/src/lib/markdown-preprocess.ts +20 -7
- package/agent/apps/desktop/src/lib/media.remote.test.ts +90 -0
- package/agent/apps/desktop/src/lib/media.ts +40 -1
- package/agent/apps/desktop/src/lib/model-status-label.test.ts +59 -0
- package/agent/apps/desktop/src/lib/model-status-label.ts +122 -0
- package/agent/apps/desktop/src/lib/mutable-ref.ts +6 -0
- package/agent/apps/desktop/src/lib/profile-color.ts +58 -0
- package/agent/apps/desktop/src/lib/query-client.ts +13 -0
- package/agent/apps/desktop/src/lib/remend-tail.test.ts +105 -0
- package/agent/apps/desktop/src/lib/remend-tail.ts +108 -0
- package/agent/apps/desktop/src/lib/session-export.ts +6 -3
- package/agent/apps/desktop/src/lib/session-ids.test.ts +44 -0
- package/agent/apps/desktop/src/lib/session-ids.ts +26 -0
- package/agent/apps/desktop/src/lib/session-search.test.ts +66 -0
- package/agent/apps/desktop/src/lib/session-search.ts +21 -0
- package/agent/apps/desktop/src/lib/session-source.ts +126 -0
- package/agent/apps/desktop/src/lib/storage.test.ts +25 -0
- package/agent/apps/desktop/src/lib/storage.ts +35 -1
- package/agent/apps/desktop/src/lib/todos.test.ts +46 -1
- package/agent/apps/desktop/src/lib/todos.ts +37 -0
- package/agent/apps/desktop/src/lib/tool-result-summary.ts +5 -1
- package/agent/apps/desktop/src/lib/update-copy.test.ts +38 -0
- package/agent/apps/desktop/src/lib/update-copy.ts +44 -0
- package/agent/apps/desktop/src/lib/use-enter-animation.ts +2 -2
- package/agent/apps/desktop/src/lib/yolo-session.ts +50 -0
- package/agent/apps/desktop/src/main.tsx +19 -19
- package/agent/apps/desktop/src/store/boot.ts +4 -3
- package/agent/apps/desktop/src/store/clarify.test.ts +81 -0
- package/agent/apps/desktop/src/store/clarify.ts +50 -13
- package/agent/apps/desktop/src/store/command-palette.ts +20 -0
- package/agent/apps/desktop/src/store/compaction.test.ts +53 -0
- package/agent/apps/desktop/src/store/compaction.ts +38 -0
- package/agent/apps/desktop/src/store/completion-sound.ts +32 -0
- package/agent/apps/desktop/src/store/composer-input-history.test.ts +147 -0
- package/agent/apps/desktop/src/store/composer-input-history.ts +158 -0
- package/agent/apps/desktop/src/store/composer-queue.test.ts +68 -0
- package/agent/apps/desktop/src/store/composer-queue.ts +76 -0
- package/agent/apps/desktop/src/store/composer-status.test.ts +99 -0
- package/agent/apps/desktop/src/store/composer-status.ts +277 -0
- package/agent/apps/desktop/src/store/composer.test.ts +106 -0
- package/agent/apps/desktop/src/store/composer.ts +116 -0
- package/agent/apps/desktop/src/store/cron.ts +19 -0
- package/agent/apps/desktop/src/store/gateway.ts +280 -6
- package/agent/apps/desktop/src/store/keybinds.ts +143 -0
- package/agent/apps/desktop/src/store/layout.ts +107 -9
- package/agent/apps/desktop/src/store/model-presets.test.ts +51 -0
- package/agent/apps/desktop/src/store/model-presets.ts +86 -0
- package/agent/apps/desktop/src/store/model-visibility.test.ts +99 -0
- package/agent/apps/desktop/src/store/model-visibility.ts +161 -0
- package/agent/apps/desktop/src/store/native-notifications.test.ts +192 -0
- package/agent/apps/desktop/src/store/native-notifications.ts +203 -0
- package/agent/apps/desktop/src/store/notifications.ts +10 -7
- package/agent/apps/desktop/src/store/onboarding.test.ts +271 -1
- package/agent/apps/desktop/src/store/onboarding.ts +268 -38
- package/agent/apps/desktop/src/store/preview.ts +10 -1
- package/agent/apps/desktop/src/store/profile.test.ts +89 -0
- package/agent/apps/desktop/src/store/profile.ts +395 -0
- package/agent/apps/desktop/src/store/prompts.test.ts +127 -0
- package/agent/apps/desktop/src/store/prompts.ts +117 -0
- package/agent/apps/desktop/src/store/session-switcher.test.ts +115 -0
- package/agent/apps/desktop/src/store/session-switcher.ts +128 -0
- package/agent/apps/desktop/src/store/session-sync.ts +25 -0
- package/agent/apps/desktop/src/store/session.test.ts +268 -2
- package/agent/apps/desktop/src/store/session.ts +392 -18
- package/agent/apps/desktop/src/store/subagents.ts +3 -0
- package/agent/apps/desktop/src/store/system-actions.ts +48 -0
- package/agent/apps/desktop/src/store/thread-scroll.ts +58 -5
- package/agent/apps/desktop/src/store/todos.test.ts +47 -0
- package/agent/apps/desktop/src/store/todos.ts +64 -0
- package/agent/apps/desktop/src/store/tool-dismiss.ts +45 -0
- package/agent/apps/desktop/src/store/translucency.ts +38 -0
- package/agent/apps/desktop/src/store/updates.test.ts +187 -2
- package/agent/apps/desktop/src/store/updates.ts +268 -18
- package/agent/apps/desktop/src/store/windows.test.ts +143 -0
- package/agent/apps/desktop/src/store/windows.ts +115 -0
- package/agent/apps/desktop/src/styles.css +510 -119
- package/agent/apps/desktop/src/themes/color.ts +142 -0
- package/agent/apps/desktop/src/themes/context.tsx +128 -75
- package/agent/apps/desktop/src/themes/install.test.ts +119 -0
- package/agent/apps/desktop/src/themes/install.ts +95 -0
- package/agent/apps/desktop/src/themes/presets.test.ts +33 -0
- package/agent/apps/desktop/src/themes/presets.ts +13 -4
- package/agent/apps/desktop/src/themes/profile-theme.test.ts +41 -0
- package/agent/apps/desktop/src/themes/types.ts +35 -0
- package/agent/apps/desktop/src/themes/user-themes.test.ts +63 -0
- package/agent/apps/desktop/src/themes/user-themes.ts +122 -0
- package/agent/apps/desktop/src/themes/vscode.test.ts +171 -0
- package/agent/apps/desktop/src/themes/vscode.ts +343 -0
- package/agent/apps/desktop/src/types/hermes.ts +138 -1
- package/agent/apps/desktop/tsconfig.json +2 -2
- package/agent/apps/desktop/vite.config.ts +18 -0
- package/agent/apps/shared/package.json +1 -1
- package/agent/apps/shared/src/json-rpc-gateway.ts +63 -2
- package/agent/apps/shared/tsconfig.json +2 -2
- package/agent/cli-config.yaml.example +78 -1
- package/agent/cli.py +2177 -3162
- package/agent/cron/blueprint_catalog.py +713 -0
- package/agent/cron/jobs.py +226 -110
- package/agent/cron/scheduler.py +468 -193
- package/agent/cron/scheduler_provider.py +177 -0
- package/agent/cron/scripts/__init__.py +1 -0
- package/agent/cron/scripts/classify_items.py +226 -0
- package/agent/cron/suggestion_catalog.py +154 -0
- package/agent/cron/suggestions.py +257 -0
- package/agent/docs/chronos-managed-cron-contract.md +196 -0
- package/agent/docs/design/profile-builder.md +146 -0
- package/agent/docs/middleware/README.md +260 -0
- package/agent/docs/observability/README.md +316 -0
- package/agent/docs/plans/2026-06-09-003-fix-telegram-stream-overflow-continuations-plan.md +240 -0
- package/agent/docs/rca-ssl-cacert-post-git-pull.md +54 -0
- package/agent/docs/relay-connector-contract.md +285 -0
- package/agent/gateway/authz_mixin.py +536 -0
- package/agent/gateway/channel_directory.py +65 -3
- package/agent/gateway/config.py +222 -12
- package/agent/gateway/display_config.py +10 -0
- package/agent/gateway/hooks.py +17 -0
- package/agent/gateway/kanban_watchers.py +1146 -0
- package/agent/gateway/message_timestamps.py +166 -0
- package/agent/gateway/platforms/ADDING_A_PLATFORM.md +29 -0
- package/agent/gateway/platforms/api_server.py +216 -38
- package/agent/gateway/platforms/base.py +210 -58
- package/agent/gateway/platforms/email.py +122 -12
- package/agent/gateway/platforms/feishu.py +80 -11
- package/agent/gateway/platforms/feishu_meeting_invite.py +212 -0
- package/agent/gateway/platforms/matrix.py +1498 -297
- package/agent/gateway/platforms/qqbot/adapter.py +6 -0
- package/agent/gateway/platforms/signal.py +8 -0
- package/agent/gateway/platforms/slack.py +308 -12
- package/agent/gateway/platforms/telegram.py +831 -24
- package/agent/gateway/platforms/webhook.py +109 -21
- package/agent/gateway/platforms/weixin.py +113 -2
- package/agent/gateway/platforms/whatsapp.py +94 -288
- package/agent/gateway/platforms/whatsapp_cloud.py +1956 -0
- package/agent/gateway/platforms/whatsapp_common.py +367 -0
- package/agent/gateway/platforms/yuanbao.py +608 -191
- package/agent/gateway/platforms/yuanbao_proto.py +232 -23
- package/agent/gateway/relay/__init__.py +375 -0
- package/agent/gateway/relay/adapter.py +222 -0
- package/agent/gateway/relay/auth.py +168 -0
- package/agent/gateway/relay/descriptor.py +118 -0
- package/agent/gateway/relay/transport.py +101 -0
- package/agent/gateway/relay/ws_transport.py +327 -0
- package/agent/gateway/response_filters.py +53 -0
- package/agent/gateway/rich_sent_store.py +80 -0
- package/agent/gateway/run.py +2940 -5001
- package/agent/gateway/session.py +109 -8
- package/agent/gateway/session_context.py +22 -4
- package/agent/gateway/slash_commands.py +3854 -0
- package/agent/gateway/status.py +141 -21
- package/agent/gateway/stream_consumer.py +288 -31
- package/agent/hermes-already-has-routines.md +1 -1
- package/agent/hermes_cli/__init__.py +62 -17
- package/agent/hermes_cli/_parser.py +30 -0
- package/agent/hermes_cli/_subprocess_compat.py +61 -0
- package/agent/hermes_cli/active_sessions.py +320 -0
- package/agent/hermes_cli/auth.py +707 -59
- package/agent/hermes_cli/auth_commands.py +39 -22
- package/agent/hermes_cli/backup.py +109 -7
- package/agent/hermes_cli/banner.py +88 -0
- package/agent/hermes_cli/blueprint_cmd.py +318 -0
- package/agent/hermes_cli/cli_agent_setup_mixin.py +684 -0
- package/agent/hermes_cli/cli_commands_mixin.py +2293 -0
- package/agent/hermes_cli/commands.py +215 -91
- package/agent/hermes_cli/config.py +967 -130
- package/agent/hermes_cli/container_boot.py +76 -11
- package/agent/hermes_cli/cron.py +5 -11
- package/agent/hermes_cli/curator.py +21 -0
- package/agent/hermes_cli/dashboard_auth/__init__.py +2 -0
- package/agent/hermes_cli/dashboard_auth/base.py +62 -0
- package/agent/hermes_cli/dashboard_auth/cookies.py +32 -19
- package/agent/hermes_cli/dashboard_auth/login_page.py +156 -6
- package/agent/hermes_cli/dashboard_auth/middleware.py +28 -4
- package/agent/hermes_cli/dashboard_auth/prefix.py +46 -2
- package/agent/hermes_cli/dashboard_auth/public_paths.py +6 -0
- package/agent/hermes_cli/dashboard_auth/routes.py +158 -2
- package/agent/hermes_cli/dashboard_auth/ws_tickets.py +85 -11
- package/agent/hermes_cli/dashboard_register.py +427 -0
- package/agent/hermes_cli/debug.py +155 -50
- package/agent/hermes_cli/doctor.py +255 -14
- package/agent/hermes_cli/dump.py +60 -6
- package/agent/hermes_cli/env_loader.py +33 -0
- package/agent/hermes_cli/gateway.py +755 -103
- package/agent/hermes_cli/gateway_enroll.py +250 -0
- package/agent/hermes_cli/gateway_windows.py +254 -11
- package/agent/hermes_cli/gui_uninstall.py +285 -0
- package/agent/hermes_cli/inventory.py +105 -4
- package/agent/hermes_cli/kanban.py +58 -71
- package/agent/hermes_cli/kanban_db.py +391 -14
- package/agent/hermes_cli/kanban_decompose.py +2 -2
- package/agent/hermes_cli/kanban_specify.py +3 -1
- package/agent/hermes_cli/logs.py +2 -0
- package/agent/hermes_cli/main.py +2889 -5287
- package/agent/hermes_cli/managed_scope.py +214 -0
- package/agent/hermes_cli/managed_uv.py +254 -0
- package/agent/hermes_cli/mcp_catalog.py +6 -3
- package/agent/hermes_cli/mcp_config.py +145 -21
- package/agent/hermes_cli/mcp_security.py +96 -0
- package/agent/hermes_cli/mcp_startup.py +32 -3
- package/agent/hermes_cli/memory_providers.py +149 -0
- package/agent/hermes_cli/memory_setup.py +97 -42
- package/agent/hermes_cli/middleware.py +313 -0
- package/agent/hermes_cli/model_catalog.py +31 -0
- package/agent/hermes_cli/model_cost_guard.py +134 -0
- package/agent/hermes_cli/model_normalize.py +2 -1
- package/agent/hermes_cli/model_setup_flows.py +2759 -0
- package/agent/hermes_cli/model_switch.py +242 -27
- package/agent/hermes_cli/models.py +284 -44
- package/agent/hermes_cli/nous_account.py +33 -6
- package/agent/hermes_cli/nous_billing.py +406 -0
- package/agent/hermes_cli/nous_subscription.py +202 -5
- package/agent/hermes_cli/platforms.py +1 -0
- package/agent/hermes_cli/plugins.py +218 -18
- package/agent/hermes_cli/plugins_cmd.py +249 -105
- package/agent/hermes_cli/portal_cli.py +56 -16
- package/agent/hermes_cli/profile_distribution.py +6 -1
- package/agent/hermes_cli/profiles.py +283 -32
- package/agent/hermes_cli/provider_catalog.py +170 -0
- package/agent/hermes_cli/providers.py +4 -1
- package/agent/hermes_cli/pty_bridge.py +53 -4
- package/agent/hermes_cli/runtime_provider.py +216 -34
- package/agent/hermes_cli/secret_prompt.py +4 -4
- package/agent/hermes_cli/secrets_cli.py +24 -0
- package/agent/hermes_cli/send_cmd.py +28 -2
- package/agent/hermes_cli/service_manager.py +166 -19
- package/agent/hermes_cli/session_listing.py +97 -0
- package/agent/hermes_cli/setup.py +158 -94
- package/agent/hermes_cli/setup_whatsapp_cloud.py +541 -0
- package/agent/hermes_cli/skills_config.py +8 -2
- package/agent/hermes_cli/skills_hub.py +149 -7
- package/agent/hermes_cli/status.py +2 -2
- package/agent/hermes_cli/subcommands/__init__.py +18 -0
- package/agent/hermes_cli/subcommands/_shared.py +29 -0
- package/agent/hermes_cli/subcommands/acp.py +52 -0
- package/agent/hermes_cli/subcommands/auth.py +109 -0
- package/agent/hermes_cli/subcommands/backup.py +38 -0
- package/agent/hermes_cli/subcommands/claw.py +92 -0
- package/agent/hermes_cli/subcommands/config.py +49 -0
- package/agent/hermes_cli/subcommands/cron.py +163 -0
- package/agent/hermes_cli/subcommands/dashboard.py +143 -0
- package/agent/hermes_cli/subcommands/debug.py +77 -0
- package/agent/hermes_cli/subcommands/doctor.py +35 -0
- package/agent/hermes_cli/subcommands/dump.py +28 -0
- package/agent/hermes_cli/subcommands/gateway.py +332 -0
- package/agent/hermes_cli/subcommands/gui.py +63 -0
- package/agent/hermes_cli/subcommands/hooks.py +77 -0
- package/agent/hermes_cli/subcommands/import_cmd.py +31 -0
- package/agent/hermes_cli/subcommands/insights.py +25 -0
- package/agent/hermes_cli/subcommands/login.py +78 -0
- package/agent/hermes_cli/subcommands/logout.py +28 -0
- package/agent/hermes_cli/subcommands/logs.py +78 -0
- package/agent/hermes_cli/subcommands/mcp.py +108 -0
- package/agent/hermes_cli/subcommands/memory.py +53 -0
- package/agent/hermes_cli/subcommands/model.py +72 -0
- package/agent/hermes_cli/subcommands/pairing.py +36 -0
- package/agent/hermes_cli/subcommands/plugins.py +94 -0
- package/agent/hermes_cli/subcommands/postinstall.py +23 -0
- package/agent/hermes_cli/subcommands/profile.py +203 -0
- package/agent/hermes_cli/subcommands/prompt_size.py +36 -0
- package/agent/hermes_cli/subcommands/security.py +62 -0
- package/agent/hermes_cli/subcommands/setup.py +58 -0
- package/agent/hermes_cli/subcommands/skills.py +298 -0
- package/agent/hermes_cli/subcommands/slack.py +60 -0
- package/agent/hermes_cli/subcommands/status.py +28 -0
- package/agent/hermes_cli/subcommands/tools.py +95 -0
- package/agent/hermes_cli/subcommands/uninstall.py +41 -0
- package/agent/hermes_cli/subcommands/update.py +70 -0
- package/agent/hermes_cli/subcommands/version.py +18 -0
- package/agent/hermes_cli/subcommands/webhook.py +76 -0
- package/agent/hermes_cli/subcommands/whatsapp.py +22 -0
- package/agent/hermes_cli/suggestions_cmd.py +153 -0
- package/agent/hermes_cli/telegram_managed_bot.py +358 -0
- package/agent/hermes_cli/tips.py +3 -4
- package/agent/hermes_cli/tools_config.py +155 -28
- package/agent/hermes_cli/uninstall.py +231 -35
- package/agent/hermes_cli/web_server.py +6188 -975
- package/agent/hermes_cli/win_pty_bridge.py +179 -0
- package/agent/hermes_cli/write_approval_commands.py +209 -0
- package/agent/hermes_constants.py +164 -33
- package/agent/hermes_logging.py +74 -2
- package/agent/hermes_state.py +919 -106
- package/agent/hermes_time.py +20 -0
- package/agent/locales/af.yaml +23 -0
- package/agent/locales/de.yaml +23 -0
- package/agent/locales/en.yaml +20 -0
- package/agent/locales/es.yaml +23 -0
- package/agent/locales/fr.yaml +23 -0
- package/agent/locales/ga.yaml +23 -0
- package/agent/locales/hu.yaml +23 -0
- package/agent/locales/it.yaml +23 -0
- package/agent/locales/ja.yaml +23 -0
- package/agent/locales/ko.yaml +23 -0
- package/agent/locales/pt.yaml +23 -0
- package/agent/locales/ru.yaml +23 -0
- package/agent/locales/tr.yaml +23 -0
- package/agent/locales/uk.yaml +23 -0
- package/agent/locales/zh-hant.yaml +23 -0
- package/agent/locales/zh.yaml +23 -0
- package/agent/model_tools.py +204 -40
- package/agent/optional-mcps/clawpump/manifest.yaml +4 -2
- package/agent/optional-mcps/clawpump-stdio/manifest.yaml +2 -0
- package/agent/optional-mcps/unreal-engine/manifest.yaml +54 -0
- package/agent/optional-skills/blockchain/hyperliquid/SKILL.md +2 -2
- package/agent/optional-skills/blockchain/hyperliquid/scripts/hyperliquid_client.py +1 -1
- package/agent/optional-skills/creative/kanban-video-orchestrator/SKILL.md +1 -1
- package/agent/optional-skills/creative/kanban-video-orchestrator/assets/setup.sh.tmpl +4 -3
- package/agent/optional-skills/creative/kanban-video-orchestrator/references/kanban-setup.md +6 -4
- package/agent/optional-skills/creative/kanban-video-orchestrator/references/tool-matrix.md +2 -2
- package/agent/{skills/software-development → optional-skills/devops}/hermes-s6-container-supervision/SKILL.md +2 -0
- package/agent/optional-skills/devops/watchers/SKILL.md +1 -1
- package/agent/optional-skills/devops/watchers/scripts/watch_github.py +2 -1
- package/agent/optional-skills/payments/mpp-agent/SKILL.md +124 -0
- package/agent/optional-skills/payments/stripe-link-cli/SKILL.md +184 -0
- package/agent/optional-skills/payments/stripe-projects/SKILL.md +120 -0
- package/agent/optional-skills/productivity/canvas/SKILL.md +1 -1
- package/agent/optional-skills/productivity/canvas/scripts/canvas_api.py +4 -1
- package/agent/optional-skills/productivity/shop/SKILL.md +224 -0
- package/agent/optional-skills/productivity/shop/references/catalog-mcp.md +236 -0
- package/agent/optional-skills/productivity/shop/references/direct-api.md +278 -0
- package/agent/optional-skills/productivity/shop/references/legal.md +3 -0
- package/agent/optional-skills/productivity/shop/references/safety.md +36 -0
- package/agent/optional-skills/productivity/shopify/SKILL.md +1 -1
- package/agent/optional-skills/productivity/siyuan/SKILL.md +1 -1
- package/agent/optional-skills/productivity/telephony/SKILL.md +4 -4
- package/agent/optional-skills/productivity/telephony/scripts/telephony.py +15 -15
- package/agent/optional-skills/security/1password/SKILL.md +1 -1
- package/agent/{skills/red-teaming → optional-skills/security}/godmode/SKILL.md +3 -4
- package/agent/{skills/red-teaming → optional-skills/security}/godmode/scripts/auto_jailbreak.py +3 -1
- package/agent/optional-skills/software-development/rest-graphql-debug/SKILL.md +1 -1
- package/agent/{skills → optional-skills}/software-development/subagent-driven-development/SKILL.md +5 -5
- package/agent/package-lock.json +4082 -7907
- package/agent/package.json +18 -3
- package/agent/plugins/browser/firecrawl/provider.py +4 -1
- package/agent/plugins/cron/__init__.py +344 -0
- package/agent/plugins/cron/chronos/__init__.py +241 -0
- package/agent/plugins/cron/chronos/_nas_client.py +123 -0
- package/agent/plugins/cron/chronos/plugin.yaml +9 -0
- package/agent/plugins/cron/chronos/verify.py +103 -0
- package/agent/plugins/dashboard_auth/basic/__init__.py +491 -0
- package/agent/plugins/dashboard_auth/basic/plugin.yaml +7 -0
- package/agent/plugins/dashboard_auth/nous/__init__.py +12 -14
- package/agent/plugins/dashboard_auth/self_hosted/__init__.py +736 -0
- package/agent/plugins/dashboard_auth/self_hosted/plugin.yaml +8 -0
- package/agent/plugins/disk-cleanup/disk_cleanup.py +100 -20
- package/agent/plugins/google_meet/audio_bridge.py +4 -0
- package/agent/plugins/google_meet/meet_bot.py +7 -1
- package/agent/plugins/hermes-achievements/dashboard/dist/index.js +9 -15
- package/agent/plugins/image_gen/fal/__init__.py +35 -6
- package/agent/plugins/image_gen/krea/__init__.py +56 -13
- package/agent/plugins/image_gen/openai/__init__.py +122 -24
- package/agent/plugins/image_gen/openai-codex/__init__.py +28 -2
- package/agent/plugins/image_gen/xai/__init__.py +92 -12
- package/agent/plugins/kanban/dashboard/dist/index.js +63 -48
- package/agent/plugins/kanban/dashboard/plugin_api.py +39 -35
- package/agent/plugins/memory/__init__.py +48 -5
- package/agent/plugins/memory/byterover/__init__.py +1 -0
- package/agent/plugins/memory/hindsight/README.md +1 -1
- package/agent/plugins/memory/hindsight/__init__.py +138 -24
- package/agent/plugins/memory/hindsight/plugin.yaml +1 -1
- package/agent/plugins/memory/honcho/README.md +13 -10
- package/agent/plugins/memory/honcho/cli.py +247 -122
- package/agent/plugins/memory/honcho/client.py +112 -102
- package/agent/plugins/memory/openviking/README.md +12 -1
- package/agent/plugins/memory/openviking/__init__.py +2281 -107
- package/agent/plugins/memory/openviking/plugin.yaml +1 -2
- package/agent/plugins/memory/supermemory/README.md +22 -10
- package/agent/plugins/memory/supermemory/__init__.py +142 -37
- package/agent/plugins/memory/supermemory/plugin.yaml +1 -1
- package/agent/plugins/model-providers/anthropic/__init__.py +1 -0
- package/agent/plugins/model-providers/bedrock/__init__.py +1 -0
- package/agent/plugins/model-providers/copilot-acp/__init__.py +1 -0
- package/agent/plugins/model-providers/custom/__init__.py +8 -2
- package/agent/plugins/model-providers/kimi-coding/__init__.py +16 -7
- package/agent/plugins/model-providers/minimax/__init__.py +60 -8
- package/agent/plugins/model-providers/opencode-zen/__init__.py +12 -3
- package/agent/plugins/model-providers/openrouter/__init__.py +75 -4
- package/agent/plugins/model-providers/xiaomi/__init__.py +2 -0
- package/agent/plugins/model-providers/zai/__init__.py +1 -0
- package/agent/plugins/observability/langfuse/__init__.py +147 -14
- package/agent/plugins/observability/nemo_relay/README.md +559 -0
- package/agent/plugins/observability/nemo_relay/__init__.py +962 -0
- package/agent/plugins/observability/nemo_relay/plugin.yaml +20 -0
- package/agent/plugins/platforms/discord/adapter.py +932 -61
- package/agent/plugins/platforms/discord/voice_mixer.py +379 -0
- package/agent/plugins/platforms/google_chat/adapter.py +9 -3
- package/agent/plugins/platforms/google_chat/oauth.py +1 -1
- package/agent/plugins/platforms/homeassistant/__init__.py +3 -0
- package/agent/{gateway/platforms/homeassistant.py → plugins/platforms/homeassistant/adapter.py} +128 -0
- package/agent/plugins/platforms/homeassistant/plugin.yaml +22 -0
- package/agent/plugins/platforms/irc/adapter.py +4 -1
- package/agent/plugins/platforms/line/adapter.py +16 -1
- package/agent/plugins/platforms/mattermost/adapter.py +100 -24
- package/agent/plugins/platforms/photon/README.md +179 -0
- package/agent/plugins/platforms/photon/__init__.py +4 -0
- package/agent/plugins/platforms/photon/adapter.py +1586 -0
- package/agent/plugins/platforms/photon/auth.py +1046 -0
- package/agent/plugins/platforms/photon/cli.py +439 -0
- package/agent/plugins/platforms/photon/plugin.yaml +88 -0
- package/agent/plugins/platforms/photon/sidecar/README.md +52 -0
- package/agent/plugins/platforms/photon/sidecar/index.mjs +720 -0
- package/agent/plugins/platforms/photon/sidecar/package-lock.json +1730 -0
- package/agent/plugins/platforms/photon/sidecar/package.json +25 -0
- package/agent/plugins/platforms/photon/sidecar/patch-spectrum-mixed-attachments.mjs +155 -0
- package/agent/plugins/platforms/raft/__init__.py +3 -0
- package/agent/plugins/platforms/raft/adapter.py +774 -0
- package/agent/plugins/platforms/raft/plugin.yaml +19 -0
- package/agent/plugins/platforms/simplex/adapter.py +777 -220
- package/agent/plugins/platforms/simplex/plugin.yaml +21 -2
- package/agent/plugins/platforms/teams/adapter.py +175 -5
- package/agent/plugins/plugin_utils.py +135 -0
- package/agent/plugins/video_gen/fal/__init__.py +10 -3
- package/agent/plugins/web/searxng/provider.py +15 -2
- package/agent/plugins/web/xai/provider.py +2 -2
- package/agent/providers/base.py +22 -3
- package/agent/pyproject.toml +115 -21
- package/agent/run_agent.py +733 -39
- package/agent/scripts/build_skills_index.py +51 -19
- package/agent/scripts/check_subprocess_stdin.py +177 -0
- package/agent/scripts/contributor_audit.py +2 -0
- package/agent/scripts/docker_config_migrate.py +67 -0
- package/agent/scripts/install.cmd +3 -3
- package/agent/scripts/install.ps1 +580 -154
- package/agent/scripts/install.sh +402 -185
- package/agent/scripts/lib/node-bootstrap.sh +39 -4
- package/agent/scripts/release.py +183 -0
- package/agent/scripts/run_tests.sh +1 -0
- package/agent/scripts/run_tests_parallel.py +18 -23
- package/agent/scripts/whatsapp-bridge/bridge.js +25 -4
- package/agent/setup.py +59 -0
- package/agent/skills/autonomous-ai-agents/codex/SKILL.md +19 -0
- package/agent/skills/autonomous-ai-agents/hermes-agent/SKILL.md +10 -3
- package/agent/skills/{mcp/native-mcp/SKILL.md → autonomous-ai-agents/hermes-agent/references/native-mcp.md} +0 -13
- package/agent/skills/{devops/webhook-subscriptions/SKILL.md → autonomous-ai-agents/hermes-agent/references/webhooks.md} +1 -11
- package/agent/skills/clawpump/SKILL.md +3 -1
- package/agent/skills/devops/kanban-orchestrator/SKILL.md +1 -0
- package/agent/skills/devops/kanban-worker/SKILL.md +1 -0
- package/agent/skills/github/github-auth/SKILL.md +2 -2
- package/agent/skills/github/github-auth/scripts/gh-env.sh +2 -2
- package/agent/skills/github/github-code-review/SKILL.md +2 -2
- package/agent/skills/github/github-issues/SKILL.md +2 -2
- package/agent/skills/github/github-pr-workflow/SKILL.md +2 -2
- package/agent/skills/github/github-repo-management/SKILL.md +2 -2
- package/agent/skills/media/gif-search/SKILL.md +1 -1
- package/agent/skills/media/youtube-content/SKILL.md +10 -7
- package/agent/skills/media/youtube-content/scripts/fetch_transcript.py +3 -3
- package/agent/skills/note-taking/obsidian/SKILL.md +1 -1
- package/agent/skills/productivity/airtable/SKILL.md +2 -2
- package/agent/skills/productivity/google-workspace/scripts/setup.py +33 -7
- package/agent/skills/productivity/notion/SKILL.md +2 -2
- package/agent/skills/productivity/teams-meeting-pipeline/SKILL.md +1 -1
- package/agent/skills/research/llm-wiki/SKILL.md +1 -1
- package/agent/skills/social-media/xurl/SKILL.md +9 -0
- package/agent/skills/software-development/hermes-agent-skill-authoring/SKILL.md +1 -1
- package/agent/skills/software-development/plan/SKILL.md +285 -5
- package/agent/skills/software-development/requesting-code-review/SKILL.md +2 -2
- package/agent/skills/software-development/simplify-code/SKILL.md +212 -0
- package/agent/skills/software-development/spike/SKILL.md +2 -2
- package/agent/skills/software-development/systematic-debugging/SKILL.md +1 -1
- package/agent/skills/software-development/test-driven-development/SKILL.md +1 -1
- package/agent/tools/approval.py +302 -4
- package/agent/tools/async_delegation.py +386 -0
- package/agent/tools/blueprints.py +325 -0
- package/agent/tools/browser_cdp_tool.py +3 -3
- package/agent/tools/browser_tool.py +34 -6
- package/agent/tools/checkpoint_manager.py +31 -1
- package/agent/tools/clarify_tool.py +55 -5
- package/agent/tools/code_execution_tool.py +31 -14
- package/agent/tools/computer_use/cua_backend.py +81 -3
- package/agent/tools/computer_use/tool.py +79 -5
- package/agent/tools/computer_use/vision_routing.py +55 -3
- package/agent/tools/credential_files.py +31 -12
- package/agent/tools/cronjob_tools.py +30 -20
- package/agent/tools/delegate_tool.py +356 -31
- package/agent/tools/env_probe.py +1 -0
- package/agent/tools/environments/docker.py +163 -8
- package/agent/tools/environments/file_sync.py +2 -1
- package/agent/tools/environments/local.py +74 -23
- package/agent/tools/environments/singularity.py +4 -1
- package/agent/tools/environments/ssh.py +78 -11
- package/agent/tools/file_operations.py +277 -41
- package/agent/tools/file_tools.py +166 -28
- package/agent/tools/image_generation_tool.py +515 -29
- package/agent/tools/kanban_tools.py +99 -0
- package/agent/tools/lazy_deps.py +33 -2
- package/agent/tools/mcp_oauth.py +5 -5
- package/agent/tools/mcp_oauth_manager.py +7 -5
- package/agent/tools/mcp_tool.py +840 -33
- package/agent/tools/memory_tool.py +335 -38
- package/agent/tools/osv_check.py +15 -1
- package/agent/tools/process_registry.py +155 -11
- package/agent/tools/read_extract.py +248 -0
- package/agent/tools/read_terminal_tool.py +93 -0
- package/agent/tools/schema_sanitizer.py +38 -0
- package/agent/tools/send_message_tool.py +163 -49
- package/agent/tools/session_search_tool.py +189 -7
- package/agent/tools/skill_manager_tool.py +202 -3
- package/agent/tools/skill_usage.py +52 -4
- package/agent/tools/skills_hub.py +184 -44
- package/agent/tools/skills_sync.py +232 -5
- package/agent/tools/skills_tool.py +125 -11
- package/agent/tools/terminal_tool.py +148 -26
- package/agent/tools/tirith_security.py +2 -0
- package/agent/tools/todo_tool.py +32 -1
- package/agent/tools/transcription_tools.py +13 -5
- package/agent/tools/tts_tool.py +332 -38
- package/agent/tools/url_safety.py +52 -1
- package/agent/tools/vision_tools.py +124 -39
- package/agent/tools/voice_mode.py +4 -3
- package/agent/tools/web_tools.py +45 -15
- package/agent/tools/write_approval.py +493 -0
- package/agent/toolsets.py +34 -10
- package/agent/trajectory_compressor.py +81 -10
- package/agent/tui_gateway/entry.py +43 -6
- package/agent/tui_gateway/server.py +3335 -330
- package/agent/tui_gateway/slash_worker.py +61 -0
- package/agent/tui_gateway/ws.py +67 -9
- package/agent/ui-tui/eslint.config.mjs +0 -4
- package/agent/ui-tui/package.json +6 -6
- package/agent/ui-tui/packages/hermes-ink/package.json +1 -1
- package/agent/ui-tui/packages/hermes-ink/src/ink/app-mouse.test.ts +34 -1
- package/agent/ui-tui/packages/hermes-ink/src/ink/app-rawmode-mouse.test.ts +91 -0
- package/agent/ui-tui/packages/hermes-ink/src/ink/components/App.tsx +35 -2
- package/agent/ui-tui/packages/hermes-ink/src/ink/events/input-event.ts +4 -11
- package/agent/ui-tui/packages/hermes-ink/src/ink/parse-keypress.test.ts +23 -57
- package/agent/ui-tui/packages/hermes-ink/src/ink/parse-keypress.ts +11 -135
- package/agent/ui-tui/packages/hermes-ink/src/ink/termio/tokenize.test.ts +185 -0
- package/agent/ui-tui/packages/hermes-ink/src/ink/termio/tokenize.ts +37 -3
- package/agent/ui-tui/packages/hermes-ink/src/utils/execFileNoThrow.ts +5 -5
- package/agent/ui-tui/src/__tests__/appChromeStatusRule.test.tsx +217 -0
- package/agent/ui-tui/src/__tests__/appChromeStatusRuleDevCredits.test.tsx +73 -0
- package/agent/ui-tui/src/__tests__/approvalAction.test.ts +11 -0
- package/agent/ui-tui/src/__tests__/billingCommand.test.ts +301 -0
- package/agent/ui-tui/src/__tests__/blockLayout.test.ts +122 -0
- package/agent/ui-tui/src/__tests__/brandingMcpCount.test.ts +111 -0
- package/agent/ui-tui/src/__tests__/completionApply.test.ts +51 -0
- package/agent/ui-tui/src/__tests__/createGatewayEventHandler.test.ts +487 -2
- package/agent/ui-tui/src/__tests__/createSlashHandler.test.ts +54 -0
- package/agent/ui-tui/src/__tests__/creditsCommand.test.ts +144 -0
- package/agent/ui-tui/src/__tests__/gatewayClient.test.ts +120 -99
- package/agent/ui-tui/src/__tests__/gracefulExit.test.ts +11 -0
- package/agent/ui-tui/src/__tests__/memoryMonitor.test.ts +102 -0
- package/agent/ui-tui/src/__tests__/paths.test.ts +41 -1
- package/agent/ui-tui/src/__tests__/terminalModes.test.ts +22 -0
- package/agent/ui-tui/src/__tests__/text.test.ts +23 -0
- package/agent/ui-tui/src/__tests__/textInputFastEcho.test.ts +37 -0
- package/agent/ui-tui/src/__tests__/turnControllerNotice.test.ts +43 -0
- package/agent/ui-tui/src/__tests__/useInputHandlers.test.ts +38 -1
- package/agent/ui-tui/src/__tests__/virtualHeights.test.ts +8 -0
- package/agent/ui-tui/src/app/createGatewayEventHandler.ts +102 -7
- package/agent/ui-tui/src/app/interfaces.ts +64 -1
- package/agent/ui-tui/src/app/overlayStore.ts +18 -2
- package/agent/ui-tui/src/app/slash/commands/billing.ts +332 -0
- package/agent/ui-tui/src/app/slash/commands/core.ts +31 -2
- package/agent/ui-tui/src/app/slash/commands/credits.ts +57 -0
- package/agent/ui-tui/src/app/slash/commands/ops.ts +28 -0
- package/agent/ui-tui/src/app/slash/commands/session.ts +32 -4
- package/agent/ui-tui/src/app/slash/registry.ts +4 -0
- package/agent/ui-tui/src/app/turnController.ts +145 -2
- package/agent/ui-tui/src/app/uiStore.ts +2 -0
- package/agent/ui-tui/src/app/useInputHandlers.ts +42 -4
- package/agent/ui-tui/src/app/useMainApp.ts +54 -8
- package/agent/ui-tui/src/app/useSessionLifecycle.ts +40 -31
- package/agent/ui-tui/src/app/useSubmission.ts +23 -31
- package/agent/ui-tui/src/components/appChrome.tsx +112 -5
- package/agent/ui-tui/src/components/appLayout.tsx +9 -0
- package/agent/ui-tui/src/components/appOverlays.tsx +25 -1
- package/agent/ui-tui/src/components/billingOverlay.tsx +684 -0
- package/agent/ui-tui/src/components/branding.tsx +15 -3
- package/agent/ui-tui/src/components/messageLine.tsx +25 -3
- package/agent/ui-tui/src/components/pluginsHub.tsx +238 -0
- package/agent/ui-tui/src/components/prompts.tsx +31 -17
- package/agent/ui-tui/src/components/streamingAssistant.tsx +63 -55
- package/agent/ui-tui/src/components/textInput.tsx +16 -0
- package/agent/ui-tui/src/config/env.ts +12 -0
- package/agent/ui-tui/src/config/limits.ts +13 -0
- package/agent/ui-tui/src/domain/blockLayout.ts +146 -0
- package/agent/ui-tui/src/domain/paths.ts +24 -0
- package/agent/ui-tui/src/domain/slash.ts +40 -0
- package/agent/ui-tui/src/entry.tsx +35 -4
- package/agent/ui-tui/src/gatewayClient.ts +22 -10
- package/agent/ui-tui/src/gatewayTypes.ts +130 -1
- package/agent/ui-tui/src/lib/gracefulExit.ts +24 -4
- package/agent/ui-tui/src/lib/memory.test.ts +162 -0
- package/agent/ui-tui/src/lib/memory.ts +60 -1
- package/agent/ui-tui/src/lib/memoryMonitor.ts +79 -4
- package/agent/ui-tui/src/lib/osc52.ts +1 -1
- package/agent/ui-tui/src/lib/text.test.ts +32 -1
- package/agent/ui-tui/src/lib/text.ts +29 -2
- package/agent/ui-tui/src/lib/virtualHeights.ts +13 -0
- package/agent/ui-tui/src/types.ts +5 -0
- package/agent/ui-tui/tsconfig.build.json +0 -1
- package/agent/ui-tui/tsconfig.json +2 -1
- package/agent/utils.py +66 -2
- package/agent/uv.lock +300 -684
- package/agent/web/index.html +2 -2
- package/agent/web/package.json +11 -6
- package/agent/web/public/claw-bg.webp +0 -0
- package/agent/web/public/claw-logo.webp +0 -0
- package/agent/web/src/App.tsx +138 -48
- package/agent/web/src/components/AutomationBlueprints.tsx +225 -0
- package/agent/web/src/components/Backdrop.tsx +15 -0
- package/agent/web/src/components/ChatSessionList.tsx +260 -0
- package/agent/web/src/components/ChatSidebar.tsx +262 -78
- package/agent/web/src/components/ConfirmDialog.tsx +122 -0
- package/agent/web/src/components/ModelPickerDialog.tsx +111 -16
- package/agent/web/src/components/ModelReloadConfirm.tsx +40 -0
- package/agent/web/src/components/ProfileScopeBanner.tsx +30 -0
- package/agent/web/src/components/ProfileSwitcher.tsx +67 -0
- package/agent/web/src/components/ReasoningPicker.tsx +167 -0
- package/agent/web/src/components/SkillEditorDialog.tsx +215 -0
- package/agent/web/src/components/ThemeSwitcher.tsx +119 -4
- package/agent/web/src/components/ToolsetConfigDrawer.tsx +457 -0
- package/agent/web/src/contexts/PageHeaderProvider.tsx +7 -4
- package/agent/web/src/contexts/ProfileProvider.tsx +137 -0
- package/agent/web/src/contexts/SystemActions.tsx +6 -8
- package/agent/web/src/contexts/profile-context.ts +19 -0
- package/agent/web/src/contexts/useProfileScope.ts +6 -0
- package/agent/web/src/i18n/af.ts +5 -4
- package/agent/web/src/i18n/de.ts +5 -4
- package/agent/web/src/i18n/en.ts +58 -4
- package/agent/web/src/i18n/es.ts +5 -3
- package/agent/web/src/i18n/fr.ts +5 -3
- package/agent/web/src/i18n/ga.ts +5 -4
- package/agent/web/src/i18n/hu.ts +5 -4
- package/agent/web/src/i18n/it.ts +5 -4
- package/agent/web/src/i18n/ja.ts +5 -4
- package/agent/web/src/i18n/ko.ts +5 -4
- package/agent/web/src/i18n/pt.ts +5 -3
- package/agent/web/src/i18n/ru.ts +5 -4
- package/agent/web/src/i18n/tr.ts +5 -4
- package/agent/web/src/i18n/types.ts +59 -1
- package/agent/web/src/i18n/uk.ts +5 -3
- package/agent/web/src/i18n/zh-hant.ts +5 -4
- package/agent/web/src/i18n/zh.ts +5 -4
- package/agent/web/src/index.css +2 -2
- package/agent/web/src/lib/api.ts +819 -52
- package/agent/web/src/lib/dashboard-flags.ts +16 -7
- package/agent/web/src/lib/reasoning-effort.test.ts +48 -0
- package/agent/web/src/lib/reasoning-effort.ts +36 -0
- package/agent/web/src/lib/session-refresh.test.ts +21 -0
- package/agent/web/src/lib/session-refresh.ts +26 -0
- package/agent/web/src/pages/ChannelsPage.tsx +529 -68
- package/agent/web/src/pages/ChatPage.tsx +249 -56
- package/agent/web/src/pages/ConfigPage.tsx +11 -1
- package/agent/web/src/pages/CronPage.tsx +219 -31
- package/agent/web/src/pages/EnvPage.tsx +25 -6
- package/agent/web/src/pages/FilesPage.tsx +525 -0
- package/agent/web/src/pages/McpPage.tsx +80 -3
- package/agent/web/src/pages/ModelsPage.tsx +97 -12
- package/agent/web/src/pages/PluginsPage.tsx +1 -1
- package/agent/web/src/pages/ProfileBuilderPage.tsx +611 -0
- package/agent/web/src/pages/ProfilesPage.tsx +1038 -172
- package/agent/web/src/pages/SessionsPage.tsx +144 -13
- package/agent/web/src/pages/SkillsPage.tsx +851 -70
- package/agent/web/src/pages/SystemPage.tsx +340 -4
- package/agent/web/src/pages/WalletPage.tsx +401 -0
- package/agent/web/src/pages/WebhooksPage.tsx +145 -15
- package/agent/web/src/pages/X402Page.tsx +207 -0
- package/agent/web/src/plugins/registry.ts +28 -11
- package/agent/web/src/plugins/sdk.d.ts +160 -0
- package/agent/web/src/themes/context.tsx +112 -5
- package/agent/web/src/themes/fonts.ts +167 -0
- package/agent/web/src/themes/index.ts +7 -0
- package/agent/web/tsconfig.app.json +0 -1
- package/agent/web/vite.config.ts +1 -8
- package/agent/web/vitest.config.ts +16 -0
- package/package.json +1 -1
- package/agent/apps/desktop/package-lock.json +0 -18363
- package/agent/apps/desktop/src/app/chat/composer/skin-slash-popover.tsx +0 -56
- package/agent/apps/desktop/src/components/assistant-ui/thread-virtualizer.tsx +0 -382
- package/agent/apps/desktop/src/components/assistant-ui/todo-tool.tsx +0 -109
- package/agent/apps/desktop/src/components/chat/generated-image-context.tsx +0 -19
- package/agent/optional-skills/productivity/shop-app/SKILL.md +0 -340
- package/agent/skills/autonomous-ai-agents/kanban-codex-lane/SKILL.md +0 -277
- package/agent/skills/autonomous-ai-agents/kanban-codex-lane/templates/pmb-codex-lane-prompt.md +0 -57
- package/agent/skills/diagramming/DESCRIPTION.md +0 -3
- package/agent/skills/domain/DESCRIPTION.md +0 -24
- package/agent/skills/gifs/DESCRIPTION.md +0 -3
- package/agent/skills/inference-sh/DESCRIPTION.md +0 -19
- package/agent/skills/mcp/DESCRIPTION.md +0 -3
- package/agent/skills/media/spotify/SKILL.md +0 -135
- package/agent/skills/mlops/training/DESCRIPTION.md +0 -3
- package/agent/skills/mlops/vector-databases/DESCRIPTION.md +0 -3
- package/agent/skills/productivity/linear/SKILL.md +0 -380
- package/agent/skills/productivity/linear/scripts/linear_api.py +0 -445
- package/agent/skills/software-development/debugging-hermes-tui-commands/SKILL.md +0 -152
- package/agent/skills/software-development/writing-plans/SKILL.md +0 -297
- package/agent/ui-tui/package-lock.json +0 -7449
- package/agent/ui-tui/packages/hermes-ink/package-lock.json +0 -1289
- package/agent/web/package-lock.json +0 -8887
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/PORT_NOTES.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/SKILL.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/prompts/system.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/palettes/macaron.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/palettes/mono-ink.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/palettes/neon.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/palettes/warm.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/prompt-construction.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/style-presets.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/blueprint.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/chalkboard.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/editorial.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/elegant.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/fantasy-animation.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/flat-doodle.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/flat.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/ink-notes.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/intuition-machine.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/minimal.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/nature.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/notion.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/pixel-art.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/playful.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/retro.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/scientific.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/screen-print.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/sketch-notes.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/sketch.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/vector-illustration.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/vintage.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/warm.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles/watercolor.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/styles.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/usage.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-article-illustrator/references/workflow.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/PORT_NOTES.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/SKILL.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/analysis-framework.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/chalk.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/ink-brush.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/ligne-claire.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/manga.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/minimalist.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/art-styles/realistic.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/auto-selection.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/base-prompt.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/character-template.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/cinematic.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/dense.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/four-panel.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/mixed.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/splash.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/standard.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/layouts/webtoon.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/ohmsha-guide.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/partial-workflows.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/presets/concept-story.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/presets/four-panel.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/presets/ohmsha.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/presets/shoujo.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/presets/wuxia.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/storyboard-template.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/action.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/dramatic.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/energetic.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/neutral.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/romantic.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/vintage.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/tones/warm.md +0 -0
- /package/agent/{skills → optional-skills}/creative/baoyu-comic/references/workflow.md +0 -0
- /package/agent/{skills → optional-skills}/creative/creative-ideation/SKILL.md +0 -0
- /package/agent/{skills → optional-skills}/creative/creative-ideation/references/full-prompt-library.md +0 -0
- /package/agent/{skills → optional-skills}/creative/pixel-art/ATTRIBUTION.md +0 -0
- /package/agent/{skills → optional-skills}/creative/pixel-art/SKILL.md +0 -0
- /package/agent/{skills → optional-skills}/creative/pixel-art/references/palettes.md +0 -0
- /package/agent/{skills → optional-skills}/creative/pixel-art/scripts/__init__.py +0 -0
- /package/agent/{skills → optional-skills}/creative/pixel-art/scripts/palettes.py +0 -0
- /package/agent/{skills → optional-skills}/creative/pixel-art/scripts/pixel_art.py +0 -0
- /package/agent/{skills → optional-skills}/creative/pixel-art/scripts/pixel_art_video.py +0 -0
- /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/SKILL.md +0 -0
- /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/references/analysis-modules.md +0 -0
- /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/references/methods-guide.md +0 -0
- /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/templates/abliteration-config.yaml +0 -0
- /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/templates/analysis-study.yaml +0 -0
- /package/agent/{skills/mlops/inference → optional-skills/mlops}/obliteratus/templates/batch-abliteration.yaml +0 -0
- /package/agent/{skills → optional-skills}/mlops/research/DESCRIPTION.md +0 -0
- /package/agent/{skills → optional-skills}/mlops/research/dspy/SKILL.md +0 -0
- /package/agent/{skills → optional-skills}/mlops/research/dspy/references/examples.md +0 -0
- /package/agent/{skills → optional-skills}/mlops/research/dspy/references/modules.md +0 -0
- /package/agent/{skills → optional-skills}/mlops/research/dspy/references/optimizers.md +0 -0
- /package/agent/{skills/red-teaming → optional-skills/security}/godmode/references/jailbreak-templates.md +0 -0
- /package/agent/{skills/red-teaming → optional-skills/security}/godmode/references/refusal-detection.md +0 -0
- /package/agent/{skills/red-teaming → optional-skills/security}/godmode/scripts/godmode_race.py +0 -0
- /package/agent/{skills/red-teaming → optional-skills/security}/godmode/scripts/load_godmode.py +0 -0
- /package/agent/{skills/red-teaming → optional-skills/security}/godmode/scripts/parseltongue.py +0 -0
- /package/agent/{skills/red-teaming → optional-skills/security}/godmode/templates/prefill-subtle.json +0 -0
- /package/agent/{skills/red-teaming → optional-skills/security}/godmode/templates/prefill.json +0 -0
- /package/agent/{skills → optional-skills}/software-development/subagent-driven-development/references/context-budget-discipline.md +0 -0
- /package/agent/{skills → optional-skills}/software-development/subagent-driven-development/references/gates-taxonomy.md +0 -0
|
@@ -83,6 +83,7 @@ import sys
|
|
|
83
83
|
import threading
|
|
84
84
|
import logging
|
|
85
85
|
import time
|
|
86
|
+
from contextvars import ContextVar, Token
|
|
86
87
|
from dataclasses import dataclass, field
|
|
87
88
|
from pathlib import Path
|
|
88
89
|
from typing import Any, Iterable, Optional
|
|
@@ -120,6 +121,16 @@ DEFAULT_CLAIM_TTL_SECONDS = 15 * 60
|
|
|
120
121
|
# effect of normal API traffic.
|
|
121
122
|
DEFAULT_CLAIM_HEARTBEAT_MAX_STALE_SECONDS = 60 * 60
|
|
122
123
|
|
|
124
|
+
# Grace added to a claim when a reclaim is deferred because the previous
|
|
125
|
+
# host-local worker is still alive after a termination attempt. Releasing the
|
|
126
|
+
# claim in that state would spawn a duplicate alongside the surviving worker —
|
|
127
|
+
# the runaway seen when a cgroup memory.high throttle parks a worker in
|
|
128
|
+
# uninterruptible (D) state, where a pending SIGKILL cannot be delivered until
|
|
129
|
+
# the throttle lifts. Holding the claim a short grace and retrying next tick
|
|
130
|
+
# stops the duplication; once no duplicate is spawned the pressure eases, the
|
|
131
|
+
# signal lands, and the following tick reclaims cleanly.
|
|
132
|
+
RECLAIM_DEFER_GRACE_SECONDS = 120
|
|
133
|
+
|
|
123
134
|
|
|
124
135
|
def _resolve_claim_ttl_seconds(ttl_seconds: Optional[int] = None) -> int:
|
|
125
136
|
"""Return the effective claim TTL, honoring the kanban env override.
|
|
@@ -153,6 +164,17 @@ def _resolve_claim_ttl_seconds(ttl_seconds: Optional[int] = None) -> int:
|
|
|
153
164
|
DEFAULT_CRASH_GRACE_SECONDS = 30
|
|
154
165
|
|
|
155
166
|
|
|
167
|
+
# Sentinel exit code a kanban worker uses to signal "I bailed because the
|
|
168
|
+
# provider rate-limited / exhausted quota, not because the task failed."
|
|
169
|
+
# The dispatcher's reap classifier maps this to a ``rate_limited`` exit kind
|
|
170
|
+
# so ``detect_crashed_workers`` can release the task back to ``ready``
|
|
171
|
+
# WITHOUT counting a failure (the circuit breaker must never trip on a
|
|
172
|
+
# transient throttle). 75 == BSD ``EX_TEMPFAIL`` (sysexits.h) — the
|
|
173
|
+
# conventional "temporary failure, retry later" code, and well clear of the
|
|
174
|
+
# 0/1/2 codes the worker uses for success / generic failure / usage error.
|
|
175
|
+
KANBAN_RATE_LIMIT_EXIT_CODE = 75
|
|
176
|
+
|
|
177
|
+
|
|
156
178
|
def _resolve_crash_grace_seconds() -> int:
|
|
157
179
|
"""Return the crash-detection grace period in seconds.
|
|
158
180
|
|
|
@@ -172,6 +194,28 @@ def _resolve_crash_grace_seconds() -> int:
|
|
|
172
194
|
return DEFAULT_CRASH_GRACE_SECONDS
|
|
173
195
|
|
|
174
196
|
|
|
197
|
+
def _resolve_rate_limit_cooldown_seconds() -> int:
|
|
198
|
+
"""Return the rate-limit requeue cooldown in seconds.
|
|
199
|
+
|
|
200
|
+
Reads ``HERMES_KANBAN_RATE_LIMIT_COOLDOWN_SECONDS`` from the environment;
|
|
201
|
+
falls back to ``DEFAULT_RATE_LIMIT_COOLDOWN_SECONDS`` when absent, empty,
|
|
202
|
+
non-integer, or negative. A value of 0 disables the cooldown (re-spawn on
|
|
203
|
+
the next tick) — useful for tests that want to assert the task becomes
|
|
204
|
+
spawnable again immediately.
|
|
205
|
+
"""
|
|
206
|
+
raw = os.environ.get(
|
|
207
|
+
"HERMES_KANBAN_RATE_LIMIT_COOLDOWN_SECONDS", ""
|
|
208
|
+
).strip()
|
|
209
|
+
if raw:
|
|
210
|
+
try:
|
|
211
|
+
parsed = int(raw)
|
|
212
|
+
except ValueError:
|
|
213
|
+
parsed = -1
|
|
214
|
+
if parsed >= 0:
|
|
215
|
+
return parsed
|
|
216
|
+
return DEFAULT_RATE_LIMIT_COOLDOWN_SECONDS
|
|
217
|
+
|
|
218
|
+
|
|
175
219
|
# Worker-context caps so build_worker_context() stays bounded on
|
|
176
220
|
# pathological boards (retry-heavy tasks, comment storms, giant
|
|
177
221
|
# summaries). Values chosen to fit a typical 100k-char LLM prompt with
|
|
@@ -189,6 +233,20 @@ _CTX_MAX_COMMENT_BYTES = 2 * 1024 # 2 KB per comment
|
|
|
189
233
|
# ---------------------------------------------------------------------------
|
|
190
234
|
|
|
191
235
|
DEFAULT_BOARD = "default"
|
|
236
|
+
_CURRENT_BOARD_OVERRIDE: ContextVar[str | None] = ContextVar(
|
|
237
|
+
"hermes_kanban_current_board_override",
|
|
238
|
+
default=None,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
@contextlib.contextmanager
|
|
243
|
+
def scoped_current_board(slug: str):
|
|
244
|
+
"""Temporarily pin the active board for the current context only."""
|
|
245
|
+
token: Token[str | None] = _CURRENT_BOARD_OVERRIDE.set(slug)
|
|
246
|
+
try:
|
|
247
|
+
yield
|
|
248
|
+
finally:
|
|
249
|
+
_CURRENT_BOARD_OVERRIDE.reset(token)
|
|
192
250
|
|
|
193
251
|
# Slug validator: lowercase alphanumerics, digits, hyphens; 1–64 chars.
|
|
194
252
|
# Strict enough to stop traversal (`..`) and embedded path separators, loose
|
|
@@ -272,6 +330,15 @@ def get_current_board() -> str:
|
|
|
272
330
|
with a best-effort warning — the dispatcher must never crash because a
|
|
273
331
|
user hand-edited a file or removed a board directory.
|
|
274
332
|
"""
|
|
333
|
+
scoped = (_CURRENT_BOARD_OVERRIDE.get() or "").strip()
|
|
334
|
+
if scoped:
|
|
335
|
+
try:
|
|
336
|
+
normed = _normalize_board_slug(scoped)
|
|
337
|
+
if normed and board_exists(normed):
|
|
338
|
+
return normed
|
|
339
|
+
except ValueError:
|
|
340
|
+
pass
|
|
341
|
+
|
|
275
342
|
env = os.environ.get("HERMES_KANBAN_BOARD", "").strip()
|
|
276
343
|
if env:
|
|
277
344
|
try:
|
|
@@ -3229,6 +3296,14 @@ def release_stale_claims(
|
|
|
3229
3296
|
termination = _terminate_reclaimed_worker(
|
|
3230
3297
|
row["worker_pid"], row["claim_lock"], signal_fn=signal_fn,
|
|
3231
3298
|
)
|
|
3299
|
+
# Never release a claim while our own worker is still alive: that would
|
|
3300
|
+
# spawn a duplicate beside it. Hold the claim and retry next tick.
|
|
3301
|
+
if _worker_survived_termination(termination):
|
|
3302
|
+
_defer_reclaim_for_live_worker(
|
|
3303
|
+
conn, row["id"], row["claim_lock"], now, termination,
|
|
3304
|
+
reason="ttl_expired_worker_alive",
|
|
3305
|
+
)
|
|
3306
|
+
continue
|
|
3232
3307
|
with write_txn(conn):
|
|
3233
3308
|
cur = conn.execute(
|
|
3234
3309
|
"UPDATE tasks SET status = 'ready', claim_lock = NULL, "
|
|
@@ -3781,6 +3856,27 @@ def _cleanup_workspace(conn: sqlite3.Connection, task_id: str) -> None:
|
|
|
3781
3856
|
kind: Optional[str] = row["workspace_kind"]
|
|
3782
3857
|
path: Optional[str] = row["workspace_path"]
|
|
3783
3858
|
if kind != "scratch" or not path:
|
|
3859
|
+
# This task's own workspace isn't a removable scratch dir, but its
|
|
3860
|
+
# completion may still unblock a deferred parent scratch cleanup
|
|
3861
|
+
# (e.g. a 'dir' child whose scratch parent was waiting on it). #33774
|
|
3862
|
+
_try_cleanup_parent_workspaces(conn, task_id)
|
|
3863
|
+
return
|
|
3864
|
+
# Check if this task has children that still need the workspace.
|
|
3865
|
+
# If any child is not yet done/archived, defer cleanup so the
|
|
3866
|
+
# child can read handoff artifacts from the scratch dir (#33774).
|
|
3867
|
+
_active_children = conn.execute(
|
|
3868
|
+
"SELECT 1 FROM task_links l "
|
|
3869
|
+
"JOIN tasks t ON t.id = l.child_id "
|
|
3870
|
+
"WHERE l.parent_id = ? AND t.status NOT IN ('done', 'archived', 'failed', 'cancelled') "
|
|
3871
|
+
"LIMIT 1",
|
|
3872
|
+
(task_id,),
|
|
3873
|
+
).fetchone()
|
|
3874
|
+
if _active_children:
|
|
3875
|
+
_log.debug(
|
|
3876
|
+
"Deferring scratch workspace cleanup for task %s: "
|
|
3877
|
+
"active children still need workspace at %s",
|
|
3878
|
+
task_id, path,
|
|
3879
|
+
)
|
|
3784
3880
|
return
|
|
3785
3881
|
import shutil
|
|
3786
3882
|
wp = Path(path)
|
|
@@ -3803,10 +3899,54 @@ def _cleanup_workspace(conn: sqlite3.Connection, task_id: str) -> None:
|
|
|
3803
3899
|
# Also kill the tmux session for the worker that owned this task,
|
|
3804
3900
|
# if the tmux session is now dead (worker process exited).
|
|
3805
3901
|
_cleanup_worker_tmux(conn, task_id)
|
|
3902
|
+
# After cleaning up this task's workspace, check if any parent
|
|
3903
|
+
# tasks now have all children done — their deferred cleanup can
|
|
3904
|
+
# proceed (#33774).
|
|
3905
|
+
_try_cleanup_parent_workspaces(conn, task_id)
|
|
3806
3906
|
except Exception:
|
|
3807
3907
|
pass # best-effort — never block completion
|
|
3808
3908
|
|
|
3809
3909
|
|
|
3910
|
+
def _try_cleanup_parent_workspaces(conn: sqlite3.Connection, task_id: str) -> None:
|
|
3911
|
+
"""Clean up parent scratch workspaces now that *task_id* completed.
|
|
3912
|
+
|
|
3913
|
+
When a parent task's cleanup was deferred because it had active children,
|
|
3914
|
+
this function is called after each child completes. If all children of a
|
|
3915
|
+
parent are now done/archived/failed/cancelled, the parent's scratch
|
|
3916
|
+
workspace is removed (#33774).
|
|
3917
|
+
"""
|
|
3918
|
+
try:
|
|
3919
|
+
parents = conn.execute(
|
|
3920
|
+
"SELECT parent_id FROM task_links WHERE child_id = ?",
|
|
3921
|
+
(task_id,),
|
|
3922
|
+
).fetchall()
|
|
3923
|
+
for (parent_id,) in parents:
|
|
3924
|
+
row = conn.execute(
|
|
3925
|
+
"SELECT workspace_kind, workspace_path FROM tasks WHERE id = ?",
|
|
3926
|
+
(parent_id,),
|
|
3927
|
+
).fetchone()
|
|
3928
|
+
if not row or row["workspace_kind"] != "scratch" or not row["workspace_path"]:
|
|
3929
|
+
continue
|
|
3930
|
+
# Check if ALL children of this parent are terminal
|
|
3931
|
+
active = conn.execute(
|
|
3932
|
+
"SELECT 1 FROM task_links l "
|
|
3933
|
+
"JOIN tasks t ON t.id = l.child_id "
|
|
3934
|
+
"WHERE l.parent_id = ? AND t.status NOT IN ('done', 'archived', 'failed', 'cancelled') "
|
|
3935
|
+
"LIMIT 1",
|
|
3936
|
+
(parent_id,),
|
|
3937
|
+
).fetchone()
|
|
3938
|
+
if active:
|
|
3939
|
+
continue # still has active children
|
|
3940
|
+
# All children done — safe to clean up parent workspace
|
|
3941
|
+
import shutil
|
|
3942
|
+
wp = Path(row["workspace_path"])
|
|
3943
|
+
if wp.is_dir() and _is_managed_scratch_path(wp):
|
|
3944
|
+
shutil.rmtree(wp, ignore_errors=True)
|
|
3945
|
+
_log.debug("Deferred cleanup: removed parent %s scratch workspace: %s", parent_id, wp)
|
|
3946
|
+
except Exception:
|
|
3947
|
+
pass # best-effort
|
|
3948
|
+
|
|
3949
|
+
|
|
3810
3950
|
def _cleanup_worker_tmux(conn: sqlite3.Connection, task_id: str) -> None:
|
|
3811
3951
|
"""Kill the tmux session associated with a task's assignee, if dead."""
|
|
3812
3952
|
try:
|
|
@@ -4719,6 +4859,15 @@ _RESPAWN_BLOCKER_RE = re.compile(
|
|
|
4719
4859
|
# Within this window a completed run counts as "recent proof"; don't re-spawn.
|
|
4720
4860
|
_RESPAWN_GUARD_SUCCESS_WINDOW = 3600 # 1 hour
|
|
4721
4861
|
|
|
4862
|
+
# Cooldown after a rate-limited (quota-wall) requeue before the dispatcher
|
|
4863
|
+
# re-spawns the worker. Without this, a task released by the rate-limit path
|
|
4864
|
+
# would be re-spawned on the very next tick and immediately bounce off the
|
|
4865
|
+
# same quota wall, burning a worker slot every tick for hours. The cooldown
|
|
4866
|
+
# spaces retries out so the board keeps cheaply probing whether quota is back
|
|
4867
|
+
# without thrashing. Overridable via ``HERMES_KANBAN_RATE_LIMIT_COOLDOWN_SECONDS``
|
|
4868
|
+
# for operators who want a tighter/looser probe cadence.
|
|
4869
|
+
DEFAULT_RATE_LIMIT_COOLDOWN_SECONDS = 300 # 5 minutes
|
|
4870
|
+
|
|
4722
4871
|
# Within this window a GitHub PR URL in a comment blocks re-spawn.
|
|
4723
4872
|
_RESPAWN_GUARD_PR_WINDOW = 86400 # 24 hours
|
|
4724
4873
|
|
|
@@ -4776,6 +4925,11 @@ class DispatchResult:
|
|
|
4776
4925
|
Reasons: ``"blocker_auth"`` (quota/auth error — also auto-blocked),
|
|
4777
4926
|
``"recent_success"`` (completed run within guard window),
|
|
4778
4927
|
``"active_pr"`` (GitHub PR URL in a recent comment)."""
|
|
4928
|
+
rate_limited: list[str] = field(default_factory=list)
|
|
4929
|
+
"""Task ids whose workers bailed on a provider rate-limit / quota wall
|
|
4930
|
+
(EX_TEMPFAIL sentinel exit) and were released back to ``ready`` WITHOUT
|
|
4931
|
+
counting a failure. These never trip the circuit breaker — a long quota
|
|
4932
|
+
window just makes the task bounce cheaply until the window clears."""
|
|
4779
4933
|
|
|
4780
4934
|
|
|
4781
4935
|
# Bounded registry of recently-reaped worker child exits, populated by the
|
|
@@ -4823,14 +4977,20 @@ def _classify_worker_exit(pid: int) -> "tuple[str, Optional[int]]":
|
|
|
4823
4977
|
task is still ``running`` in the DB, this is a protocol violation
|
|
4824
4978
|
(worker exited without calling ``kanban_complete`` / ``kanban_block``)
|
|
4825
4979
|
and should be auto-blocked immediately — retrying will just loop.
|
|
4980
|
+
* ``"rate_limited"`` — ``WIFEXITED`` with status
|
|
4981
|
+
``KANBAN_RATE_LIMIT_EXIT_CODE``. The worker bailed because the
|
|
4982
|
+
provider rate-limited / exhausted quota, NOT because the task failed.
|
|
4983
|
+
``detect_crashed_workers`` releases the task back to ``ready`` without
|
|
4984
|
+
counting a failure, so a long quota window can't trip the breaker.
|
|
4826
4985
|
* ``"nonzero_exit"`` — ``WIFEXITED`` with non-zero status. Real error.
|
|
4827
4986
|
* ``"signaled"`` — ``WIFSIGNALED`` (OOM killer, SIGKILL, etc). Real crash.
|
|
4828
4987
|
* ``"unknown"`` — pid was not in the reap registry (either reaped by
|
|
4829
4988
|
something else, or died between reap tick and liveness check). Fall
|
|
4830
4989
|
back to existing crashed-counter behavior.
|
|
4831
4990
|
|
|
4832
|
-
``code`` is the exit status (for ``clean_exit`` / ``
|
|
4833
|
-
the signal number (for ``signaled``), or ``None``
|
|
4991
|
+
``code`` is the exit status (for ``clean_exit`` / ``rate_limited`` /
|
|
4992
|
+
``nonzero_exit``) or the signal number (for ``signaled``), or ``None``
|
|
4993
|
+
for ``unknown``.
|
|
4834
4994
|
"""
|
|
4835
4995
|
entry = _recent_worker_exits.get(int(pid))
|
|
4836
4996
|
if entry is None:
|
|
@@ -4841,6 +5001,8 @@ def _classify_worker_exit(pid: int) -> "tuple[str, Optional[int]]":
|
|
|
4841
5001
|
code = os.WEXITSTATUS(raw)
|
|
4842
5002
|
if code == 0:
|
|
4843
5003
|
return ("clean_exit", 0)
|
|
5004
|
+
if code == KANBAN_RATE_LIMIT_EXIT_CODE:
|
|
5005
|
+
return ("rate_limited", code)
|
|
4844
5006
|
return ("nonzero_exit", code)
|
|
4845
5007
|
if os.WIFSIGNALED(raw):
|
|
4846
5008
|
return ("signaled", os.WTERMSIG(raw))
|
|
@@ -4969,7 +5131,13 @@ def _terminate_reclaimed_worker(
|
|
|
4969
5131
|
info["termination_attempted"] = True
|
|
4970
5132
|
try:
|
|
4971
5133
|
kill(int(pid), signal.SIGTERM)
|
|
4972
|
-
except
|
|
5134
|
+
except ProcessLookupError:
|
|
5135
|
+
# Process is already gone — that's a successful termination, not a
|
|
5136
|
+
# survival. Leaving terminated=False here would make the reclaim guard
|
|
5137
|
+
# misread a dead worker as still-alive and defer forever.
|
|
5138
|
+
info["terminated"] = True
|
|
5139
|
+
return info
|
|
5140
|
+
except OSError:
|
|
4973
5141
|
return info
|
|
4974
5142
|
|
|
4975
5143
|
for _ in range(10):
|
|
@@ -4992,6 +5160,63 @@ def _terminate_reclaimed_worker(
|
|
|
4992
5160
|
return info
|
|
4993
5161
|
|
|
4994
5162
|
|
|
5163
|
+
def _worker_survived_termination(termination: dict) -> bool:
|
|
5164
|
+
"""True when we tried to kill our own host-local worker and it is still alive.
|
|
5165
|
+
|
|
5166
|
+
Reclaiming in this state would release the claim and let the dispatcher
|
|
5167
|
+
spawn a second worker while the first is still running — the duplication
|
|
5168
|
+
loop. Only host-local workers we actually signalled count: a non-local
|
|
5169
|
+
claim lock or a no-op attempt (no ``os.kill`` available) must fall through
|
|
5170
|
+
to the normal release path, since we cannot manage that worker anyway.
|
|
5171
|
+
"""
|
|
5172
|
+
return bool(
|
|
5173
|
+
termination.get("termination_attempted")
|
|
5174
|
+
and termination.get("host_local")
|
|
5175
|
+
and not termination.get("terminated")
|
|
5176
|
+
)
|
|
5177
|
+
|
|
5178
|
+
|
|
5179
|
+
def _defer_reclaim_for_live_worker(
|
|
5180
|
+
conn: sqlite3.Connection,
|
|
5181
|
+
task_id: str,
|
|
5182
|
+
claim_lock: Optional[str],
|
|
5183
|
+
now: int,
|
|
5184
|
+
termination: dict,
|
|
5185
|
+
*,
|
|
5186
|
+
reason: str,
|
|
5187
|
+
) -> None:
|
|
5188
|
+
"""Hold a claim whose worker survived termination instead of releasing it.
|
|
5189
|
+
|
|
5190
|
+
Extends ``claim_expires`` by ``RECLAIM_DEFER_GRACE_SECONDS`` so the task
|
|
5191
|
+
stays ``running`` (no duplicate spawn) and records a ``reclaim_deferred``
|
|
5192
|
+
event so the hold is visible in ``hermes kanban tail``. The next dispatch
|
|
5193
|
+
tick retries the kill; this is self-correcting because not spawning a
|
|
5194
|
+
duplicate is what lets the throttled worker finally die.
|
|
5195
|
+
"""
|
|
5196
|
+
grace = now + RECLAIM_DEFER_GRACE_SECONDS
|
|
5197
|
+
with write_txn(conn):
|
|
5198
|
+
cur = conn.execute(
|
|
5199
|
+
"UPDATE tasks SET claim_expires = ? "
|
|
5200
|
+
"WHERE id = ? AND status = 'running' AND claim_lock IS ?",
|
|
5201
|
+
(grace, task_id, claim_lock),
|
|
5202
|
+
)
|
|
5203
|
+
if cur.rowcount != 1:
|
|
5204
|
+
return
|
|
5205
|
+
run_id = _current_run_id(conn, task_id)
|
|
5206
|
+
if run_id is not None:
|
|
5207
|
+
conn.execute(
|
|
5208
|
+
"UPDATE task_runs SET claim_expires = ? WHERE id = ?",
|
|
5209
|
+
(grace, run_id),
|
|
5210
|
+
)
|
|
5211
|
+
payload = {
|
|
5212
|
+
"reason": reason,
|
|
5213
|
+
"claim_lock": claim_lock,
|
|
5214
|
+
"claim_expires_now": grace,
|
|
5215
|
+
}
|
|
5216
|
+
payload.update(termination)
|
|
5217
|
+
_append_event(conn, task_id, "reclaim_deferred", payload, run_id=run_id)
|
|
5218
|
+
|
|
5219
|
+
|
|
4995
5220
|
def heartbeat_worker(
|
|
4996
5221
|
conn: sqlite3.Connection,
|
|
4997
5222
|
task_id: str,
|
|
@@ -5230,6 +5455,15 @@ def detect_stale_running(
|
|
|
5230
5455
|
pid, lock, signal_fn=signal_fn,
|
|
5231
5456
|
)
|
|
5232
5457
|
|
|
5458
|
+
# Never release a claim while our own worker is still alive: that would
|
|
5459
|
+
# spawn a duplicate beside it. Hold the claim and retry next tick.
|
|
5460
|
+
if _worker_survived_termination(termination):
|
|
5461
|
+
_defer_reclaim_for_live_worker(
|
|
5462
|
+
conn, tid, lock, now, termination,
|
|
5463
|
+
reason="heartbeat_stale_worker_alive",
|
|
5464
|
+
)
|
|
5465
|
+
continue
|
|
5466
|
+
|
|
5233
5467
|
with write_txn(conn):
|
|
5234
5468
|
cur = conn.execute(
|
|
5235
5469
|
"UPDATE tasks SET status = 'ready', claim_lock = NULL, "
|
|
@@ -5311,8 +5545,18 @@ def detect_crashed_workers(conn: sqlite3.Connection) -> list[str]:
|
|
|
5311
5545
|
``kanban_complete`` / ``kanban_block``) and trip the circuit breaker
|
|
5312
5546
|
on the first occurrence — retrying a worker whose CLI keeps
|
|
5313
5547
|
returning 0 without a terminal transition just loops forever.
|
|
5548
|
+
|
|
5549
|
+
When the reap registry shows the worker exited with the rate-limit
|
|
5550
|
+
sentinel (``KANBAN_RATE_LIMIT_EXIT_CODE``), the worker bailed on a
|
|
5551
|
+
provider quota wall, NOT a task failure. Such tasks are released back
|
|
5552
|
+
to ``ready`` WITHOUT counting a failure (so a long quota window can't
|
|
5553
|
+
trip the breaker) and stamped with a quota-blocker error so
|
|
5554
|
+
``check_respawn_guard`` defers their respawn until the window clears.
|
|
5555
|
+
The ids are returned via the ``_last_rate_limited`` function attribute
|
|
5556
|
+
(the public return stays the crashed-only ``list[str]``).
|
|
5314
5557
|
"""
|
|
5315
5558
|
crashed: list[str] = []
|
|
5559
|
+
rate_limited: list[str] = []
|
|
5316
5560
|
# Per-crash details collected inside the main txn, used after it
|
|
5317
5561
|
# closes to run ``_record_task_failure`` (which needs its own
|
|
5318
5562
|
# write_txn so can't nest). ``protocol_violation`` flags the
|
|
@@ -5344,6 +5588,7 @@ def detect_crashed_workers(conn: sqlite3.Connection) -> list[str]:
|
|
|
5344
5588
|
|
|
5345
5589
|
pid = int(row["worker_pid"])
|
|
5346
5590
|
kind, code = _classify_worker_exit(pid)
|
|
5591
|
+
rate_limited_exit = False
|
|
5347
5592
|
if kind == "clean_exit":
|
|
5348
5593
|
# Worker subprocess returned 0 but its task is still
|
|
5349
5594
|
# ``running`` in the DB — it exited without calling
|
|
@@ -5360,6 +5605,26 @@ def detect_crashed_workers(conn: sqlite3.Connection) -> list[str]:
|
|
|
5360
5605
|
"claimer": row["claim_lock"],
|
|
5361
5606
|
"exit_code": code,
|
|
5362
5607
|
}
|
|
5608
|
+
elif kind == "rate_limited":
|
|
5609
|
+
# Worker bailed because the provider rate-limited / exhausted
|
|
5610
|
+
# quota (EX_TEMPFAIL sentinel). This is NOT a task failure —
|
|
5611
|
+
# the task is fine, the account just hit a wall. Release it
|
|
5612
|
+
# back to ``ready`` so the respawn guard defers it until the
|
|
5613
|
+
# quota window clears, and crucially do NOT count a failure
|
|
5614
|
+
# (skip ``_record_task_failure``) so a long quota window can't
|
|
5615
|
+
# trip the circuit breaker and permanently block the card.
|
|
5616
|
+
protocol_violation = False
|
|
5617
|
+
rate_limited_exit = True
|
|
5618
|
+
error_text = (
|
|
5619
|
+
f"pid {pid} exited rate-limited (quota wall) — "
|
|
5620
|
+
f"requeued without counting a failure"
|
|
5621
|
+
)
|
|
5622
|
+
event_kind = "rate_limited"
|
|
5623
|
+
event_payload = {
|
|
5624
|
+
"pid": pid,
|
|
5625
|
+
"claimer": row["claim_lock"],
|
|
5626
|
+
"exit_code": code,
|
|
5627
|
+
}
|
|
5363
5628
|
else:
|
|
5364
5629
|
protocol_violation = False
|
|
5365
5630
|
if kind == "nonzero_exit":
|
|
@@ -5381,9 +5646,13 @@ def detect_crashed_workers(conn: sqlite3.Connection) -> list[str]:
|
|
|
5381
5646
|
(row["id"],),
|
|
5382
5647
|
)
|
|
5383
5648
|
if cur.rowcount == 1:
|
|
5649
|
+
# Rate-limited requeues are a clean release, not a crash —
|
|
5650
|
+
# record the run outcome as ``rate_limited`` so the board
|
|
5651
|
+
# history doesn't show a phantom crash for a quota wall.
|
|
5652
|
+
_run_outcome = "rate_limited" if rate_limited_exit else "crashed"
|
|
5384
5653
|
run_id = _end_run(
|
|
5385
5654
|
conn, row["id"],
|
|
5386
|
-
outcome=
|
|
5655
|
+
outcome=_run_outcome, status=_run_outcome,
|
|
5387
5656
|
error=error_text,
|
|
5388
5657
|
metadata=dict(event_payload),
|
|
5389
5658
|
)
|
|
@@ -5392,11 +5661,23 @@ def detect_crashed_workers(conn: sqlite3.Connection) -> list[str]:
|
|
|
5392
5661
|
event_payload,
|
|
5393
5662
|
run_id=run_id,
|
|
5394
5663
|
)
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5664
|
+
if rate_limited_exit:
|
|
5665
|
+
# Stamp the failure-error column so ``check_respawn_guard``
|
|
5666
|
+
# recognizes this as a quota blocker and defers the
|
|
5667
|
+
# respawn until the window clears — WITHOUT touching
|
|
5668
|
+
# ``consecutive_failures`` (that's the whole point: no
|
|
5669
|
+
# breaker trip on a throttle).
|
|
5670
|
+
conn.execute(
|
|
5671
|
+
"UPDATE tasks SET last_failure_error = ? WHERE id = ?",
|
|
5672
|
+
(error_text[:500], row["id"]),
|
|
5673
|
+
)
|
|
5674
|
+
rate_limited.append(row["id"])
|
|
5675
|
+
else:
|
|
5676
|
+
crashed.append(row["id"])
|
|
5677
|
+
crash_details.append(
|
|
5678
|
+
(row["id"], pid, row["claim_lock"],
|
|
5679
|
+
protocol_violation, error_text)
|
|
5680
|
+
)
|
|
5400
5681
|
# Outside the main txn: increment the unified failure counter for
|
|
5401
5682
|
# each crashed task. If the breaker trips, the task transitions
|
|
5402
5683
|
# ready → blocked with a ``gave_up`` event on top of the ``crashed``
|
|
@@ -5436,6 +5717,9 @@ def detect_crashed_workers(conn: sqlite3.Connection) -> list[str]:
|
|
|
5436
5717
|
# and tests that destructure the result; ``dispatch_once`` reads this
|
|
5437
5718
|
# side-channel attribute to populate ``DispatchResult.auto_blocked``.
|
|
5438
5719
|
detect_crashed_workers._last_auto_blocked = auto_blocked # type: ignore[attr-defined]
|
|
5720
|
+
# Same side-channel for rate-limited requeues — these did NOT count a
|
|
5721
|
+
# failure and are NOT crashes, so they stay out of the ``crashed`` return.
|
|
5722
|
+
detect_crashed_workers._last_rate_limited = rate_limited # type: ignore[attr-defined]
|
|
5439
5723
|
return crashed
|
|
5440
5724
|
|
|
5441
5725
|
|
|
@@ -5663,6 +5947,18 @@ def check_respawn_guard(conn: sqlite3.Connection, task_id: str) -> Optional[str]
|
|
|
5663
5947
|
|
|
5664
5948
|
Checks in priority order:
|
|
5665
5949
|
|
|
5950
|
+
``"rate_limit_cooldown"``
|
|
5951
|
+
The task's most recent run ended with the ``rate_limited`` outcome
|
|
5952
|
+
(a worker bailed on a provider quota wall via the EX_TEMPFAIL
|
|
5953
|
+
sentinel) within ``_resolve_rate_limit_cooldown_seconds()``. The
|
|
5954
|
+
quota almost certainly hasn't reset yet, so defer the respawn until
|
|
5955
|
+
the cooldown elapses — then allow a cheap probe. This is checked
|
|
5956
|
+
BEFORE ``blocker_auth`` because the rate-limit requeue stamps a
|
|
5957
|
+
quota-flavored ``last_failure_error`` that would otherwise match the
|
|
5958
|
+
auth-blocker regex and park the task forever (the rate-limit path
|
|
5959
|
+
never increments ``consecutive_failures``, so the breaker can't free
|
|
5960
|
+
it). Once the cooldown elapses the task falls through and respawns.
|
|
5961
|
+
|
|
5666
5962
|
``"blocker_auth"``
|
|
5667
5963
|
The task's last failure error matches a quota / authentication
|
|
5668
5964
|
pattern. Retrying immediately is unlikely to help (rate limits
|
|
@@ -5695,14 +5991,50 @@ def check_respawn_guard(conn: sqlite3.Connection, task_id: str) -> Optional[str]
|
|
|
5695
5991
|
if row is None:
|
|
5696
5992
|
return None
|
|
5697
5993
|
|
|
5698
|
-
|
|
5994
|
+
now = int(time.time())
|
|
5995
|
+
|
|
5996
|
+
# 1. Rate-limit cooldown. The most recent run ended ``rate_limited``
|
|
5997
|
+
# (quota wall) — defer while inside the cooldown window, then allow a
|
|
5998
|
+
# cheap probe. Must run BEFORE the blocker_auth regex check, because a
|
|
5999
|
+
# rate-limit requeue stamps a quota-flavored last_failure_error that
|
|
6000
|
+
# the regex would otherwise match → defer forever (no failure counter
|
|
6001
|
+
# increment on this path means the breaker can never free it).
|
|
6002
|
+
#
|
|
6003
|
+
# We look at the LATEST run only (ORDER BY ended_at DESC LIMIT 1): if a
|
|
6004
|
+
# newer crash/completion superseded the rate-limit run, this guard
|
|
6005
|
+
# no longer applies and the normal paths take over.
|
|
6006
|
+
rl_cooldown = _resolve_rate_limit_cooldown_seconds()
|
|
6007
|
+
latest_run = conn.execute(
|
|
6008
|
+
"SELECT outcome, ended_at FROM task_runs "
|
|
6009
|
+
"WHERE task_id = ? AND ended_at IS NOT NULL "
|
|
6010
|
+
"ORDER BY ended_at DESC LIMIT 1",
|
|
6011
|
+
(task_id,),
|
|
6012
|
+
).fetchone()
|
|
6013
|
+
if (
|
|
6014
|
+
latest_run is not None
|
|
6015
|
+
and latest_run["outcome"] == "rate_limited"
|
|
6016
|
+
):
|
|
6017
|
+
if rl_cooldown <= 0:
|
|
6018
|
+
# Cooldown disabled — respawn immediately, and skip the
|
|
6019
|
+
# blocker_auth regex so the stamped rate-limit text doesn't
|
|
6020
|
+
# re-trap the task.
|
|
6021
|
+
return None
|
|
6022
|
+
ended_at = latest_run["ended_at"]
|
|
6023
|
+
if ended_at is not None and (now - int(ended_at)) < rl_cooldown:
|
|
6024
|
+
return "rate_limit_cooldown"
|
|
6025
|
+
# Cooldown elapsed — allow the respawn. Return early so the
|
|
6026
|
+
# blocker_auth check below doesn't catch the rate-limit text we
|
|
6027
|
+
# stamped on the task; this path intentionally retries forever
|
|
6028
|
+
# (cheaply, spaced by the cooldown) until quota returns or a real
|
|
6029
|
+
# crash/completion supersedes it.
|
|
6030
|
+
return None
|
|
6031
|
+
|
|
6032
|
+
# 2. Quota / auth blocker: retrying immediately will not help.
|
|
5699
6033
|
err = row["last_failure_error"]
|
|
5700
6034
|
if err and _RESPAWN_BLOCKER_RE.search(err):
|
|
5701
6035
|
return "blocker_auth"
|
|
5702
6036
|
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
# 2. Completed run within guard window — proof of recent success.
|
|
6037
|
+
# 3. Completed run within guard window — proof of recent success.
|
|
5706
6038
|
cutoff = now - _RESPAWN_GUARD_SUCCESS_WINDOW
|
|
5707
6039
|
if conn.execute(
|
|
5708
6040
|
"SELECT id FROM task_runs "
|
|
@@ -5711,7 +6043,7 @@ def check_respawn_guard(conn: sqlite3.Connection, task_id: str) -> Optional[str]
|
|
|
5711
6043
|
).fetchone():
|
|
5712
6044
|
return "recent_success"
|
|
5713
6045
|
|
|
5714
|
-
#
|
|
6046
|
+
# 4. GitHub PR URL in a recent comment — prior worker already opened a PR.
|
|
5715
6047
|
pr_cutoff = now - _RESPAWN_GUARD_PR_WINDOW
|
|
5716
6048
|
for c in conn.execute(
|
|
5717
6049
|
"SELECT body FROM task_comments WHERE task_id = ? AND created_at >= ?",
|
|
@@ -5840,6 +6172,14 @@ def dispatch_once(
|
|
|
5840
6172
|
)
|
|
5841
6173
|
if _crash_auto_blocked:
|
|
5842
6174
|
result.auto_blocked.extend(_crash_auto_blocked)
|
|
6175
|
+
# Rate-limited requeues (quota wall, no failure counted) — surface for
|
|
6176
|
+
# telemetry / tests. These tasks went back to ``ready`` and the respawn
|
|
6177
|
+
# guard will defer them until the quota window clears.
|
|
6178
|
+
_crash_rate_limited = getattr(
|
|
6179
|
+
detect_crashed_workers, "_last_rate_limited", []
|
|
6180
|
+
)
|
|
6181
|
+
if _crash_rate_limited:
|
|
6182
|
+
result.rate_limited.extend(_crash_rate_limited)
|
|
5843
6183
|
result.timed_out = enforce_max_runtime(conn)
|
|
5844
6184
|
result.promoted = recompute_ready(conn, failure_limit=failure_limit)
|
|
5845
6185
|
|
|
@@ -6425,6 +6765,40 @@ def _worker_terminal_timeout_env(
|
|
|
6425
6765
|
return str(desired)
|
|
6426
6766
|
|
|
6427
6767
|
|
|
6768
|
+
def _resolve_worker_cli_toolsets(hermes_home: Optional[str]) -> Optional[list[str]]:
|
|
6769
|
+
"""Return the assigned profile's effective CLI toolsets for a worker.
|
|
6770
|
+
|
|
6771
|
+
Dispatcher-spawned workers are launched from a long-lived gateway process,
|
|
6772
|
+
then the child re-enters the CLI with ``-p <assignee>``. Resolve the
|
|
6773
|
+
assignee profile's CLI tool surface at dispatch time and pass it as an
|
|
6774
|
+
explicit ``--toolsets`` pin so worker startup cannot fall back to a stale
|
|
6775
|
+
root/active-profile config or a profile whose top-level ``toolsets`` entry
|
|
6776
|
+
is only the kanban orchestrator surface. ``model_tools`` still appends the
|
|
6777
|
+
task-scoped kanban lifecycle tools when ``HERMES_KANBAN_TASK`` is set.
|
|
6778
|
+
"""
|
|
6779
|
+
if not hermes_home:
|
|
6780
|
+
return None
|
|
6781
|
+
try:
|
|
6782
|
+
from hermes_constants import reset_hermes_home_override, set_hermes_home_override
|
|
6783
|
+
from hermes_cli.config import load_config
|
|
6784
|
+
from hermes_cli.tools_config import _get_platform_tools
|
|
6785
|
+
|
|
6786
|
+
token = set_hermes_home_override(hermes_home)
|
|
6787
|
+
try:
|
|
6788
|
+
cfg = load_config()
|
|
6789
|
+
toolsets = sorted(_get_platform_tools(cfg, "cli"))
|
|
6790
|
+
finally:
|
|
6791
|
+
reset_hermes_home_override(token)
|
|
6792
|
+
return toolsets or None
|
|
6793
|
+
except Exception as exc:
|
|
6794
|
+
_log.debug(
|
|
6795
|
+
"kanban worker: could not resolve CLI toolsets for HERMES_HOME=%r (%s)",
|
|
6796
|
+
hermes_home,
|
|
6797
|
+
exc,
|
|
6798
|
+
)
|
|
6799
|
+
return None
|
|
6800
|
+
|
|
6801
|
+
|
|
6428
6802
|
def _default_spawn(
|
|
6429
6803
|
task: Task,
|
|
6430
6804
|
workspace: str,
|
|
@@ -6558,6 +6932,9 @@ def _default_spawn(
|
|
|
6558
6932
|
cmd.extend(["--skills", sk])
|
|
6559
6933
|
if task.model_override:
|
|
6560
6934
|
cmd.extend(["-m", task.model_override])
|
|
6935
|
+
worker_toolsets = _resolve_worker_cli_toolsets(env.get("HERMES_HOME"))
|
|
6936
|
+
if worker_toolsets:
|
|
6937
|
+
cmd.extend(["--toolsets", ",".join(worker_toolsets)])
|
|
6561
6938
|
cmd.extend([
|
|
6562
6939
|
"chat",
|
|
6563
6940
|
"-q", prompt,
|
|
@@ -20,7 +20,7 @@ Design notes
|
|
|
20
20
|
|
|
21
21
|
* The system prompt sees the *configured* profile roster — names plus
|
|
22
22
|
descriptions plus the default fallback. Profiles without a
|
|
23
|
-
description are still listed (with a note) so the
|
|
23
|
+
description are still listed (with a note) so the decomposer can
|
|
24
24
|
match on name as a fallback, but the user has an obvious incentive
|
|
25
25
|
to describe them.
|
|
26
26
|
|
|
@@ -178,7 +178,7 @@ def _load_config() -> dict:
|
|
|
178
178
|
|
|
179
179
|
|
|
180
180
|
def _resolve_orchestrator_profile(cfg: dict) -> str:
|
|
181
|
-
"""Resolve which profile owns
|
|
181
|
+
"""Resolve which profile owns the root/orchestration task after fan-out.
|
|
182
182
|
|
|
183
183
|
Falls back to the active default profile when ``kanban.orchestrator_profile``
|
|
184
184
|
is unset, so a task is never stranded for lack of an orchestrator.
|
|
@@ -40,9 +40,11 @@ from typing import Optional
|
|
|
40
40
|
|
|
41
41
|
from hermes_cli import kanban_db as kb
|
|
42
42
|
|
|
43
|
+
from utils import env_int
|
|
44
|
+
|
|
43
45
|
HERMES_KANBAN_SPECIFY_MAX_TOKENS = max(
|
|
44
46
|
1500,
|
|
45
|
-
|
|
47
|
+
env_int("HERMES_KANBAN_SPECIFY_MAX_TOKENS", 6000),
|
|
46
48
|
)
|
|
47
49
|
|
|
48
50
|
logger = logging.getLogger(__name__)
|
package/agent/hermes_cli/logs.py
CHANGED
|
@@ -11,6 +11,7 @@ Usage examples::
|
|
|
11
11
|
hermes logs errors # last 50 lines of errors.log
|
|
12
12
|
hermes logs gateway -n 100 # last 100 lines of gateway.log
|
|
13
13
|
hermes logs gui -f # follow gui.log (dashboard/pty/ws)
|
|
14
|
+
hermes logs desktop -f # follow desktop.log (Electron app boot/backend)
|
|
14
15
|
hermes logs --level WARNING # only WARNING+ lines
|
|
15
16
|
hermes logs --session abc123 # filter by session ID substring
|
|
16
17
|
hermes logs --component tools # only tool-related lines
|
|
@@ -33,6 +34,7 @@ LOG_FILES = {
|
|
|
33
34
|
"errors": "errors.log",
|
|
34
35
|
"gateway": "gateway.log",
|
|
35
36
|
"gui": "gui.log",
|
|
37
|
+
"desktop": "desktop.log",
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
# Log line timestamp regex — matches "2026-04-05 22:35:00,123" or
|