@clawpump/claw-agent 0.1.5 → 0.1.7
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 +6190 -973
- 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 +4 -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
|
@@ -7,12 +7,14 @@ import {
|
|
|
7
7
|
MessagePrimitive,
|
|
8
8
|
type ToolCallMessagePartProps,
|
|
9
9
|
useAui,
|
|
10
|
-
useAuiState
|
|
10
|
+
useAuiState,
|
|
11
|
+
useMessageRuntime
|
|
11
12
|
} from '@assistant-ui/react'
|
|
12
13
|
import { useStore } from '@nanostores/react'
|
|
13
14
|
import { IconPlayerStopFilled } from '@tabler/icons-react'
|
|
14
15
|
import {
|
|
15
16
|
type ClipboardEvent,
|
|
17
|
+
type ComponentProps,
|
|
16
18
|
type FC,
|
|
17
19
|
type FocusEvent,
|
|
18
20
|
type FormEvent,
|
|
@@ -36,7 +38,12 @@ import {
|
|
|
36
38
|
} from '@/app/chat/composer/focus'
|
|
37
39
|
import { useAtCompletions } from '@/app/chat/composer/hooks/use-at-completions'
|
|
38
40
|
import { useSlashCompletions } from '@/app/chat/composer/hooks/use-slash-completions'
|
|
39
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
dragHasAttachments,
|
|
43
|
+
droppedFileInlineRefs,
|
|
44
|
+
type InlineRefInput,
|
|
45
|
+
insertInlineRefsIntoEditor
|
|
46
|
+
} from '@/app/chat/composer/inline-refs'
|
|
40
47
|
import {
|
|
41
48
|
composerPlainText,
|
|
42
49
|
placeCaretEnd,
|
|
@@ -46,23 +53,28 @@ import {
|
|
|
46
53
|
} from '@/app/chat/composer/rich-editor'
|
|
47
54
|
import { detectTrigger, textBeforeCaret, type TriggerState } from '@/app/chat/composer/text-utils'
|
|
48
55
|
import { ComposerTriggerPopover } from '@/app/chat/composer/trigger-popover'
|
|
49
|
-
import {
|
|
56
|
+
import {
|
|
57
|
+
extractDroppedFiles,
|
|
58
|
+
HERMES_PATHS_MIME,
|
|
59
|
+
isImagePath,
|
|
60
|
+
partitionDroppedFiles
|
|
61
|
+
} from '@/app/chat/hooks/use-composer-actions'
|
|
62
|
+
import { uploadComposerAttachment } from '@/app/session/hooks/use-prompt-actions'
|
|
50
63
|
import { ClarifyTool } from '@/components/assistant-ui/clarify-tool'
|
|
51
|
-
import { DirectiveContent,
|
|
52
|
-
import {
|
|
53
|
-
import {
|
|
54
|
-
import { VirtualizedThread } from '@/components/assistant-ui/thread-virtualizer'
|
|
55
|
-
import { HoistedTodoPanel, todosFromMessageContent } from '@/components/assistant-ui/todo-tool'
|
|
64
|
+
import { DirectiveContent, hermesDirectiveFormatter } from '@/components/assistant-ui/directive-text'
|
|
65
|
+
import { MarkdownText, MarkdownTextContent } from '@/components/assistant-ui/markdown-text'
|
|
66
|
+
import { ThreadMessageList } from '@/components/assistant-ui/thread-list'
|
|
56
67
|
import { ToolFallback, ToolGroupSlot } from '@/components/assistant-ui/tool-fallback'
|
|
57
68
|
import { TooltipIconButton } from '@/components/assistant-ui/tooltip-icon-button'
|
|
69
|
+
import { UserMessageText } from '@/components/assistant-ui/user-message-text'
|
|
58
70
|
import { useElapsedSeconds } from '@/components/chat/activity-timer'
|
|
59
71
|
import { ActivityTimerText } from '@/components/chat/activity-timer-text'
|
|
60
72
|
import { DisclosureRow } from '@/components/chat/disclosure-row'
|
|
61
|
-
import {
|
|
62
|
-
import { ImageGenerationPlaceholder } from '@/components/chat/image-generation-placeholder'
|
|
73
|
+
import { GeneratedImage } from '@/components/chat/generated-image-result'
|
|
63
74
|
import { Intro, type IntroProps } from '@/components/chat/intro'
|
|
64
75
|
import { PreviewAttachment } from '@/components/chat/preview-attachment'
|
|
65
76
|
import { Codicon } from '@/components/ui/codicon'
|
|
77
|
+
import { ConfirmDialog } from '@/components/ui/confirm-dialog'
|
|
66
78
|
import { CopyButton } from '@/components/ui/copy-button'
|
|
67
79
|
import {
|
|
68
80
|
DropdownMenu,
|
|
@@ -73,21 +85,33 @@ import {
|
|
|
73
85
|
} from '@/components/ui/dropdown-menu'
|
|
74
86
|
import { Loader } from '@/components/ui/loader'
|
|
75
87
|
import type { HermesGateway } from '@/hermes'
|
|
88
|
+
import { useResizeObserver } from '@/hooks/use-resize-observer'
|
|
89
|
+
import { useI18n } from '@/i18n'
|
|
90
|
+
import { attachmentDisplayText, attachmentId, pathLabel } from '@/lib/chat-runtime'
|
|
76
91
|
import { DATA_IMAGE_URL_RE } from '@/lib/embedded-images'
|
|
92
|
+
import { LinkifiedText } from '@/lib/external-link'
|
|
77
93
|
import { triggerHaptic } from '@/lib/haptics'
|
|
78
|
-
import { GitBranchIcon, Loader2Icon, Volume2Icon, VolumeXIcon } from '@/lib/icons'
|
|
94
|
+
import { GitBranchIcon, Loader2Icon, Volume2Icon, VolumeXIcon, XIcon } from '@/lib/icons'
|
|
79
95
|
import { extractPreviewTargets } from '@/lib/preview-targets'
|
|
80
96
|
import { useEnterAnimation } from '@/lib/use-enter-animation'
|
|
81
97
|
import { cn } from '@/lib/utils'
|
|
82
98
|
import { playSpeechText, stopVoicePlayback } from '@/lib/voice-playback'
|
|
99
|
+
import { $compactionActive } from '@/store/compaction'
|
|
100
|
+
import type { ComposerAttachment } from '@/store/composer'
|
|
83
101
|
import { notifyError } from '@/store/notifications'
|
|
102
|
+
import { $connection } from '@/store/session'
|
|
103
|
+
import { notifyThreadEditClose, notifyThreadEditOpen } from '@/store/thread-scroll'
|
|
84
104
|
import { $voicePlayback } from '@/store/voice-playback'
|
|
85
105
|
|
|
86
106
|
type ThreadLoadingState = 'response' | 'session'
|
|
87
107
|
|
|
88
108
|
interface MessageActionProps {
|
|
89
109
|
messageId: string
|
|
90
|
-
|
|
110
|
+
/** Lazy accessor — reads the live message text at action time. Passing the
|
|
111
|
+
* text itself as a prop forces the whole footer to re-render on every
|
|
112
|
+
* streaming delta flush (the text changes ~30×/s), which profiling showed
|
|
113
|
+
* was a large slice of per-token script time on long transcripts. */
|
|
114
|
+
getMessageText: () => string
|
|
91
115
|
onBranchInNewChat?: (messageId: string) => void
|
|
92
116
|
}
|
|
93
117
|
|
|
@@ -115,9 +139,27 @@ function messageContentText(content: unknown): string {
|
|
|
115
139
|
return Array.isArray(content) ? content.map(partText).join('').trim() : ''
|
|
116
140
|
}
|
|
117
141
|
|
|
118
|
-
|
|
142
|
+
// Cheap streaming-stable "does this message have visible text" check: returns
|
|
143
|
+
// on the first non-whitespace text part without concatenating the whole
|
|
144
|
+
// message. Used as a useAuiState selector so its boolean output stays stable
|
|
145
|
+
// across token flushes (flips false→true once per turn).
|
|
146
|
+
function contentHasVisibleText(content: unknown): boolean {
|
|
147
|
+
if (typeof content === 'string') {
|
|
148
|
+
return content.trim().length > 0
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (!Array.isArray(content)) {
|
|
152
|
+
return false
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
for (const part of content) {
|
|
156
|
+
if (partText(part).trim().length > 0) {
|
|
157
|
+
return true
|
|
158
|
+
}
|
|
159
|
+
}
|
|
119
160
|
|
|
120
|
-
|
|
161
|
+
return false
|
|
162
|
+
}
|
|
121
163
|
|
|
122
164
|
export const Thread: FC<{
|
|
123
165
|
clampToComposer?: boolean
|
|
@@ -127,6 +169,8 @@ export const Thread: FC<{
|
|
|
127
169
|
loading?: ThreadLoadingState
|
|
128
170
|
onBranchInNewChat?: (messageId: string) => void
|
|
129
171
|
onCancel?: () => Promise<void> | void
|
|
172
|
+
onDismissError?: (messageId: string) => void
|
|
173
|
+
onRestoreToMessage?: (messageId: string) => Promise<void> | void
|
|
130
174
|
sessionId?: string | null
|
|
131
175
|
sessionKey?: string | null
|
|
132
176
|
}> = ({
|
|
@@ -137,41 +181,38 @@ export const Thread: FC<{
|
|
|
137
181
|
loading,
|
|
138
182
|
onBranchInNewChat,
|
|
139
183
|
onCancel,
|
|
184
|
+
onDismissError,
|
|
185
|
+
onRestoreToMessage,
|
|
140
186
|
sessionId = null,
|
|
141
187
|
sessionKey
|
|
142
188
|
}) => {
|
|
143
189
|
const messageComponents = useMemo(
|
|
144
190
|
() => ({
|
|
145
|
-
AssistantMessage: () => <AssistantMessage onBranchInNewChat={onBranchInNewChat} />,
|
|
191
|
+
AssistantMessage: () => <AssistantMessage onBranchInNewChat={onBranchInNewChat} onDismissError={onDismissError} />,
|
|
146
192
|
SystemMessage,
|
|
147
193
|
UserEditComposer: () => <UserEditComposer cwd={cwd} gateway={gateway} sessionId={sessionId} />,
|
|
148
|
-
UserMessage: () => <UserMessage onCancel={onCancel} />
|
|
194
|
+
UserMessage: () => <UserMessage onCancel={onCancel} onRestoreToMessage={onRestoreToMessage} />
|
|
149
195
|
}),
|
|
150
|
-
[cwd, gateway, onBranchInNewChat, onCancel, sessionId]
|
|
196
|
+
[cwd, gateway, onBranchInNewChat, onCancel, onDismissError, onRestoreToMessage, sessionId]
|
|
151
197
|
)
|
|
152
198
|
|
|
153
199
|
const emptyPlaceholder = intro ? (
|
|
154
|
-
<div
|
|
155
|
-
className="flex min-h-0 w-full flex-col items-center justify-center"
|
|
156
|
-
style={{ paddingBottom: 'var(--composer-measured-height)' }}
|
|
157
|
-
>
|
|
200
|
+
<div className="flex min-h-0 w-full flex-col items-center justify-center pt-[var(--composer-measured-height)]">
|
|
158
201
|
<Intro {...intro} />
|
|
159
202
|
</div>
|
|
160
203
|
) : undefined
|
|
161
204
|
|
|
162
205
|
return (
|
|
163
|
-
<
|
|
164
|
-
<
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
</div>
|
|
174
|
-
</GeneratedImageProvider>
|
|
206
|
+
<div className="relative grid h-full min-h-0 max-w-full grid-rows-[minmax(0,1fr)] overflow-hidden bg-transparent contain-[layout_paint]">
|
|
207
|
+
<ThreadMessageList
|
|
208
|
+
clampToComposer={clampToComposer}
|
|
209
|
+
components={messageComponents}
|
|
210
|
+
emptyPlaceholder={emptyPlaceholder}
|
|
211
|
+
loadingIndicator={loading === 'response' ? <ResponseLoadingIndicator /> : null}
|
|
212
|
+
sessionKey={sessionKey}
|
|
213
|
+
/>
|
|
214
|
+
{loading === 'session' && <CenteredThreadSpinner />}
|
|
215
|
+
</div>
|
|
175
216
|
)
|
|
176
217
|
}
|
|
177
218
|
|
|
@@ -185,40 +226,63 @@ function pickPrimaryPreviewTarget(targets: string[]): string[] {
|
|
|
185
226
|
return [localUrl || targets[targets.length - 1]]
|
|
186
227
|
}
|
|
187
228
|
|
|
188
|
-
const CenteredThreadSpinner: FC = () =>
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
229
|
+
const CenteredThreadSpinner: FC = () => {
|
|
230
|
+
const { t } = useI18n()
|
|
231
|
+
|
|
232
|
+
return (
|
|
233
|
+
<div
|
|
234
|
+
aria-label={t.assistant.thread.loadingSession}
|
|
235
|
+
className="pointer-events-none absolute inset-0 z-1 grid place-items-center"
|
|
236
|
+
role="status"
|
|
237
|
+
>
|
|
238
|
+
<Loader
|
|
239
|
+
aria-hidden="true"
|
|
240
|
+
className="size-12 text-midground/70"
|
|
241
|
+
pathSteps={220}
|
|
242
|
+
role="presentation"
|
|
243
|
+
strokeScale={0.72}
|
|
244
|
+
type="rose-curve"
|
|
245
|
+
/>
|
|
246
|
+
</div>
|
|
247
|
+
)
|
|
248
|
+
}
|
|
204
249
|
|
|
205
|
-
const AssistantMessage: FC<{
|
|
250
|
+
const AssistantMessage: FC<{
|
|
251
|
+
onBranchInNewChat?: (messageId: string) => void
|
|
252
|
+
onDismissError?: (messageId: string) => void
|
|
253
|
+
}> = ({ onBranchInNewChat, onDismissError }) => {
|
|
206
254
|
const messageId = useAuiState(s => s.message.id)
|
|
207
|
-
const
|
|
208
|
-
const
|
|
209
|
-
|
|
255
|
+
const messageRuntime = useMessageRuntime()
|
|
256
|
+
const { t } = useI18n()
|
|
257
|
+
|
|
258
|
+
// PERF: this component must NOT subscribe to the streaming text. Every
|
|
259
|
+
// selector here returns a value that stays referentially stable across
|
|
260
|
+
// token flushes (booleans, status strings, '' while running), so the
|
|
261
|
+
// 30 Hz delta stream only re-renders the markdown part and the tiny
|
|
262
|
+
// StreamStallIndicator leaf — not the footer/preview/root subtree.
|
|
263
|
+
const messageStatus = useAuiState(s => s.message.status?.type)
|
|
264
|
+
const isRunning = messageStatus === 'running'
|
|
265
|
+
const isPlaceholder = useAuiState(s => s.message.status?.type === 'running' && s.message.content.length === 0)
|
|
266
|
+
const hasVisibleText = useAuiState(s => contentHasVisibleText(s.message.content))
|
|
267
|
+
|
|
268
|
+
// Preview targets only materialize once the turn completes — while running
|
|
269
|
+
// the selector returns '' (stable), so per-token flushes skip the regex
|
|
270
|
+
// scan and the re-render it would cause.
|
|
271
|
+
const completedText = useAuiState(s =>
|
|
272
|
+
s.message.status?.type === 'running' ? '' : messageContentText(s.message.content)
|
|
273
|
+
)
|
|
210
274
|
|
|
211
275
|
const previewTargets = useMemo(() => {
|
|
212
|
-
if (!
|
|
276
|
+
if (!completedText || !/(https?:\/\/|file:\/\/)/i.test(completedText)) {
|
|
213
277
|
return []
|
|
214
278
|
}
|
|
215
279
|
|
|
216
|
-
return pickPrimaryPreviewTarget(extractPreviewTargets(
|
|
217
|
-
}, [
|
|
280
|
+
return pickPrimaryPreviewTarget(extractPreviewTargets(completedText))
|
|
281
|
+
}, [completedText])
|
|
218
282
|
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
const
|
|
283
|
+
const getMessageText = useCallback(() => messageContentText(messageRuntime.getState().content), [messageRuntime])
|
|
284
|
+
|
|
285
|
+
const enterRef = useEnterAnimation(isRunning, `assistant-message:${messageId}`)
|
|
222
286
|
|
|
223
287
|
if (isPlaceholder) {
|
|
224
288
|
return null
|
|
@@ -229,16 +293,16 @@ const AssistantMessage: FC<{ onBranchInNewChat?: (messageId: string) => void }>
|
|
|
229
293
|
className="group flex w-full min-w-0 max-w-full flex-col gap-0 self-start overflow-hidden"
|
|
230
294
|
data-role="assistant"
|
|
231
295
|
data-slot="aui_assistant-message-root"
|
|
296
|
+
data-streaming={isRunning ? 'true' : undefined}
|
|
297
|
+
ref={enterRef}
|
|
232
298
|
>
|
|
233
299
|
<div
|
|
234
|
-
className=
|
|
235
|
-
'wrap-anywhere min-w-0 max-w-full overflow-hidden text-pretty text-[length:var(--conversation-text-font-size)] leading-(--dt-line-height) text-foreground',
|
|
236
|
-
interruptedOnly && 'text-[0.8rem] leading-5 text-muted-foreground/82'
|
|
237
|
-
)}
|
|
300
|
+
className="wrap-anywhere min-w-0 max-w-full overflow-hidden text-pretty text-[length:var(--conversation-text-font-size)] leading-(--dt-line-height) text-foreground"
|
|
238
301
|
data-slot="aui_assistant-message-content"
|
|
239
302
|
>
|
|
240
|
-
{
|
|
303
|
+
{/* Todos render in the composer status stack now, not inline. */}
|
|
241
304
|
<MessagePrimitive.Parts components={MESSAGE_PARTS_COMPONENTS} />
|
|
305
|
+
{isRunning && <StreamStallIndicator />}
|
|
242
306
|
{previewTargets.length > 0 && (
|
|
243
307
|
<div className="mt-3 flex flex-wrap gap-2">
|
|
244
308
|
{previewTargets.map(target => (
|
|
@@ -248,15 +312,25 @@ const AssistantMessage: FC<{ onBranchInNewChat?: (messageId: string) => void }>
|
|
|
248
312
|
)}
|
|
249
313
|
<MessagePrimitive.Error>
|
|
250
314
|
<ErrorPrimitive.Root
|
|
251
|
-
className="mt-1.5 text-[0.78rem] leading-5 text-[color-mix(in_srgb,var(--dt-destructive)_78%,var(--ui-text-secondary))]"
|
|
315
|
+
className="mt-1.5 flex items-start gap-1.5 text-[0.78rem] leading-5 text-[color-mix(in_srgb,var(--dt-destructive)_78%,var(--ui-text-secondary))]"
|
|
252
316
|
role="alert"
|
|
253
317
|
>
|
|
254
|
-
<ErrorPrimitive.Message />
|
|
318
|
+
<ErrorPrimitive.Message className="min-w-0 flex-1" />
|
|
319
|
+
{onDismissError && (
|
|
320
|
+
<TooltipIconButton
|
|
321
|
+
className="-my-0.5 shrink-0 text-current opacity-70 hover:opacity-100"
|
|
322
|
+
onClick={() => onDismissError(messageId)}
|
|
323
|
+
side="top"
|
|
324
|
+
tooltip={t.assistant.thread.dismissError}
|
|
325
|
+
>
|
|
326
|
+
<XIcon className="size-3.5" />
|
|
327
|
+
</TooltipIconButton>
|
|
328
|
+
)}
|
|
255
329
|
</ErrorPrimitive.Root>
|
|
256
330
|
</MessagePrimitive.Error>
|
|
257
331
|
</div>
|
|
258
|
-
{
|
|
259
|
-
<AssistantFooter
|
|
332
|
+
{hasVisibleText && (
|
|
333
|
+
<AssistantFooter getMessageText={getMessageText} messageId={messageId} onBranchInNewChat={onBranchInNewChat} />
|
|
260
334
|
)}
|
|
261
335
|
</MessagePrimitive.Root>
|
|
262
336
|
)
|
|
@@ -279,32 +353,94 @@ const StatusRow: FC<{ children: ReactNode; label: string } & React.ComponentProp
|
|
|
279
353
|
</div>
|
|
280
354
|
)
|
|
281
355
|
|
|
356
|
+
// Fixed label while auto-compaction runs — decoupled from backend status text.
|
|
357
|
+
const COMPACTION_LABEL = 'Summarizing thread'
|
|
358
|
+
|
|
359
|
+
const CompactionHint: FC = () => (
|
|
360
|
+
<span className="shimmer min-w-0 truncate text-muted-foreground/55">{COMPACTION_LABEL}</span>
|
|
361
|
+
)
|
|
362
|
+
|
|
282
363
|
const ResponseLoadingIndicator: FC = () => {
|
|
364
|
+
const { t } = useI18n()
|
|
283
365
|
const elapsed = useElapsedSeconds()
|
|
366
|
+
const compacting = useStore($compactionActive)
|
|
284
367
|
|
|
285
368
|
return (
|
|
286
|
-
<StatusRow
|
|
369
|
+
<StatusRow
|
|
370
|
+
data-slot="aui_response-loading"
|
|
371
|
+
label={compacting ? COMPACTION_LABEL : t.assistant.thread.loadingResponse}
|
|
372
|
+
>
|
|
287
373
|
<span aria-hidden="true" className="dither inline-block size-3 rounded-[2px] text-midground/80 animate-pulse" />
|
|
374
|
+
{compacting && <CompactionHint />}
|
|
288
375
|
<ActivityTimerText seconds={elapsed} />
|
|
289
376
|
</StatusRow>
|
|
290
377
|
)
|
|
291
378
|
}
|
|
292
379
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
380
|
+
// Seconds of no visible output (text or part count) before a still-running turn
|
|
381
|
+
// is treated as stalled and the thinking indicator returns at the tail.
|
|
382
|
+
const STREAM_STALL_S = 2
|
|
383
|
+
|
|
384
|
+
// Tail "still thinking" indicator: the pre-first-token spinner goes away once
|
|
385
|
+
// text flows, but if the stream then goes quiet mid-turn (tool think-time,
|
|
386
|
+
// provider stall) nothing signals that work continues. Watch a per-flush
|
|
387
|
+
// activity signal; when it hasn't changed for STREAM_STALL_S, re-show the
|
|
388
|
+
// dither + a timer counting from the last activity.
|
|
389
|
+
//
|
|
390
|
+
// Subscribes to the activity signal ITSELF (rather than taking it as a prop)
|
|
391
|
+
// so that per-token updates re-render only this leaf, not the whole
|
|
392
|
+
// AssistantMessage subtree.
|
|
393
|
+
const StreamStallIndicator: FC = () => {
|
|
394
|
+
const activity = useAuiState(s => {
|
|
395
|
+
let textLength = 0
|
|
396
|
+
|
|
397
|
+
for (const part of s.message.content) {
|
|
398
|
+
const text = (part as { text?: unknown }).text
|
|
399
|
+
|
|
400
|
+
if (typeof text === 'string') {
|
|
401
|
+
textLength += text.length
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return `${s.message.content.length}:${textLength}`
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
const [stalled, setStalled] = useState(false)
|
|
409
|
+
const compacting = useStore($compactionActive)
|
|
296
410
|
|
|
297
411
|
useEffect(() => {
|
|
298
|
-
|
|
299
|
-
|
|
412
|
+
setStalled(false)
|
|
413
|
+
const id = window.setTimeout(() => setStalled(true), STREAM_STALL_S * 1000)
|
|
414
|
+
|
|
415
|
+
return () => window.clearTimeout(id)
|
|
416
|
+
}, [activity])
|
|
417
|
+
|
|
418
|
+
const active = stalled || compacting
|
|
419
|
+
const elapsed = useElapsedSeconds(active)
|
|
300
420
|
|
|
301
|
-
if (!
|
|
421
|
+
if (!active) {
|
|
302
422
|
return null
|
|
303
423
|
}
|
|
304
424
|
|
|
425
|
+
return (
|
|
426
|
+
<StatusRow
|
|
427
|
+
className="mt-1.5"
|
|
428
|
+
data-slot="aui_stream-stall"
|
|
429
|
+
label={compacting ? COMPACTION_LABEL : 'Hermes is thinking'}
|
|
430
|
+
>
|
|
431
|
+
<span aria-hidden="true" className="dither inline-block size-3 rounded-[2px] text-midground/80 animate-pulse" />
|
|
432
|
+
{compacting && <CompactionHint />}
|
|
433
|
+
<ActivityTimerText seconds={elapsed} />
|
|
434
|
+
</StatusRow>
|
|
435
|
+
)
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const ImageGenerateTool: FC<ToolCallMessagePartProps> = ({ args, result }) => {
|
|
439
|
+
const aspectRatio = typeof args?.aspect_ratio === 'string' ? args.aspect_ratio : undefined
|
|
440
|
+
|
|
305
441
|
return (
|
|
306
442
|
<div className="mt-1.5">
|
|
307
|
-
<
|
|
443
|
+
<GeneratedImage aspectRatio={aspectRatio} result={result} />
|
|
308
444
|
</div>
|
|
309
445
|
)
|
|
310
446
|
}
|
|
@@ -332,6 +468,7 @@ const ThinkingDisclosure: FC<{
|
|
|
332
468
|
pending?: boolean
|
|
333
469
|
timerKey?: string
|
|
334
470
|
}> = ({ children, messageRunning = false, pending = false, timerKey }) => {
|
|
471
|
+
const { t } = useI18n()
|
|
335
472
|
// `null` = no explicit user toggle yet, defer to the streaming default.
|
|
336
473
|
// The default is "auto-open while streaming, auto-collapse when done" so
|
|
337
474
|
// reasoning surfaces a live preview without manual interaction. The first
|
|
@@ -370,7 +507,9 @@ const ThinkingDisclosure: FC<{
|
|
|
370
507
|
observer.observe(content)
|
|
371
508
|
|
|
372
509
|
return () => observer.disconnect()
|
|
373
|
-
|
|
510
|
+
// Re-run when the disclosure toggles so the observer attaches to the new
|
|
511
|
+
// DOM after expand/collapse (refs are conditionally rendered on `open`).
|
|
512
|
+
}, [isPreview, open])
|
|
374
513
|
|
|
375
514
|
return (
|
|
376
515
|
<div
|
|
@@ -386,7 +525,7 @@ const ThinkingDisclosure: FC<{
|
|
|
386
525
|
pending && 'shimmer text-foreground/55'
|
|
387
526
|
)}
|
|
388
527
|
>
|
|
389
|
-
|
|
528
|
+
{t.assistant.thread.thinking}
|
|
390
529
|
</span>
|
|
391
530
|
{pending && (
|
|
392
531
|
<ActivityTimerText
|
|
@@ -431,10 +570,26 @@ const ReasoningAccordionGroup: FC<{ children?: ReactNode; endIndex: number; star
|
|
|
431
570
|
s.thread.isRunning &&
|
|
432
571
|
s.message.status?.type === 'running' &&
|
|
433
572
|
s.message.parts
|
|
434
|
-
.slice(Math.max(0, startIndex),
|
|
573
|
+
.slice(Math.max(0, startIndex), endIndex + 1)
|
|
435
574
|
.some(p => p?.type === 'reasoning' && p.status?.type !== 'complete')
|
|
436
575
|
)
|
|
437
576
|
|
|
577
|
+
// A reasoning group with no actual text is pure noise — drop the whole
|
|
578
|
+
// "Thinking" disclosure rather than leave an empty header eating a row. This
|
|
579
|
+
// applies live too: encrypted/spinner-coerced reasoning (Opus reasoning max)
|
|
580
|
+
// never carries visible text, and the bottom-of-thread loader already signals
|
|
581
|
+
// "thinking", so an empty header is never wanted. Real reasoning surfaces the
|
|
582
|
+
// instant its first token lands.
|
|
583
|
+
const hasContent = useAuiState(s =>
|
|
584
|
+
s.message.parts
|
|
585
|
+
.slice(Math.max(0, startIndex), endIndex + 1)
|
|
586
|
+
.some(p => p?.type === 'reasoning' && typeof p.text === 'string' && p.text.trim().length > 0)
|
|
587
|
+
)
|
|
588
|
+
|
|
589
|
+
if (!hasContent) {
|
|
590
|
+
return null
|
|
591
|
+
}
|
|
592
|
+
|
|
438
593
|
return (
|
|
439
594
|
<ThinkingDisclosure messageRunning={messageRunning} pending={pending} timerKey={`reasoning:${messageId}`}>
|
|
440
595
|
{children}
|
|
@@ -444,17 +599,16 @@ const ReasoningAccordionGroup: FC<{ children?: ReactNode; endIndex: number; star
|
|
|
444
599
|
|
|
445
600
|
const ReasoningTextPart: FC<{ text: string; status?: { type: string } }> = ({ text, status }) => {
|
|
446
601
|
const displayText = text.trimStart()
|
|
602
|
+
const messageRunning = useAuiState(s => s.message.status?.type === 'running')
|
|
603
|
+
const isRunning = status?.type === 'running' || messageRunning
|
|
447
604
|
|
|
448
605
|
return (
|
|
449
|
-
<
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
>
|
|
456
|
-
{displayText}
|
|
457
|
-
</div>
|
|
606
|
+
<MarkdownTextContent
|
|
607
|
+
containerClassName="text-xs leading-snug text-muted-foreground/85"
|
|
608
|
+
containerProps={{ 'data-slot': 'aui_reasoning-text' } as ComponentProps<'div'>}
|
|
609
|
+
isRunning={isRunning}
|
|
610
|
+
text={displayText}
|
|
611
|
+
/>
|
|
458
612
|
)
|
|
459
613
|
}
|
|
460
614
|
|
|
@@ -486,7 +640,10 @@ function startOfDay(d: Date): number {
|
|
|
486
640
|
return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime()
|
|
487
641
|
}
|
|
488
642
|
|
|
489
|
-
function formatMessageTimestamp(
|
|
643
|
+
function formatMessageTimestamp(
|
|
644
|
+
value: Date | string | number | undefined,
|
|
645
|
+
labels: { today: (time: string) => string; yesterday: (time: string) => string }
|
|
646
|
+
): string {
|
|
490
647
|
if (!value) {
|
|
491
648
|
return ''
|
|
492
649
|
}
|
|
@@ -500,17 +657,19 @@ function formatMessageTimestamp(value: Date | string | number | undefined): stri
|
|
|
500
657
|
const dayDelta = Math.round((startOfDay(new Date()) - startOfDay(date)) / 86_400_000)
|
|
501
658
|
|
|
502
659
|
if (dayDelta === 0) {
|
|
503
|
-
return
|
|
660
|
+
return labels.today(TIME_FMT.format(date))
|
|
504
661
|
}
|
|
505
662
|
|
|
506
663
|
if (dayDelta === 1) {
|
|
507
|
-
return
|
|
664
|
+
return labels.yesterday(TIME_FMT.format(date))
|
|
508
665
|
}
|
|
509
666
|
|
|
510
667
|
return SHORT_FMT.format(date)
|
|
511
668
|
}
|
|
512
669
|
|
|
513
|
-
const AssistantActionBar: FC<MessageActionProps> = ({ messageId,
|
|
670
|
+
const AssistantActionBar: FC<MessageActionProps> = ({ messageId, getMessageText, onBranchInNewChat }) => {
|
|
671
|
+
const { t } = useI18n()
|
|
672
|
+
const copy = t.assistant.thread
|
|
514
673
|
const [menuOpen, setMenuOpen] = useState(false)
|
|
515
674
|
|
|
516
675
|
return (
|
|
@@ -529,15 +688,15 @@ const AssistantActionBar: FC<MessageActionProps> = ({ messageId, messageText, on
|
|
|
529
688
|
)}
|
|
530
689
|
data-slot="aui_msg-actions"
|
|
531
690
|
>
|
|
532
|
-
<CopyButton appearance="icon" buttonSize="icon"
|
|
691
|
+
<CopyButton appearance="icon" buttonSize="icon" label={copy.copy} text={getMessageText} />
|
|
533
692
|
<ActionBarPrimitive.Reload asChild>
|
|
534
|
-
<TooltipIconButton onClick={() => triggerHaptic('submit')} tooltip=
|
|
693
|
+
<TooltipIconButton onClick={() => triggerHaptic('submit')} tooltip={copy.refresh}>
|
|
535
694
|
<Codicon name="refresh" />
|
|
536
695
|
</TooltipIconButton>
|
|
537
696
|
</ActionBarPrimitive.Reload>
|
|
538
697
|
<DropdownMenu onOpenChange={setMenuOpen} open={menuOpen}>
|
|
539
698
|
<DropdownMenuTrigger asChild>
|
|
540
|
-
<TooltipIconButton tooltip=
|
|
699
|
+
<TooltipIconButton tooltip={copy.moreActions}>
|
|
541
700
|
<Codicon name="ellipsis" />
|
|
542
701
|
</TooltipIconButton>
|
|
543
702
|
</DropdownMenuTrigger>
|
|
@@ -545,9 +704,9 @@ const AssistantActionBar: FC<MessageActionProps> = ({ messageId, messageText, on
|
|
|
545
704
|
<MessageTimestamp />
|
|
546
705
|
<DropdownMenuItem onSelect={() => onBranchInNewChat?.(messageId)}>
|
|
547
706
|
<GitBranchIcon />
|
|
548
|
-
|
|
707
|
+
{copy.branchNewChat}
|
|
549
708
|
</DropdownMenuItem>
|
|
550
|
-
<ReadAloudItem
|
|
709
|
+
<ReadAloudItem getText={getMessageText} messageId={messageId} />
|
|
551
710
|
</DropdownMenuContent>
|
|
552
711
|
</DropdownMenu>
|
|
553
712
|
</ActionBarPrimitive.Root>
|
|
@@ -555,7 +714,9 @@ const AssistantActionBar: FC<MessageActionProps> = ({ messageId, messageText, on
|
|
|
555
714
|
)
|
|
556
715
|
}
|
|
557
716
|
|
|
558
|
-
const ReadAloudItem: FC<{
|
|
717
|
+
const ReadAloudItem: FC<{ getText: () => string; messageId: string }> = ({ getText, messageId }) => {
|
|
718
|
+
const { t } = useI18n()
|
|
719
|
+
const copy = t.assistant.thread
|
|
559
720
|
const voicePlayback = useStore($voicePlayback)
|
|
560
721
|
|
|
561
722
|
const readAloudStatus =
|
|
@@ -567,6 +728,8 @@ const ReadAloudItem: FC<{ messageId: string; text: string }> = ({ messageId, tex
|
|
|
567
728
|
const Icon = isPreparing ? Loader2Icon : isSpeaking ? VolumeXIcon : Volume2Icon
|
|
568
729
|
|
|
569
730
|
const read = useCallback(async () => {
|
|
731
|
+
const text = getText()
|
|
732
|
+
|
|
570
733
|
if (!text || $voicePlayback.get().status !== 'idle') {
|
|
571
734
|
return
|
|
572
735
|
}
|
|
@@ -574,27 +737,28 @@ const ReadAloudItem: FC<{ messageId: string; text: string }> = ({ messageId, tex
|
|
|
574
737
|
try {
|
|
575
738
|
await playSpeechText(text, { messageId, source: 'read-aloud' })
|
|
576
739
|
} catch (error) {
|
|
577
|
-
notifyError(error,
|
|
740
|
+
notifyError(error, copy.readAloudFailed)
|
|
578
741
|
}
|
|
579
|
-
}, [
|
|
742
|
+
}, [copy.readAloudFailed, getText, messageId])
|
|
580
743
|
|
|
581
744
|
return (
|
|
582
745
|
<DropdownMenuItem
|
|
583
|
-
disabled={isPreparing || (!isSpeaking &&
|
|
746
|
+
disabled={isPreparing || (!isSpeaking && anyPlaybackActive)}
|
|
584
747
|
onSelect={e => {
|
|
585
748
|
e.preventDefault()
|
|
586
749
|
void (isSpeaking ? stopVoicePlayback() : read())
|
|
587
750
|
}}
|
|
588
751
|
>
|
|
589
752
|
<Icon className={isPreparing ? 'animate-spin' : undefined} />
|
|
590
|
-
{isPreparing ?
|
|
753
|
+
{isPreparing ? copy.preparingAudio : isSpeaking ? copy.stopReading : copy.readAloud}
|
|
591
754
|
</DropdownMenuItem>
|
|
592
755
|
)
|
|
593
756
|
}
|
|
594
757
|
|
|
595
758
|
const MessageTimestamp: FC = () => {
|
|
759
|
+
const { t } = useI18n()
|
|
596
760
|
const createdAt = useAuiState(s => s.message.createdAt)
|
|
597
|
-
const label = formatMessageTimestamp(createdAt)
|
|
761
|
+
const label = formatMessageTimestamp(createdAt, t.assistant.thread)
|
|
598
762
|
|
|
599
763
|
if (!label) {
|
|
600
764
|
return null
|
|
@@ -609,13 +773,13 @@ const AssistantFooter: FC<MessageActionProps> = props => (
|
|
|
609
773
|
className="inline-flex h-6 items-center gap-1 text-xs text-muted-foreground"
|
|
610
774
|
hideWhenSingleBranch
|
|
611
775
|
>
|
|
612
|
-
<BranchPickerPrimitive.Previous className="grid size-6
|
|
776
|
+
<BranchPickerPrimitive.Previous className="grid size-6 place-items-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground disabled:cursor-default disabled:opacity-35">
|
|
613
777
|
<Codicon name="chevron-left" size="0.875rem" />
|
|
614
778
|
</BranchPickerPrimitive.Previous>
|
|
615
779
|
<span className="tabular-nums">
|
|
616
780
|
<BranchPickerPrimitive.Number /> / <BranchPickerPrimitive.Count />
|
|
617
781
|
</span>
|
|
618
|
-
<BranchPickerPrimitive.Next className="grid size-6
|
|
782
|
+
<BranchPickerPrimitive.Next className="grid size-6 place-items-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground disabled:cursor-default disabled:opacity-35">
|
|
619
783
|
<Codicon name="chevron-right" size="0.875rem" />
|
|
620
784
|
</BranchPickerPrimitive.Next>
|
|
621
785
|
</BranchPickerPrimitive.Root>
|
|
@@ -633,34 +797,87 @@ function messageAttachmentRefs(value: unknown): string[] {
|
|
|
633
797
|
return value.every(ref => typeof ref === 'string') ? value : EMPTY_ATTACHMENT_REFS
|
|
634
798
|
}
|
|
635
799
|
|
|
636
|
-
function StickyHumanMessageContainer({ children }: { children: ReactNode }) {
|
|
800
|
+
function StickyHumanMessageContainer({ attachments, children }: { attachments?: ReactNode; children: ReactNode }) {
|
|
637
801
|
return (
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
802
|
+
// Fragment, not a wrapper: a wrapping element becomes the sticky's
|
|
803
|
+
// containing block (it'd stick within its own height = never). The bubble
|
|
804
|
+
// and attachments are flow siblings so the bubble pins against the scroller
|
|
805
|
+
// while attachments below it scroll away.
|
|
806
|
+
<>
|
|
807
|
+
<div
|
|
808
|
+
className="group/user-message sticky z-40 -mx-4 flex w-[calc(100%+2rem)] min-w-0 max-w-none flex-col items-stretch gap-0 self-end overflow-visible bg-(--ui-chat-surface-background) px-4 pb-(--conversation-turn-gap) pt-1"
|
|
809
|
+
data-role="user"
|
|
810
|
+
data-slot="aui_user-message-root"
|
|
811
|
+
>
|
|
812
|
+
{children}
|
|
813
|
+
</div>
|
|
814
|
+
{attachments}
|
|
815
|
+
</>
|
|
645
816
|
)
|
|
646
817
|
}
|
|
647
818
|
|
|
648
819
|
// Shared "user bubble" base. Both the read-only message and the inline
|
|
649
|
-
// edit composer render the same bubble surface (rounded glass card
|
|
650
|
-
//
|
|
651
|
-
//
|
|
820
|
+
// edit composer render the same bubble surface (rounded glass card);
|
|
821
|
+
// they only differ in border weight, cursor, and padding-right (the
|
|
822
|
+
// read-only view reserves room for the restore icon).
|
|
823
|
+
//
|
|
824
|
+
// no-drag: sticky bubbles park at --sticky-human-top (~4px), sliding under the
|
|
825
|
+
// titlebar's [-webkit-app-region:drag] strips (app-shell.tsx). Electron resolves
|
|
826
|
+
// drag regions at the compositor level — z-index and pointer-events don't help —
|
|
827
|
+
// so without the carve-out, clicking a stuck bubble drags the window instead of
|
|
828
|
+
// opening the edit composer.
|
|
652
829
|
const USER_BUBBLE_BASE_CLASS =
|
|
653
|
-
'composer-human-message standalone-glass relative flex w-full min-w-0 max-w-full flex-col gap-1.5 overflow-
|
|
830
|
+
'composer-human-message standalone-glass relative flex w-full min-w-0 max-w-full flex-col gap-1.5 overflow-y-auto rounded-xl border bg-(--dt-user-bubble) px-3 py-2 text-left [-webkit-app-region:no-drag]'
|
|
654
831
|
|
|
655
832
|
const USER_ACTION_ICON_BUTTON_CLASS =
|
|
656
|
-
'grid
|
|
833
|
+
'grid place-items-center rounded-md bg-transparent text-(--ui-text-secondary) transition-colors hover:bg-(--ui-control-active-background) hover:text-foreground disabled:cursor-default disabled:text-(--ui-text-quaternary) disabled:opacity-70'
|
|
657
834
|
|
|
658
835
|
const USER_ACTION_ICON_SIZE = '0.6875rem'
|
|
659
836
|
const StopGlyph = <IconPlayerStopFilled aria-hidden className="size-3.5 -translate-y-px" />
|
|
660
837
|
|
|
838
|
+
// Background-process notifications are injected into the conversation as user
|
|
839
|
+
// messages (the agent must react to them, and message-role alternation forbids
|
|
840
|
+
// a synthetic system row mid-loop). They are NOT something the human typed, so
|
|
841
|
+
// render them as a compact system-style notice instead of a user bubble.
|
|
842
|
+
// Shape: see tools/process_registry.py format_process_notification().
|
|
843
|
+
const PROCESS_NOTIFICATION_RE = /^\[IMPORTANT: Background process [\s\S]*\]$/
|
|
844
|
+
|
|
845
|
+
const ProcessNotificationNote: FC<{ text: string }> = ({ text }) => {
|
|
846
|
+
const body = text.replace(/^\[IMPORTANT:\s*/, '').replace(/\]$/, '')
|
|
847
|
+
const newline = body.indexOf('\n')
|
|
848
|
+
const headline = (newline === -1 ? body : body.slice(0, newline)).trim()
|
|
849
|
+
const detail = newline === -1 ? '' : body.slice(newline + 1).trim()
|
|
850
|
+
|
|
851
|
+
return (
|
|
852
|
+
<div className="flex max-w-[min(86%,44rem)] flex-col gap-0.5 self-center px-2 py-0.5 text-[0.6875rem] leading-5 text-muted-foreground/60">
|
|
853
|
+
<span className="flex items-center gap-1.5">
|
|
854
|
+
<Codicon className="shrink-0 text-muted-foreground/55" name="terminal" size="0.75rem" />
|
|
855
|
+
<span className="wrap-anywhere">{headline}</span>
|
|
856
|
+
</span>
|
|
857
|
+
{detail && (
|
|
858
|
+
<details className="pl-[1.3125rem]">
|
|
859
|
+
<summary className="cursor-pointer select-none text-muted-foreground/45 hover:text-muted-foreground/70">
|
|
860
|
+
output
|
|
861
|
+
</summary>
|
|
862
|
+
<pre
|
|
863
|
+
className="mt-0.5 max-h-48 overflow-auto whitespace-pre-wrap font-mono text-[0.625rem] leading-4 text-muted-foreground/55"
|
|
864
|
+
data-selectable-text="true"
|
|
865
|
+
>
|
|
866
|
+
{detail}
|
|
867
|
+
</pre>
|
|
868
|
+
</details>
|
|
869
|
+
)}
|
|
870
|
+
</div>
|
|
871
|
+
)
|
|
872
|
+
}
|
|
873
|
+
|
|
661
874
|
const UserMessage: FC<{
|
|
662
875
|
onCancel?: () => Promise<void> | void
|
|
663
|
-
|
|
876
|
+
onRestoreToMessage?: (messageId: string) => Promise<void> | void
|
|
877
|
+
}> = ({ onCancel, onRestoreToMessage }) => {
|
|
878
|
+
const { t } = useI18n()
|
|
879
|
+
const copy = t.assistant.thread
|
|
880
|
+
const [restoreConfirmOpen, setRestoreConfirmOpen] = useState(false)
|
|
664
881
|
const messageId = useAuiState(s => s.message.id)
|
|
665
882
|
const content = useAuiState(s => s.message.content)
|
|
666
883
|
const messageText = messageContentText(content)
|
|
@@ -684,77 +901,154 @@ const UserMessage: FC<{
|
|
|
684
901
|
return messageAttachmentRefs(custom.attachmentRefs)
|
|
685
902
|
})
|
|
686
903
|
|
|
904
|
+
// Sticky human bubbles clamp to ~2 lines with a soft fade so a long prompt
|
|
905
|
+
// doesn't dominate the viewport while the response streams underneath; the
|
|
906
|
+
// clamp lifts on hover / focus (see styles.css). We measure the *unclamped*
|
|
907
|
+
// inner wrapper so the ResizeObserver only fires on real content / width
|
|
908
|
+
// changes, not on every frame while the outer max-height animates open.
|
|
909
|
+
const clampInnerRef = useRef<HTMLDivElement | null>(null)
|
|
910
|
+
const [bodyClamped, setBodyClamped] = useState(false)
|
|
911
|
+
const lastClampHeightRef = useRef(-1)
|
|
912
|
+
const lineHeightRef = useRef(0)
|
|
913
|
+
|
|
914
|
+
const measureClamp = useCallback((entries: readonly ResizeObserverEntry[]) => {
|
|
915
|
+
const inner = clampInnerRef.current
|
|
916
|
+
const outer = inner?.parentElement
|
|
917
|
+
|
|
918
|
+
if (!inner || !outer) {
|
|
919
|
+
return
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
// Prefer the size the ResizeObserver already computed — reading
|
|
923
|
+
// `scrollHeight` outside RO timing forces a synchronous layout, and with
|
|
924
|
+
// many user bubbles observed at once those reads interleave with the
|
|
925
|
+
// style write below into a read-write-read reflow cascade.
|
|
926
|
+
const entryHeight = entries.find(entry => entry.target === inner)?.borderBoxSize?.[0]?.blockSize
|
|
927
|
+
const fullHeight = Math.ceil(entryHeight ?? inner.scrollHeight)
|
|
928
|
+
|
|
929
|
+
if (fullHeight === lastClampHeightRef.current) {
|
|
930
|
+
return
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
lastClampHeightRef.current = fullHeight
|
|
934
|
+
|
|
935
|
+
// Line-height is stable for the life of the bubble (font settings don't
|
|
936
|
+
// change under it) — resolve the computed style once.
|
|
937
|
+
if (!lineHeightRef.current) {
|
|
938
|
+
const styles = getComputedStyle(inner)
|
|
939
|
+
lineHeightRef.current = parseFloat(styles.lineHeight) || 1.5 * parseFloat(styles.fontSize) || 20
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
outer.style.setProperty('--human-msg-full', `${fullHeight}px`)
|
|
943
|
+
setBodyClamped(fullHeight > lineHeightRef.current * 2 + 1)
|
|
944
|
+
}, [])
|
|
945
|
+
|
|
946
|
+
useResizeObserver(measureClamp, clampInnerRef)
|
|
947
|
+
|
|
948
|
+
// Injected background-process notification, not a human prompt — render the
|
|
949
|
+
// compact system-style notice (after all hooks above have run).
|
|
950
|
+
if (PROCESS_NOTIFICATION_RE.test(messageText.trim())) {
|
|
951
|
+
return (
|
|
952
|
+
<MessagePrimitive.Root
|
|
953
|
+
className="flex w-full min-w-0 flex-col items-stretch"
|
|
954
|
+
data-role="user"
|
|
955
|
+
data-slot="aui_user-message-root"
|
|
956
|
+
>
|
|
957
|
+
<ProcessNotificationNote text={messageText.trim()} />
|
|
958
|
+
</MessagePrimitive.Root>
|
|
959
|
+
)
|
|
960
|
+
}
|
|
961
|
+
|
|
687
962
|
const hasBody = messageText.trim().length > 0
|
|
688
963
|
const isLatestUser = messageId === latestUserId
|
|
689
964
|
const showStop = isLatestUser && threadRunning && Boolean(onCancel)
|
|
690
|
-
|
|
965
|
+
// Restore (re-run this exact prompt) is available everywhere the Stop button
|
|
966
|
+
// isn't — including mid-stream on older prompts, since the action interrupts
|
|
967
|
+
// the live turn before rewinding.
|
|
968
|
+
const showRestore = !showStop && Boolean(onRestoreToMessage) && hasBody
|
|
691
969
|
|
|
692
970
|
const bubbleClassName = cn(
|
|
693
971
|
USER_BUBBLE_BASE_CLASS,
|
|
694
|
-
'
|
|
695
|
-
|
|
972
|
+
'cursor-pointer pr-9 text-[length:var(--conversation-text-font-size)] leading-(--dt-line-height) text-foreground/95 transition-colors',
|
|
973
|
+
'border-(--ui-stroke-tertiary) hover:border-(--ui-stroke-secondary)'
|
|
696
974
|
)
|
|
697
975
|
|
|
698
|
-
const bubbleContent = (
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
</>
|
|
976
|
+
const bubbleContent = hasBody && (
|
|
977
|
+
// Render the user's text through a minimal markdown pipeline:
|
|
978
|
+
// backtick `code` and ``` fenced ``` blocks, with directive chips
|
|
979
|
+
// (`@file:` etc.) still resolved inside the plain-text spans.
|
|
980
|
+
<div className="sticky-human-clamp" data-clamped={bodyClamped ? 'true' : undefined}>
|
|
981
|
+
{/* Match the edit composer's collapsed line box (min-h-[1.25rem]) so
|
|
982
|
+
clicking to edit can't grow the bubble by a sub-pixel and reflow the
|
|
983
|
+
turn 1px. */}
|
|
984
|
+
<div className="min-h-[1.25rem]" ref={clampInnerRef}>
|
|
985
|
+
<UserMessageText className="wrap-anywhere" text={messageText} />
|
|
986
|
+
</div>
|
|
987
|
+
</div>
|
|
711
988
|
)
|
|
712
989
|
|
|
713
990
|
return (
|
|
714
991
|
<MessagePrimitive.Root asChild>
|
|
715
|
-
<StickyHumanMessageContainer
|
|
992
|
+
<StickyHumanMessageContainer
|
|
993
|
+
attachments={
|
|
994
|
+
// Attachments live BELOW the sticky bubble in normal flow, so they
|
|
995
|
+
// scroll away behind the pinned bubble instead of riding along with
|
|
996
|
+
// it. Image refs render as thumbnails, file refs as chips; no border.
|
|
997
|
+
attachmentRefs.length > 0 ? (
|
|
998
|
+
<div className="flex flex-wrap gap-1 -mt-3 mb-2">
|
|
999
|
+
<DirectiveContent text={attachmentRefs.join(' ')} />
|
|
1000
|
+
</div>
|
|
1001
|
+
) : null
|
|
1002
|
+
}
|
|
1003
|
+
>
|
|
716
1004
|
<ActionBarPrimitive.Root className="relative w-full max-w-full" data-slot="aui_user-bubble-actions">
|
|
717
1005
|
<div className="human-message-with-todos-wrapper flex w-full flex-col gap-0">
|
|
718
1006
|
<div className="relative w-full">
|
|
719
|
-
{
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
<
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
)}
|
|
1007
|
+
{/* Always editable — clicking opens the edit composer even while a
|
|
1008
|
+
turn streams; sending the edit reverts (interrupt + rewind). */}
|
|
1009
|
+
<ActionBarPrimitive.Edit asChild>
|
|
1010
|
+
<button
|
|
1011
|
+
aria-label={copy.editMessage}
|
|
1012
|
+
className={bubbleClassName}
|
|
1013
|
+
onClick={() => triggerHaptic('selection')}
|
|
1014
|
+
onPointerDown={() => notifyThreadEditOpen()}
|
|
1015
|
+
title={copy.editMessage}
|
|
1016
|
+
type="button"
|
|
1017
|
+
>
|
|
1018
|
+
{bubbleContent}
|
|
1019
|
+
</button>
|
|
1020
|
+
</ActionBarPrimitive.Edit>
|
|
734
1021
|
{(showStop || showRestore) && (
|
|
735
1022
|
<div className="pointer-events-none absolute right-2 bottom-2 z-10 flex items-center justify-center opacity-0 transition-opacity group-hover/user-message:opacity-100 group-focus-within/user-message:opacity-100">
|
|
736
1023
|
{showStop ? (
|
|
737
1024
|
<button
|
|
738
|
-
aria-label=
|
|
1025
|
+
aria-label={copy.stop}
|
|
739
1026
|
className={cn('pointer-events-auto size-5', USER_ACTION_ICON_BUTTON_CLASS)}
|
|
740
1027
|
onClick={event => {
|
|
741
1028
|
event.preventDefault()
|
|
742
1029
|
event.stopPropagation()
|
|
743
1030
|
void onCancel?.()
|
|
744
1031
|
}}
|
|
745
|
-
title=
|
|
1032
|
+
title={copy.stop}
|
|
746
1033
|
type="button"
|
|
747
1034
|
>
|
|
748
1035
|
{StopGlyph}
|
|
749
1036
|
</button>
|
|
750
1037
|
) : (
|
|
751
|
-
<
|
|
752
|
-
aria-
|
|
753
|
-
className=
|
|
754
|
-
|
|
1038
|
+
<button
|
|
1039
|
+
aria-label={copy.restoreCheckpoint}
|
|
1040
|
+
className={cn('pointer-events-auto size-6', USER_ACTION_ICON_BUTTON_CLASS)}
|
|
1041
|
+
onClick={event => {
|
|
1042
|
+
event.preventDefault()
|
|
1043
|
+
event.stopPropagation()
|
|
1044
|
+
triggerHaptic('selection')
|
|
1045
|
+
setRestoreConfirmOpen(true)
|
|
1046
|
+
}}
|
|
1047
|
+
title={copy.restoreFromHere}
|
|
1048
|
+
type="button"
|
|
755
1049
|
>
|
|
756
1050
|
<Codicon name="discard" size="0.875rem" />
|
|
757
|
-
</
|
|
1051
|
+
</button>
|
|
758
1052
|
)}
|
|
759
1053
|
</div>
|
|
760
1054
|
)}
|
|
@@ -765,29 +1059,41 @@ const UserMessage: FC<{
|
|
|
765
1059
|
>
|
|
766
1060
|
<span aria-hidden className="checkpoint-icon size-1.5 rounded-full border border-current" />
|
|
767
1061
|
<BranchPickerPrimitive.Previous
|
|
768
|
-
className="checkpoint-restore-text
|
|
769
|
-
title=
|
|
1062
|
+
className="checkpoint-restore-text rounded-sm bg-transparent px-1 opacity-65 hover:opacity-100 disabled:hidden disabled:cursor-default"
|
|
1063
|
+
title={copy.restorePrevious}
|
|
770
1064
|
>
|
|
771
|
-
|
|
1065
|
+
{copy.restoreCheckpoint}
|
|
772
1066
|
</BranchPickerPrimitive.Previous>
|
|
773
1067
|
<span className="checkpoint-divider opacity-55">
|
|
774
1068
|
<BranchPickerPrimitive.Number />/<BranchPickerPrimitive.Count />
|
|
775
1069
|
</span>
|
|
776
1070
|
<BranchPickerPrimitive.Next
|
|
777
|
-
className="checkpoint-restore-text
|
|
778
|
-
title=
|
|
1071
|
+
className="checkpoint-restore-text rounded-sm bg-transparent px-1 opacity-65 hover:opacity-100 disabled:hidden disabled:cursor-default"
|
|
1072
|
+
title={copy.restoreNext}
|
|
779
1073
|
>
|
|
780
|
-
|
|
1074
|
+
{copy.goForward}
|
|
781
1075
|
</BranchPickerPrimitive.Next>
|
|
782
1076
|
</BranchPickerPrimitive.Root>
|
|
783
1077
|
</div>
|
|
784
1078
|
</ActionBarPrimitive.Root>
|
|
1079
|
+
{showRestore && (
|
|
1080
|
+
<ConfirmDialog
|
|
1081
|
+
confirmLabel={copy.restoreConfirm}
|
|
1082
|
+
description={copy.restoreBody}
|
|
1083
|
+
destructive
|
|
1084
|
+
onClose={() => setRestoreConfirmOpen(false)}
|
|
1085
|
+
onConfirm={() => onRestoreToMessage?.(messageId)}
|
|
1086
|
+
open={restoreConfirmOpen}
|
|
1087
|
+
title={copy.restoreTitle}
|
|
1088
|
+
/>
|
|
1089
|
+
)}
|
|
785
1090
|
</StickyHumanMessageContainer>
|
|
786
1091
|
</MessagePrimitive.Root>
|
|
787
1092
|
)
|
|
788
1093
|
}
|
|
789
1094
|
|
|
790
1095
|
const SLASH_STATUS_RE = /^slash:(?<command>\/[^\n]+)\n(?<output>[\s\S]*)$/
|
|
1096
|
+
const STEER_NOTE_RE = /^steer:(?<text>[\s\S]+)$/
|
|
791
1097
|
|
|
792
1098
|
const SystemMessage: FC = () => {
|
|
793
1099
|
const text = useAuiState(s => messageContentText(s.message.content))
|
|
@@ -796,29 +1102,66 @@ const SystemMessage: FC = () => {
|
|
|
796
1102
|
return null
|
|
797
1103
|
}
|
|
798
1104
|
|
|
1105
|
+
const steerNote = text.match(STEER_NOTE_RE)
|
|
1106
|
+
|
|
1107
|
+
if (steerNote?.groups) {
|
|
1108
|
+
return (
|
|
1109
|
+
<MessagePrimitive.Root
|
|
1110
|
+
className="flex max-w-[min(86%,44rem)] items-center gap-1.5 self-center px-2 py-0.5 text-[0.6875rem] leading-5 text-muted-foreground/60"
|
|
1111
|
+
data-role="system"
|
|
1112
|
+
data-slot="aui_system-message-root"
|
|
1113
|
+
>
|
|
1114
|
+
<Codicon className="text-muted-foreground/55" name="compass" size="0.75rem" />
|
|
1115
|
+
<span className="text-muted-foreground/55">steered</span>
|
|
1116
|
+
<span className="text-muted-foreground/35">·</span>
|
|
1117
|
+
<span className="whitespace-pre-wrap">{steerNote.groups.text.trim()}</span>
|
|
1118
|
+
</MessagePrimitive.Root>
|
|
1119
|
+
)
|
|
1120
|
+
}
|
|
1121
|
+
|
|
799
1122
|
const slashStatus = text.match(SLASH_STATUS_RE)
|
|
800
1123
|
|
|
801
1124
|
if (slashStatus?.groups) {
|
|
1125
|
+
const output = slashStatus.groups.output.trim()
|
|
1126
|
+
// Single-line status (e.g. "model → x") reads best centered inline; padded
|
|
1127
|
+
// multiline output (catalogs, usage tables) needs left-aligned, wider room
|
|
1128
|
+
// or the column alignment breaks.
|
|
1129
|
+
const multiline = output.includes('\n')
|
|
1130
|
+
|
|
802
1131
|
return (
|
|
803
1132
|
<MessagePrimitive.Root
|
|
804
|
-
className=
|
|
1133
|
+
className={cn(
|
|
1134
|
+
'w-[60%] max-w-[44rem] self-center px-2 py-0.5 text-[0.6875rem] leading-5 text-muted-foreground/60',
|
|
1135
|
+
multiline ? 'text-left' : 'text-center'
|
|
1136
|
+
)}
|
|
805
1137
|
data-role="system"
|
|
806
1138
|
data-slot="aui_system-message-root"
|
|
807
1139
|
>
|
|
808
1140
|
<span className="font-mono text-muted-foreground/55">{slashStatus.groups.command}</span>
|
|
809
|
-
|
|
810
|
-
|
|
1141
|
+
{multiline ? (
|
|
1142
|
+
<LinkifiedText className="mt-0.5 block whitespace-pre-wrap" explicitOnly pretty={false} text={output} />
|
|
1143
|
+
) : (
|
|
1144
|
+
<>
|
|
1145
|
+
<span className="mx-1.5 text-muted-foreground/35">·</span>
|
|
1146
|
+
<LinkifiedText className="whitespace-pre-wrap" explicitOnly pretty={false} text={output} />
|
|
1147
|
+
</>
|
|
1148
|
+
)}
|
|
811
1149
|
</MessagePrimitive.Root>
|
|
812
1150
|
)
|
|
813
1151
|
}
|
|
814
1152
|
|
|
1153
|
+
const multiline = text.includes('\n')
|
|
1154
|
+
|
|
815
1155
|
return (
|
|
816
1156
|
<MessagePrimitive.Root
|
|
817
|
-
className=
|
|
1157
|
+
className={cn(
|
|
1158
|
+
'w-[60%] max-w-[44rem] self-center px-2 py-0.5 text-[0.6875rem] leading-5 text-muted-foreground/55',
|
|
1159
|
+
multiline ? 'text-left' : 'text-center'
|
|
1160
|
+
)}
|
|
818
1161
|
data-role="system"
|
|
819
1162
|
data-slot="aui_system-message-root"
|
|
820
1163
|
>
|
|
821
|
-
<
|
|
1164
|
+
<LinkifiedText className="whitespace-pre-wrap" explicitOnly pretty={false} text={text} />
|
|
822
1165
|
</MessagePrimitive.Root>
|
|
823
1166
|
)
|
|
824
1167
|
}
|
|
@@ -830,6 +1173,8 @@ interface UserEditComposerProps {
|
|
|
830
1173
|
}
|
|
831
1174
|
|
|
832
1175
|
const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }) => {
|
|
1176
|
+
const { t } = useI18n()
|
|
1177
|
+
const copy = t.assistant.thread
|
|
833
1178
|
const aui = useAui()
|
|
834
1179
|
const draft = useAuiState(s => s.composer.text)
|
|
835
1180
|
const rootRef = useRef<HTMLDivElement | null>(null)
|
|
@@ -840,14 +1185,24 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
|
|
|
840
1185
|
const [trigger, setTrigger] = useState<TriggerState | null>(null)
|
|
841
1186
|
const [triggerActive, setTriggerActive] = useState(0)
|
|
842
1187
|
const [triggerItems, setTriggerItems] = useState<readonly Unstable_TriggerItem[]>([])
|
|
1188
|
+
// See index.tsx: set in keydown when the open popover consumes a nav/control
|
|
1189
|
+
// key so the matching keyup skips refreshTrigger (timing-immune vs reading
|
|
1190
|
+
// `trigger`, which keyup sees as already-null after Escape).
|
|
1191
|
+
const triggerKeyConsumedRef = useRef(false)
|
|
843
1192
|
const [triggerPlacement, setTriggerPlacement] = useState<'bottom' | 'top'>('top')
|
|
844
1193
|
const [focusRequestId, setFocusRequestId] = useState(0)
|
|
845
1194
|
const [submitting, setSubmitting] = useState(false)
|
|
1195
|
+
// True while OS-drop files are being staged/uploaded into the session. Blocks
|
|
1196
|
+
// submit and shows a spinner so confirming the edit can't race the async
|
|
1197
|
+
// upload and drop the gateway-side ref before it lands in the draft.
|
|
1198
|
+
const [staging, setStaging] = useState(false)
|
|
846
1199
|
const expanded = draft.includes('\n')
|
|
847
1200
|
const canSubmit = draft.trim().length > 0
|
|
848
1201
|
const at = useAtCompletions({ cwd, gateway, sessionId })
|
|
849
1202
|
const slash = useSlashCompletions({ gateway })
|
|
850
1203
|
|
|
1204
|
+
useEffect(() => () => notifyThreadEditClose(), [])
|
|
1205
|
+
|
|
851
1206
|
const focusEditor = useCallback(() => {
|
|
852
1207
|
const editor = editorRef.current
|
|
853
1208
|
|
|
@@ -964,8 +1319,15 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
|
|
|
964
1319
|
}
|
|
965
1320
|
|
|
966
1321
|
setTrigger(detected)
|
|
967
|
-
|
|
968
|
-
|
|
1322
|
+
|
|
1323
|
+
// Only reset the highlight when the trigger actually changed (opened, or
|
|
1324
|
+
// the query/kind differs). Re-detecting the *same* trigger — e.g. on a
|
|
1325
|
+
// caret move (mouseup) or a stray refresh — must preserve the user's
|
|
1326
|
+
// current selection instead of snapping back to the first item.
|
|
1327
|
+
if (detected?.kind !== trigger?.kind || detected?.query !== trigger?.query) {
|
|
1328
|
+
setTriggerActive(0)
|
|
1329
|
+
}
|
|
1330
|
+
}, [trigger])
|
|
969
1331
|
|
|
970
1332
|
const closeTrigger = useCallback(() => {
|
|
971
1333
|
setTrigger(null)
|
|
@@ -1052,18 +1414,14 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
|
|
|
1052
1414
|
[aui, closeTrigger, refreshTrigger, requestEditFocus, trigger]
|
|
1053
1415
|
)
|
|
1054
1416
|
|
|
1055
|
-
const
|
|
1056
|
-
(
|
|
1417
|
+
const insertRefStrings = useCallback(
|
|
1418
|
+
(refs: InlineRefInput[]) => {
|
|
1057
1419
|
const editor = editorRef.current
|
|
1058
1420
|
|
|
1059
|
-
if (!editor) {
|
|
1421
|
+
if (!editor || refs.length === 0) {
|
|
1060
1422
|
return false
|
|
1061
1423
|
}
|
|
1062
1424
|
|
|
1063
|
-
const refs = candidates
|
|
1064
|
-
.map(candidate => droppedFileInlineRef(candidate, cwd))
|
|
1065
|
-
.filter((ref): ref is string => Boolean(ref))
|
|
1066
|
-
|
|
1067
1425
|
const nextDraft = insertInlineRefsIntoEditor(editor, refs)
|
|
1068
1426
|
|
|
1069
1427
|
if (nextDraft === null) {
|
|
@@ -1076,7 +1434,63 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
|
|
|
1076
1434
|
|
|
1077
1435
|
return true
|
|
1078
1436
|
},
|
|
1079
|
-
[aui,
|
|
1437
|
+
[aui, requestEditFocus]
|
|
1438
|
+
)
|
|
1439
|
+
|
|
1440
|
+
const insertDroppedRefs = useCallback(
|
|
1441
|
+
(candidates: ReturnType<typeof extractDroppedFiles>) => insertRefStrings(droppedFileInlineRefs(candidates, cwd)),
|
|
1442
|
+
[cwd, insertRefStrings]
|
|
1443
|
+
)
|
|
1444
|
+
|
|
1445
|
+
// OS/Finder drops carry an absolute path on THIS machine — the gateway can't
|
|
1446
|
+
// read it in remote mode, and an image needs its bytes uploaded for vision.
|
|
1447
|
+
// Stage each through the same file.attach/image.attach_bytes pipeline the main
|
|
1448
|
+
// composer uses, then insert the *gateway-side* ref the agent can resolve —
|
|
1449
|
+
// never the raw local path (the MahmoudR remote-attach bug, which the main
|
|
1450
|
+
// composer fixes but this edit composer used to reproduce).
|
|
1451
|
+
const uploadOsDropRefs = useCallback(
|
|
1452
|
+
async (osDrops: ReturnType<typeof extractDroppedFiles>): Promise<InlineRefInput[]> => {
|
|
1453
|
+
if (!gateway || !sessionId) {
|
|
1454
|
+
// No session to stage into — best-effort inline refs (matches old path).
|
|
1455
|
+
return droppedFileInlineRefs(osDrops, cwd)
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
const remote = $connection.get()?.mode === 'remote'
|
|
1459
|
+
|
|
1460
|
+
const requestGateway = <T,>(method: string, params?: Record<string, unknown>) =>
|
|
1461
|
+
gateway.request<T>(method, params)
|
|
1462
|
+
|
|
1463
|
+
const refs: InlineRefInput[] = []
|
|
1464
|
+
|
|
1465
|
+
for (const candidate of osDrops) {
|
|
1466
|
+
const path = candidate.path || ''
|
|
1467
|
+
|
|
1468
|
+
if (!path) {
|
|
1469
|
+
continue
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
const kind: ComposerAttachment['kind'] =
|
|
1473
|
+
candidate.file?.type.startsWith('image/') || isImagePath(candidate.file?.name || path) ? 'image' : 'file'
|
|
1474
|
+
|
|
1475
|
+
try {
|
|
1476
|
+
const uploaded = await uploadComposerAttachment(
|
|
1477
|
+
{ detail: path, id: attachmentId(kind, path), kind, label: pathLabel(path), path },
|
|
1478
|
+
{ remote, requestGateway, sessionId }
|
|
1479
|
+
)
|
|
1480
|
+
|
|
1481
|
+
const ref = attachmentDisplayText(uploaded)
|
|
1482
|
+
|
|
1483
|
+
if (ref) {
|
|
1484
|
+
refs.push(ref)
|
|
1485
|
+
}
|
|
1486
|
+
} catch (err) {
|
|
1487
|
+
notifyError(err, t.desktop.dropFiles)
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
return refs
|
|
1492
|
+
},
|
|
1493
|
+
[cwd, gateway, sessionId, t.desktop.dropFiles]
|
|
1080
1494
|
)
|
|
1081
1495
|
|
|
1082
1496
|
const resetDragState = useCallback(() => {
|
|
@@ -1130,9 +1544,25 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
|
|
|
1130
1544
|
event.stopPropagation()
|
|
1131
1545
|
resetDragState()
|
|
1132
1546
|
|
|
1133
|
-
|
|
1547
|
+
// In-app drags (project tree / gutter) are workspace-relative paths that
|
|
1548
|
+
// resolve on the gateway as-is, so they stay inline refs. OS drops need to
|
|
1549
|
+
// be staged + uploaded first, then their gateway-side ref is inserted.
|
|
1550
|
+
const { inAppRefs, osDrops } = partitionDroppedFiles(candidates)
|
|
1551
|
+
|
|
1552
|
+
if (insertDroppedRefs(inAppRefs)) {
|
|
1134
1553
|
triggerHaptic('selection')
|
|
1135
1554
|
}
|
|
1555
|
+
|
|
1556
|
+
if (osDrops.length) {
|
|
1557
|
+
setStaging(true)
|
|
1558
|
+
void uploadOsDropRefs(osDrops)
|
|
1559
|
+
.then(refs => {
|
|
1560
|
+
if (insertRefStrings(refs)) {
|
|
1561
|
+
triggerHaptic('selection')
|
|
1562
|
+
}
|
|
1563
|
+
})
|
|
1564
|
+
.finally(() => setStaging(false))
|
|
1565
|
+
}
|
|
1136
1566
|
}
|
|
1137
1567
|
|
|
1138
1568
|
const handleInput = (event: FormEvent<HTMLDivElement>) => {
|
|
@@ -1163,7 +1593,7 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
|
|
|
1163
1593
|
const submitEdit = (editor: HTMLDivElement) => {
|
|
1164
1594
|
const nextDraft = syncDraftFromEditor(editor)
|
|
1165
1595
|
|
|
1166
|
-
if (submitting || !nextDraft.trim()) {
|
|
1596
|
+
if (submitting || staging || !nextDraft.trim()) {
|
|
1167
1597
|
return
|
|
1168
1598
|
}
|
|
1169
1599
|
|
|
@@ -1198,6 +1628,7 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
|
|
|
1198
1628
|
if (trigger && triggerItems.length > 0) {
|
|
1199
1629
|
if (event.key === 'ArrowDown') {
|
|
1200
1630
|
event.preventDefault()
|
|
1631
|
+
triggerKeyConsumedRef.current = true
|
|
1201
1632
|
setTriggerActive(idx => (idx + 1) % triggerItems.length)
|
|
1202
1633
|
|
|
1203
1634
|
return
|
|
@@ -1205,6 +1636,7 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
|
|
|
1205
1636
|
|
|
1206
1637
|
if (event.key === 'ArrowUp') {
|
|
1207
1638
|
event.preventDefault()
|
|
1639
|
+
triggerKeyConsumedRef.current = true
|
|
1208
1640
|
setTriggerActive(idx => (idx - 1 + triggerItems.length) % triggerItems.length)
|
|
1209
1641
|
|
|
1210
1642
|
return
|
|
@@ -1212,6 +1644,7 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
|
|
|
1212
1644
|
|
|
1213
1645
|
if (event.key === 'Enter' || event.key === 'Tab') {
|
|
1214
1646
|
event.preventDefault()
|
|
1647
|
+
triggerKeyConsumedRef.current = true
|
|
1215
1648
|
const item = triggerItems[triggerActive]
|
|
1216
1649
|
|
|
1217
1650
|
if (item) {
|
|
@@ -1223,6 +1656,7 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
|
|
|
1223
1656
|
|
|
1224
1657
|
if (event.key === 'Escape') {
|
|
1225
1658
|
event.preventDefault()
|
|
1659
|
+
triggerKeyConsumedRef.current = true
|
|
1226
1660
|
closeTrigger()
|
|
1227
1661
|
|
|
1228
1662
|
return
|
|
@@ -1242,6 +1676,22 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
|
|
|
1242
1676
|
}
|
|
1243
1677
|
}
|
|
1244
1678
|
|
|
1679
|
+
const handleKeyUp = () => {
|
|
1680
|
+
// If this keyup belongs to a key the open trigger popover already consumed
|
|
1681
|
+
// in keydown (Arrow/Enter/Tab/Escape), skip the refresh. Those keys never
|
|
1682
|
+
// edit text, and for Escape the keydown already closed the menu — a refresh
|
|
1683
|
+
// here would re-detect the still-present `/` and instantly reopen it. We
|
|
1684
|
+
// read a ref set during keydown rather than `trigger`, because by keyup
|
|
1685
|
+
// time React has re-rendered and `trigger` may already be null.
|
|
1686
|
+
if (triggerKeyConsumedRef.current) {
|
|
1687
|
+
triggerKeyConsumedRef.current = false
|
|
1688
|
+
|
|
1689
|
+
return
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
window.setTimeout(refreshTrigger, 0)
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1245
1695
|
return (
|
|
1246
1696
|
<ComposerPrimitive.Root className="contents" data-slot="aui_edit-composer-root">
|
|
1247
1697
|
<StickyHumanMessageContainer>
|
|
@@ -1275,16 +1725,17 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
|
|
|
1275
1725
|
data-expanded={expanded ? 'true' : undefined}
|
|
1276
1726
|
>
|
|
1277
1727
|
<div
|
|
1278
|
-
aria-label=
|
|
1279
|
-
|
|
1728
|
+
aria-label={copy.editMessage}
|
|
1729
|
+
autoCapitalize="off"
|
|
1730
|
+
autoCorrect="off"
|
|
1280
1731
|
className={cn(
|
|
1281
|
-
'ui-prompt-input-editor__input max-h-48 w-full resize-none bg-transparent p-0 pr-7 text-[length:var(--conversation-text-font-size)]
|
|
1732
|
+
'ui-prompt-input-editor__input max-h-48 w-full resize-none bg-transparent p-0 pr-7 text-[length:var(--conversation-text-font-size)] text-foreground/95 outline-none',
|
|
1282
1733
|
'empty:before:content-[attr(data-placeholder)] empty:before:text-muted-foreground/60',
|
|
1283
1734
|
'**:data-ref-text:cursor-default',
|
|
1284
1735
|
expanded ? 'min-h-16' : 'min-h-[1.25rem]'
|
|
1285
1736
|
)}
|
|
1286
1737
|
contentEditable
|
|
1287
|
-
data-placeholder=
|
|
1738
|
+
data-placeholder={copy.editMessage}
|
|
1288
1739
|
data-slot={RICH_INPUT_SLOT}
|
|
1289
1740
|
onBlur={() => window.setTimeout(closeTrigger, 80)}
|
|
1290
1741
|
onDragOver={handleDragOver}
|
|
@@ -1292,18 +1743,44 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
|
|
|
1292
1743
|
onFocus={() => markActiveComposer('edit')}
|
|
1293
1744
|
onInput={handleInput}
|
|
1294
1745
|
onKeyDown={handleKeyDown}
|
|
1295
|
-
onKeyUp={
|
|
1746
|
+
onKeyUp={handleKeyUp}
|
|
1296
1747
|
onMouseUp={refreshTrigger}
|
|
1297
1748
|
onPaste={handlePaste}
|
|
1298
1749
|
ref={editorRef}
|
|
1299
1750
|
role="textbox"
|
|
1751
|
+
spellCheck={false}
|
|
1300
1752
|
suppressContentEditableWarning
|
|
1301
1753
|
/>
|
|
1302
|
-
<ComposerPrimitive.Input
|
|
1754
|
+
<ComposerPrimitive.Input
|
|
1755
|
+
asChild
|
|
1756
|
+
className="sr-only"
|
|
1757
|
+
submitMode="ctrlEnter"
|
|
1758
|
+
tabIndex={-1}
|
|
1759
|
+
unstable_focusOnScrollToBottom={false}
|
|
1760
|
+
>
|
|
1761
|
+
<textarea
|
|
1762
|
+
aria-hidden
|
|
1763
|
+
autoCapitalize="off"
|
|
1764
|
+
autoComplete="off"
|
|
1765
|
+
autoCorrect="off"
|
|
1766
|
+
className="sr-only"
|
|
1767
|
+
spellCheck={false}
|
|
1768
|
+
tabIndex={-1}
|
|
1769
|
+
/>
|
|
1770
|
+
</ComposerPrimitive.Input>
|
|
1771
|
+
{staging && (
|
|
1772
|
+
<span
|
|
1773
|
+
className="pointer-events-none absolute bottom-2 left-2 inline-flex items-center gap-1 rounded-full bg-background/80 px-1.5 py-0.5 text-[0.62rem] text-muted-foreground backdrop-blur-[1px]"
|
|
1774
|
+
data-slot="aui_edit-staging"
|
|
1775
|
+
>
|
|
1776
|
+
<Loader2Icon className="size-3 animate-spin" />
|
|
1777
|
+
{copy.attachingFile}
|
|
1778
|
+
</span>
|
|
1779
|
+
)}
|
|
1303
1780
|
<button
|
|
1304
|
-
aria-label=
|
|
1781
|
+
aria-label={copy.sendEdited}
|
|
1305
1782
|
className={cn('absolute right-2 bottom-2 size-5', USER_ACTION_ICON_BUTTON_CLASS)}
|
|
1306
|
-
disabled={!canSubmit || submitting}
|
|
1783
|
+
disabled={!canSubmit || submitting || staging}
|
|
1307
1784
|
onClick={() => {
|
|
1308
1785
|
const editor = editorRef.current
|
|
1309
1786
|
|
|
@@ -1311,7 +1788,7 @@ const UserEditComposer: FC<UserEditComposerProps> = ({ cwd, gateway, sessionId }
|
|
|
1311
1788
|
submitEdit(editor)
|
|
1312
1789
|
}
|
|
1313
1790
|
}}
|
|
1314
|
-
title=
|
|
1791
|
+
title={copy.sendEdited}
|
|
1315
1792
|
type="button"
|
|
1316
1793
|
>
|
|
1317
1794
|
{submitting ? StopGlyph : <Codicon name="arrow-up" size={USER_ACTION_ICON_SIZE} />}
|