@clawpump/claw-agent 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +2294 -3146
- 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/clawpump_cli.py +3 -3
- 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 +216 -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/distribution.py +227 -0
- package/agent/hermes_cli/doctor.py +255 -14
- package/agent/hermes_cli/dump.py +60 -6
- package/agent/hermes_cli/env_loader.py +33 -0
- package/agent/hermes_cli/gateway.py +755 -103
- package/agent/hermes_cli/gateway_enroll.py +250 -0
- package/agent/hermes_cli/gateway_windows.py +254 -11
- package/agent/hermes_cli/gui_uninstall.py +285 -0
- package/agent/hermes_cli/inventory.py +105 -4
- package/agent/hermes_cli/kanban.py +58 -71
- package/agent/hermes_cli/kanban_db.py +391 -14
- package/agent/hermes_cli/kanban_decompose.py +2 -2
- package/agent/hermes_cli/kanban_specify.py +3 -1
- package/agent/hermes_cli/logs.py +2 -0
- package/agent/hermes_cli/main.py +2889 -5287
- package/agent/hermes_cli/managed_scope.py +214 -0
- package/agent/hermes_cli/managed_uv.py +254 -0
- package/agent/hermes_cli/mcp_catalog.py +6 -3
- package/agent/hermes_cli/mcp_config.py +145 -21
- package/agent/hermes_cli/mcp_security.py +96 -0
- package/agent/hermes_cli/mcp_startup.py +32 -3
- package/agent/hermes_cli/memory_providers.py +149 -0
- package/agent/hermes_cli/memory_setup.py +97 -42
- package/agent/hermes_cli/middleware.py +313 -0
- package/agent/hermes_cli/model_catalog.py +31 -0
- package/agent/hermes_cli/model_cost_guard.py +134 -0
- package/agent/hermes_cli/model_normalize.py +2 -1
- package/agent/hermes_cli/model_setup_flows.py +2759 -0
- package/agent/hermes_cli/model_switch.py +242 -27
- package/agent/hermes_cli/models.py +284 -44
- package/agent/hermes_cli/nous_account.py +33 -6
- package/agent/hermes_cli/nous_billing.py +406 -0
- package/agent/hermes_cli/nous_subscription.py +202 -5
- package/agent/hermes_cli/platforms.py +1 -0
- package/agent/hermes_cli/plugins.py +218 -18
- package/agent/hermes_cli/plugins_cmd.py +249 -105
- package/agent/hermes_cli/portal_cli.py +56 -16
- package/agent/hermes_cli/profile_distribution.py +6 -1
- package/agent/hermes_cli/profiles.py +283 -32
- package/agent/hermes_cli/provider_catalog.py +170 -0
- package/agent/hermes_cli/providers.py +4 -1
- package/agent/hermes_cli/pty_bridge.py +53 -4
- package/agent/hermes_cli/runtime_provider.py +216 -34
- package/agent/hermes_cli/secret_prompt.py +4 -4
- package/agent/hermes_cli/secrets_cli.py +24 -0
- package/agent/hermes_cli/send_cmd.py +28 -2
- package/agent/hermes_cli/service_manager.py +166 -19
- package/agent/hermes_cli/session_listing.py +97 -0
- package/agent/hermes_cli/setup.py +158 -94
- package/agent/hermes_cli/setup_whatsapp_cloud.py +541 -0
- package/agent/hermes_cli/skills_config.py +8 -2
- package/agent/hermes_cli/skills_hub.py +149 -7
- package/agent/hermes_cli/status.py +2 -2
- package/agent/hermes_cli/subcommands/__init__.py +18 -0
- package/agent/hermes_cli/subcommands/_shared.py +29 -0
- package/agent/hermes_cli/subcommands/acp.py +52 -0
- package/agent/hermes_cli/subcommands/auth.py +109 -0
- package/agent/hermes_cli/subcommands/backup.py +38 -0
- package/agent/hermes_cli/subcommands/claw.py +92 -0
- package/agent/hermes_cli/subcommands/config.py +49 -0
- package/agent/hermes_cli/subcommands/cron.py +163 -0
- package/agent/hermes_cli/subcommands/dashboard.py +143 -0
- package/agent/hermes_cli/subcommands/debug.py +77 -0
- package/agent/hermes_cli/subcommands/doctor.py +35 -0
- package/agent/hermes_cli/subcommands/dump.py +28 -0
- package/agent/hermes_cli/subcommands/gateway.py +332 -0
- package/agent/hermes_cli/subcommands/gui.py +63 -0
- package/agent/hermes_cli/subcommands/hooks.py +77 -0
- package/agent/hermes_cli/subcommands/import_cmd.py +31 -0
- package/agent/hermes_cli/subcommands/insights.py +25 -0
- package/agent/hermes_cli/subcommands/login.py +78 -0
- package/agent/hermes_cli/subcommands/logout.py +28 -0
- package/agent/hermes_cli/subcommands/logs.py +78 -0
- package/agent/hermes_cli/subcommands/mcp.py +108 -0
- package/agent/hermes_cli/subcommands/memory.py +53 -0
- package/agent/hermes_cli/subcommands/model.py +72 -0
- package/agent/hermes_cli/subcommands/pairing.py +36 -0
- package/agent/hermes_cli/subcommands/plugins.py +94 -0
- package/agent/hermes_cli/subcommands/postinstall.py +23 -0
- package/agent/hermes_cli/subcommands/profile.py +203 -0
- package/agent/hermes_cli/subcommands/prompt_size.py +36 -0
- package/agent/hermes_cli/subcommands/security.py +62 -0
- package/agent/hermes_cli/subcommands/setup.py +58 -0
- package/agent/hermes_cli/subcommands/skills.py +298 -0
- package/agent/hermes_cli/subcommands/slack.py +60 -0
- package/agent/hermes_cli/subcommands/status.py +28 -0
- package/agent/hermes_cli/subcommands/tools.py +95 -0
- package/agent/hermes_cli/subcommands/uninstall.py +41 -0
- package/agent/hermes_cli/subcommands/update.py +70 -0
- package/agent/hermes_cli/subcommands/version.py +18 -0
- package/agent/hermes_cli/subcommands/webhook.py +76 -0
- package/agent/hermes_cli/subcommands/whatsapp.py +22 -0
- package/agent/hermes_cli/suggestions_cmd.py +153 -0
- package/agent/hermes_cli/telegram_managed_bot.py +358 -0
- package/agent/hermes_cli/tips.py +3 -4
- package/agent/hermes_cli/tools_config.py +155 -28
- package/agent/hermes_cli/uninstall.py +231 -35
- package/agent/hermes_cli/web_server.py +6188 -975
- package/agent/hermes_cli/win_pty_bridge.py +179 -0
- package/agent/hermes_cli/write_approval_commands.py +209 -0
- package/agent/hermes_constants.py +164 -33
- package/agent/hermes_logging.py +74 -2
- package/agent/hermes_state.py +919 -106
- package/agent/hermes_time.py +20 -0
- package/agent/locales/af.yaml +23 -0
- package/agent/locales/de.yaml +23 -0
- package/agent/locales/en.yaml +20 -0
- package/agent/locales/es.yaml +23 -0
- package/agent/locales/fr.yaml +23 -0
- package/agent/locales/ga.yaml +23 -0
- package/agent/locales/hu.yaml +23 -0
- package/agent/locales/it.yaml +23 -0
- package/agent/locales/ja.yaml +23 -0
- package/agent/locales/ko.yaml +23 -0
- package/agent/locales/pt.yaml +23 -0
- package/agent/locales/ru.yaml +23 -0
- package/agent/locales/tr.yaml +23 -0
- package/agent/locales/uk.yaml +23 -0
- package/agent/locales/zh-hant.yaml +23 -0
- package/agent/locales/zh.yaml +23 -0
- package/agent/model_tools.py +204 -40
- package/agent/optional-mcps/clawpump/manifest.yaml +15 -5
- package/agent/optional-mcps/clawpump-stdio/manifest.yaml +14 -4
- 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 +53 -5
- 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 +308 -696
- 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
|
@@ -4,8 +4,12 @@ import { useEffect, useMemo, useRef, useState } from 'react'
|
|
|
4
4
|
|
|
5
5
|
import { ModelPickerDialog } from '@/components/model-picker'
|
|
6
6
|
import { Button } from '@/components/ui/button'
|
|
7
|
+
import { Codicon } from '@/components/ui/codicon'
|
|
8
|
+
import { ErrorIcon } from '@/components/ui/error-state'
|
|
7
9
|
import { Input } from '@/components/ui/input'
|
|
10
|
+
import { Loader } from '@/components/ui/loader'
|
|
8
11
|
import { getGlobalModelOptions } from '@/hermes'
|
|
12
|
+
import { useI18n } from '@/i18n'
|
|
9
13
|
import {
|
|
10
14
|
Check,
|
|
11
15
|
ChevronDown,
|
|
@@ -14,7 +18,6 @@ import {
|
|
|
14
18
|
ExternalLink,
|
|
15
19
|
KeyRound,
|
|
16
20
|
Loader2,
|
|
17
|
-
Sparkles,
|
|
18
21
|
Terminal
|
|
19
22
|
} from '@/lib/icons'
|
|
20
23
|
import { isProviderSetupErrorMessage } from '@/lib/provider-setup-errors'
|
|
@@ -23,12 +26,17 @@ import { $desktopBoot, type DesktopBootState } from '@/store/boot'
|
|
|
23
26
|
import {
|
|
24
27
|
$desktopOnboarding,
|
|
25
28
|
cancelOnboardingFlow,
|
|
29
|
+
clearPendingProviderOAuth,
|
|
26
30
|
closeManualOnboarding,
|
|
27
31
|
confirmOnboardingModel,
|
|
28
32
|
copyDeviceCode,
|
|
29
33
|
copyExternalCommand,
|
|
34
|
+
DEFAULT_MANUAL_ONBOARDING_REASON,
|
|
35
|
+
DEFAULT_ONBOARDING_REASON,
|
|
36
|
+
dismissFirstRunOnboarding,
|
|
30
37
|
type OnboardingContext,
|
|
31
38
|
type OnboardingFlow,
|
|
39
|
+
peekPendingProviderOAuth,
|
|
32
40
|
recheckExternalSignin,
|
|
33
41
|
refreshOnboarding,
|
|
34
42
|
saveOnboardingApiKey,
|
|
@@ -38,7 +46,7 @@ import {
|
|
|
38
46
|
startProviderOAuth,
|
|
39
47
|
submitOnboardingCode
|
|
40
48
|
} from '@/store/onboarding'
|
|
41
|
-
import type { OAuthProvider } from '@/types/hermes'
|
|
49
|
+
import type { ModelOptionProvider, OAuthProvider } from '@/types/hermes'
|
|
42
50
|
|
|
43
51
|
interface DesktopOnboardingOverlayProps {
|
|
44
52
|
enabled: boolean
|
|
@@ -46,8 +54,8 @@ interface DesktopOnboardingOverlayProps {
|
|
|
46
54
|
requestGateway: OnboardingContext['requestGateway']
|
|
47
55
|
}
|
|
48
56
|
|
|
49
|
-
interface ApiKeyOption {
|
|
50
|
-
description
|
|
57
|
+
export interface ApiKeyOption {
|
|
58
|
+
description?: string
|
|
51
59
|
docsUrl: string
|
|
52
60
|
envKey: string
|
|
53
61
|
id: string
|
|
@@ -56,76 +64,135 @@ interface ApiKeyOption {
|
|
|
56
64
|
short?: string
|
|
57
65
|
}
|
|
58
66
|
|
|
59
|
-
const MIN_KEY_LENGTH = 8
|
|
60
|
-
|
|
61
67
|
const API_KEY_OPTIONS: ApiKeyOption[] = [
|
|
62
68
|
{
|
|
63
69
|
id: 'openrouter',
|
|
64
70
|
name: 'OpenRouter',
|
|
65
|
-
short: 'one key, many models',
|
|
66
71
|
envKey: 'OPENROUTER_API_KEY',
|
|
67
|
-
description: 'Hosts hundreds of models behind a single key. Good default for new installs.',
|
|
68
72
|
docsUrl: 'https://openrouter.ai/keys'
|
|
69
73
|
},
|
|
70
74
|
{
|
|
71
75
|
id: 'openai',
|
|
72
76
|
name: 'OpenAI',
|
|
73
|
-
short: 'GPT-class models',
|
|
74
77
|
envKey: 'OPENAI_API_KEY',
|
|
75
|
-
description: 'Direct access to OpenAI models.',
|
|
76
78
|
docsUrl: 'https://platform.openai.com/api-keys'
|
|
77
79
|
},
|
|
78
80
|
{
|
|
79
81
|
id: 'gemini',
|
|
80
82
|
name: 'Google Gemini',
|
|
81
|
-
short: 'Gemini models',
|
|
82
83
|
envKey: 'GEMINI_API_KEY',
|
|
83
|
-
description: 'Direct access to Google Gemini models.',
|
|
84
84
|
docsUrl: 'https://aistudio.google.com/app/apikey'
|
|
85
85
|
},
|
|
86
86
|
{
|
|
87
87
|
id: 'xai',
|
|
88
88
|
name: 'xAI Grok',
|
|
89
|
-
short: 'Grok models',
|
|
90
89
|
envKey: 'XAI_API_KEY',
|
|
91
|
-
description: 'Direct access to xAI Grok models.',
|
|
92
90
|
docsUrl: 'https://console.x.ai/'
|
|
93
91
|
},
|
|
94
92
|
{
|
|
95
93
|
id: 'local',
|
|
96
94
|
name: 'Local / custom endpoint',
|
|
97
|
-
short: 'self-hosted',
|
|
98
95
|
envKey: 'OPENAI_BASE_URL',
|
|
99
|
-
description: 'Point Hermes at a local or self-hosted OpenAI-compatible endpoint (vLLM, llama.cpp, Ollama, etc).',
|
|
100
96
|
docsUrl: 'https://github.com/NousResearch/hermes-agent#bring-your-own-endpoint',
|
|
101
97
|
placeholder: 'http://127.0.0.1:8000/v1'
|
|
102
98
|
}
|
|
103
99
|
]
|
|
104
100
|
|
|
101
|
+
// Build the FULL API-key provider catalog from the backend model options so the
|
|
102
|
+
// onboarding / Providers key form lists every `api_key` provider `hermes model`
|
|
103
|
+
// knows about — not just the hand-curated five. Curated entries keep their
|
|
104
|
+
// richer copy + placeholders and float to the top (recommended defaults); every
|
|
105
|
+
// other api_key provider is appended with a generic "paste {KEY}" affordance.
|
|
106
|
+
// OAuth / external providers are intentionally excluded here — they go through
|
|
107
|
+
// the OAuth picker / sign-in flow, not a pasted key.
|
|
108
|
+
function useApiKeyCatalog(): ApiKeyOption[] {
|
|
109
|
+
const [rows, setRows] = useState<ModelOptionProvider[]>([])
|
|
110
|
+
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
let cancelled = false
|
|
113
|
+
|
|
114
|
+
// Best-effort — on failure the curated defaults still render. Wrapped in
|
|
115
|
+
// Promise.resolve().then so a synchronous throw (e.g. no desktop bridge in
|
|
116
|
+
// tests) is funneled into the same .catch instead of escaping.
|
|
117
|
+
void Promise.resolve()
|
|
118
|
+
.then(() => getGlobalModelOptions())
|
|
119
|
+
.then(res => {
|
|
120
|
+
if (!cancelled) {
|
|
121
|
+
setRows(res.providers ?? [])
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
.catch(() => {
|
|
125
|
+
// Ignore — fall back to the curated API_KEY_OPTIONS only.
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
return () => {
|
|
129
|
+
cancelled = true
|
|
130
|
+
}
|
|
131
|
+
}, [])
|
|
132
|
+
|
|
133
|
+
return useMemo(() => {
|
|
134
|
+
const curatedByEnv = new Map(API_KEY_OPTIONS.map(o => [o.envKey, o]))
|
|
135
|
+
const derived: ApiKeyOption[] = []
|
|
136
|
+
const seenEnv = new Set<string>(API_KEY_OPTIONS.map(o => o.envKey))
|
|
137
|
+
|
|
138
|
+
for (const row of rows) {
|
|
139
|
+
// Only api_key providers can be activated with a pasted key. Skip OAuth /
|
|
140
|
+
// external / managed flows and anything missing an env var to write to.
|
|
141
|
+
if (row.auth_type && row.auth_type !== 'api_key') {
|
|
142
|
+
continue
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const envKey = row.key_env
|
|
146
|
+
|
|
147
|
+
if (!envKey || seenEnv.has(envKey)) {
|
|
148
|
+
continue
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
seenEnv.add(envKey)
|
|
152
|
+
derived.push({
|
|
153
|
+
id: row.slug,
|
|
154
|
+
name: row.name,
|
|
155
|
+
envKey,
|
|
156
|
+
description: `Direct API access to ${row.name}.`,
|
|
157
|
+
docsUrl: ''
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Curated first (recommended order), then the rest alphabetically so the
|
|
162
|
+
// long tail is scannable.
|
|
163
|
+
derived.sort((a, b) => a.name.localeCompare(b.name))
|
|
164
|
+
|
|
165
|
+
return [...API_KEY_OPTIONS.filter(o => curatedByEnv.has(o.envKey)), ...derived]
|
|
166
|
+
}, [rows])
|
|
167
|
+
}
|
|
168
|
+
|
|
105
169
|
const PROVIDER_DISPLAY: Record<string, { order: number; title: string }> = {
|
|
106
170
|
nous: { order: 0, title: 'Nous Portal' },
|
|
107
|
-
|
|
108
|
-
'
|
|
109
|
-
'
|
|
110
|
-
'
|
|
111
|
-
|
|
171
|
+
'openai-codex': { order: 1, title: 'OpenAI OAuth (ChatGPT)' },
|
|
172
|
+
'minimax-oauth': { order: 2, title: 'MiniMax' },
|
|
173
|
+
'qwen-oauth': { order: 3, title: 'Qwen Code' },
|
|
174
|
+
'xai-oauth': { order: 4, title: 'xAI Grok' },
|
|
175
|
+
// Both Anthropic entries sit at the bottom: the API-key path first, then
|
|
176
|
+
// the subscription OAuth path (only works with extra usage credits).
|
|
177
|
+
anthropic: { order: 5, title: 'Anthropic API Key' },
|
|
178
|
+
'claude-code': { order: 6, title: 'Anthropic OAuth: Required Extra Usage Credits to Use Subscription' }
|
|
112
179
|
}
|
|
113
180
|
|
|
114
181
|
const assetPath = (path: string) => `${import.meta.env.BASE_URL}${path.replace(/^\/+/, '')}`
|
|
115
182
|
|
|
116
|
-
const
|
|
117
|
-
pkce: 'Opens your browser to sign in, then continues here',
|
|
118
|
-
device_code: 'Opens a verification page in your browser — Hermes connects automatically',
|
|
119
|
-
external: 'Sign in once in your terminal, then come back to chat'
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const providerTitle = (p: OAuthProvider) => PROVIDER_DISPLAY[p.id]?.title ?? p.name
|
|
183
|
+
export const providerTitle = (p: OAuthProvider) => PROVIDER_DISPLAY[p.id]?.title ?? p.name
|
|
123
184
|
const orderOf = (p: OAuthProvider) => PROVIDER_DISPLAY[p.id]?.order ?? 99
|
|
124
185
|
|
|
125
|
-
const sortProviders = (providers: OAuthProvider[]) =>
|
|
186
|
+
export const sortProviders = (providers: OAuthProvider[]) =>
|
|
126
187
|
[...providers].sort((a, b) => orderOf(a) - orderOf(b) || a.name.localeCompare(b.name))
|
|
127
188
|
|
|
189
|
+
// Exit choreography, mirroring the gateway "connecting" overlay's timing:
|
|
190
|
+
// text-out (360ms: CONNECTED fades down, rest scrambles+fades) → hold (300ms)
|
|
191
|
+
// → surface-out (520ms, held back by [transition-delay:660ms]). Finalize after.
|
|
192
|
+
const ONBOARDING_EXIT_MS = 1180
|
|
193
|
+
|
|
128
194
|
export function DesktopOnboardingOverlay({ enabled, onCompleted, requestGateway }: DesktopOnboardingOverlayProps) {
|
|
195
|
+
const { t } = useI18n()
|
|
129
196
|
const onboarding = useStore($desktopOnboarding)
|
|
130
197
|
const boot = useStore($desktopBoot)
|
|
131
198
|
const ctxRef = useRef<OnboardingContext>({ requestGateway, onCompleted })
|
|
@@ -139,12 +206,65 @@ export function DesktopOnboardingOverlay({ enabled, onCompleted, requestGateway
|
|
|
139
206
|
[]
|
|
140
207
|
)
|
|
141
208
|
|
|
209
|
+
// Cinematic exit on "Begin": dissolve the panel + overlay (revealing the chat
|
|
210
|
+
// behind), THEN finalize so the unmount lands after the fade — mirrors the
|
|
211
|
+
// connecting overlay's exit choreography instead of cutting instantly.
|
|
212
|
+
const [leaving, setLeaving] = useState(false)
|
|
213
|
+
|
|
214
|
+
const finalizeOnboarding = () => {
|
|
215
|
+
if (leaving) {
|
|
216
|
+
return
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const reduce =
|
|
220
|
+
typeof window !== 'undefined' && window.matchMedia?.('(prefers-reduced-motion: reduce)').matches
|
|
221
|
+
|
|
222
|
+
if (reduce) {
|
|
223
|
+
confirmOnboardingModel(ctx)
|
|
224
|
+
|
|
225
|
+
return
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
setLeaving(true)
|
|
229
|
+
window.setTimeout(() => confirmOnboardingModel(ctx), ONBOARDING_EXIT_MS)
|
|
230
|
+
}
|
|
231
|
+
|
|
142
232
|
useEffect(() => {
|
|
143
233
|
if (enabled || onboarding.requested) {
|
|
144
234
|
void refreshOnboarding(ctx)
|
|
145
235
|
}
|
|
146
236
|
}, [ctx, enabled, onboarding.requested])
|
|
147
237
|
|
|
238
|
+
// When the Providers settings page asked to connect a specific provider, the
|
|
239
|
+
// store stashed its id. Once the provider list has loaded and we're back at
|
|
240
|
+
// an idle picker, launch that exact OAuth flow so the user lands directly in
|
|
241
|
+
// sign-in instead of the picker they just came from.
|
|
242
|
+
useEffect(() => {
|
|
243
|
+
if (!onboarding.manual || onboarding.providers === null || onboarding.flow.status !== 'idle') {
|
|
244
|
+
return
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const pendingId = peekPendingProviderOAuth()
|
|
248
|
+
|
|
249
|
+
if (!pendingId) {
|
|
250
|
+
return
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const provider = onboarding.providers.find(p => p.id === pendingId)
|
|
254
|
+
|
|
255
|
+
if (provider) {
|
|
256
|
+
// Only clear once we've committed to launching it, so a failed/empty
|
|
257
|
+
// provider fetch doesn't silently drop the hand-off.
|
|
258
|
+
clearPendingProviderOAuth()
|
|
259
|
+
void startProviderOAuth(provider, ctx)
|
|
260
|
+
} else if (onboarding.providers.length > 0) {
|
|
261
|
+
// The list loaded but the id isn't a real provider — drop the stale
|
|
262
|
+
// hand-off. An empty list means the fetch isn't ready yet, so keep it
|
|
263
|
+
// and let a later refresh retry.
|
|
264
|
+
clearPendingProviderOAuth()
|
|
265
|
+
}
|
|
266
|
+
}, [ctx, onboarding.flow.status, onboarding.manual, onboarding.providers])
|
|
267
|
+
|
|
148
268
|
// Mount from frame 1 so we replace the boot overlay seamlessly. The
|
|
149
269
|
// configured field stays null until the runtime check resolves; only then
|
|
150
270
|
// do we know whether to dismiss (true) or surface the picker (false).
|
|
@@ -154,48 +274,101 @@ export function DesktopOnboardingOverlay({ enabled, onCompleted, requestGateway
|
|
|
154
274
|
return null
|
|
155
275
|
}
|
|
156
276
|
|
|
277
|
+
// The user chose "I'll choose a provider later" on first run. Stay out of the
|
|
278
|
+
// way on every subsequent launch — they re-enter via Settings → Providers
|
|
279
|
+
// (manual mode), which sets manual=true and bypasses this gate.
|
|
280
|
+
if (onboarding.firstRunSkipped && !onboarding.manual) {
|
|
281
|
+
return null
|
|
282
|
+
}
|
|
283
|
+
|
|
157
284
|
const { flow } = onboarding
|
|
285
|
+
// Show the launch reason only when it's a meaningful, caller-supplied prompt —
|
|
286
|
+
// suppress the generic defaults (useless noise) and provider-setup errors
|
|
287
|
+
// (those are surfaced by FlowPanel, not as a banner).
|
|
158
288
|
const rawReason = onboarding.reason?.trim() || null
|
|
159
|
-
|
|
289
|
+
|
|
290
|
+
const reason =
|
|
291
|
+
rawReason &&
|
|
292
|
+
!isProviderSetupErrorMessage(rawReason) &&
|
|
293
|
+
rawReason !== DEFAULT_ONBOARDING_REASON &&
|
|
294
|
+
rawReason !== DEFAULT_MANUAL_ONBOARDING_REASON
|
|
295
|
+
? rawReason
|
|
296
|
+
: null
|
|
297
|
+
|
|
160
298
|
// In manual mode the app is already configured, so the flow is "ready"
|
|
161
299
|
// immediately — no runtime gate needed. Otherwise wait for the readiness
|
|
162
300
|
// check (configured === false) before showing the picker.
|
|
163
301
|
const ready = onboarding.manual || (enabled && onboarding.configured === false)
|
|
164
302
|
const showPicker = flow.status === 'idle' || flow.status === 'success'
|
|
303
|
+
// The final "you're in" screen drops the card chrome and floats centered on
|
|
304
|
+
// the surface — same bare, cinematic treatment as the connecting overlay.
|
|
305
|
+
const bare = ready && !showPicker && flow.status === 'confirming_model'
|
|
165
306
|
|
|
166
307
|
return (
|
|
167
|
-
<div
|
|
168
|
-
|
|
169
|
-
|
|
308
|
+
<div
|
|
309
|
+
className={cn(
|
|
310
|
+
'fixed inset-0 z-1300 flex items-center justify-center bg-(--ui-chat-surface-background) p-6 transition-opacity duration-[520ms] ease-out',
|
|
311
|
+
// On the bare confirm screen, hold the surface (text-out + hold) so the
|
|
312
|
+
// per-element exit plays before it dissolves.
|
|
313
|
+
bare && leaving ? '[transition-delay:660ms]' : '',
|
|
314
|
+
leaving ? 'pointer-events-none opacity-0' : 'opacity-100'
|
|
315
|
+
)}
|
|
316
|
+
>
|
|
317
|
+
<div
|
|
318
|
+
className={cn(
|
|
319
|
+
'relative w-full max-w-[45rem] transition-all duration-500 ease-out',
|
|
320
|
+
bare
|
|
321
|
+
? ''
|
|
322
|
+
: 'overflow-hidden rounded-xl border border-(--stroke-nous) bg-(--ui-chat-bubble-background) shadow-nous',
|
|
323
|
+
// Bare confirm screen orchestrates its own per-element exit; the
|
|
324
|
+
// carded states use the simple lift/blur dissolve.
|
|
325
|
+
leaving && !bare
|
|
326
|
+
? '-translate-y-1 scale-[0.985] opacity-0 blur-[2px]'
|
|
327
|
+
: 'translate-y-0 scale-100 opacity-100 blur-0'
|
|
328
|
+
)}
|
|
329
|
+
>
|
|
330
|
+
{showPicker || !ready ? <Header /> : null}
|
|
331
|
+
{onboarding.manual ? (
|
|
332
|
+
<Button
|
|
333
|
+
aria-label={t.common.close}
|
|
334
|
+
className="absolute right-3 top-3 z-10 text-(--ui-text-tertiary) hover:bg-(--chrome-action-hover) hover:text-foreground"
|
|
335
|
+
onClick={() => closeManualOnboarding()}
|
|
336
|
+
size="icon-sm"
|
|
337
|
+
variant="ghost"
|
|
338
|
+
>
|
|
339
|
+
<Codicon name="close" size="1rem" />
|
|
340
|
+
</Button>
|
|
341
|
+
) : null}
|
|
170
342
|
<div className="grid gap-3 p-5">
|
|
171
|
-
{onboarding.manual ? (
|
|
172
|
-
<div className="flex justify-end">
|
|
173
|
-
<button
|
|
174
|
-
className="text-xs font-medium text-muted-foreground transition hover:text-foreground"
|
|
175
|
-
onClick={() => closeManualOnboarding()}
|
|
176
|
-
type="button"
|
|
177
|
-
>
|
|
178
|
-
Close
|
|
179
|
-
</button>
|
|
180
|
-
</div>
|
|
181
|
-
) : null}
|
|
182
343
|
{reason ? <ReasonNotice reason={reason} /> : null}
|
|
183
|
-
{ready ?
|
|
344
|
+
{ready ? (
|
|
345
|
+
showPicker ? (
|
|
346
|
+
<Picker ctx={ctx} />
|
|
347
|
+
) : (
|
|
348
|
+
<FlowPanel ctx={ctx} flow={flow} leaving={leaving} onBegin={finalizeOnboarding} />
|
|
349
|
+
)
|
|
350
|
+
) : (
|
|
351
|
+
<Preparing boot={boot} />
|
|
352
|
+
)}
|
|
184
353
|
</div>
|
|
185
354
|
</div>
|
|
186
355
|
</div>
|
|
187
356
|
)
|
|
188
357
|
}
|
|
189
358
|
|
|
359
|
+
// The launch reason is a prompt ("why am I seeing this"), not an error. Only
|
|
360
|
+
// rendered for meaningful caller-supplied reasons (defaults are filtered out
|
|
361
|
+
// upstream), so it never shows the generic "no provider configured" noise.
|
|
190
362
|
function ReasonNotice({ reason }: { reason: string }) {
|
|
191
363
|
return (
|
|
192
|
-
<div className="rounded-2xl border border-
|
|
364
|
+
<div className="rounded-2xl border border-(--ui-stroke-tertiary) bg-(--ui-bg-tertiary)/40 px-4 py-3 text-sm text-muted-foreground">
|
|
193
365
|
{reason}
|
|
194
366
|
</div>
|
|
195
367
|
)
|
|
196
368
|
}
|
|
197
369
|
|
|
198
370
|
function Preparing({ boot }: { boot: DesktopBootState }) {
|
|
371
|
+
const { t } = useI18n()
|
|
199
372
|
const progress = Math.max(2, Math.min(100, Math.round(boot.progress)))
|
|
200
373
|
const hasError = Boolean(boot.error)
|
|
201
374
|
const installing = boot.phase.startsWith('runtime.')
|
|
@@ -203,9 +376,7 @@ function Preparing({ boot }: { boot: DesktopBootState }) {
|
|
|
203
376
|
return (
|
|
204
377
|
<div className="grid gap-3" role="status">
|
|
205
378
|
<p className="text-sm text-muted-foreground">
|
|
206
|
-
{installing
|
|
207
|
-
? 'Hermes is finishing install. This usually takes under a minute on first run.'
|
|
208
|
-
: 'Starting Hermes…'}
|
|
379
|
+
{installing ? t.onboarding.preparingInstall : t.onboarding.starting}
|
|
209
380
|
</p>
|
|
210
381
|
<div className="h-2 overflow-hidden rounded-full bg-muted">
|
|
211
382
|
<div
|
|
@@ -226,25 +397,17 @@ function Preparing({ boot }: { boot: DesktopBootState }) {
|
|
|
226
397
|
}
|
|
227
398
|
|
|
228
399
|
function Header() {
|
|
400
|
+
const { t } = useI18n()
|
|
401
|
+
|
|
229
402
|
return (
|
|
230
|
-
<div className="
|
|
231
|
-
<
|
|
232
|
-
|
|
233
|
-
<Sparkles className="size-5" />
|
|
234
|
-
</div>
|
|
235
|
-
<div>
|
|
236
|
-
<h2 className="text-[0.9375rem] font-semibold tracking-tight">Let's get you setup with Hermes Agent</h2>
|
|
237
|
-
<p className="mt-1 max-w-xl text-[0.8125rem] leading-5 text-(--ui-text-tertiary)">
|
|
238
|
-
Connect a model provider to start chatting. Most options take one click.
|
|
239
|
-
</p>
|
|
240
|
-
</div>
|
|
241
|
-
</div>
|
|
403
|
+
<div className="bg-(--ui-chat-bubble-background) px-5 pt-5 pb-1">
|
|
404
|
+
<h2 className="text-[0.9375rem] font-semibold tracking-tight">{t.onboarding.headerTitle}</h2>
|
|
405
|
+
<p className="mt-1 max-w-xl text-[0.8125rem] leading-5 text-(--ui-text-tertiary)">{t.onboarding.headerDesc}</p>
|
|
242
406
|
</div>
|
|
243
407
|
)
|
|
244
408
|
}
|
|
245
409
|
|
|
246
|
-
const FEATURED_ID = 'nous'
|
|
247
|
-
const FEATURED_PITCH = 'One subscription, 300+ frontier models — the recommended way to run Hermes'
|
|
410
|
+
export const FEATURED_ID = 'nous'
|
|
248
411
|
const SHOW_ALL_KEY = 'hermes-onboarding-show-all-v1'
|
|
249
412
|
|
|
250
413
|
const readShowAll = () => {
|
|
@@ -266,17 +429,38 @@ const persistShowAll = (value: boolean) => {
|
|
|
266
429
|
}
|
|
267
430
|
|
|
268
431
|
export function Picker({ ctx }: { ctx: OnboardingContext }) {
|
|
269
|
-
const {
|
|
432
|
+
const { t } = useI18n()
|
|
433
|
+
const { localEndpoint, manual, mode, providers } = useStore($desktopOnboarding)
|
|
270
434
|
const [showAll, setShowAll] = useState(readShowAll)
|
|
271
435
|
const ordered = useMemo(() => (providers ? sortProviders(providers) : []), [providers])
|
|
272
436
|
const hasOauth = ordered.length > 0
|
|
437
|
+
const apiKeyOptions = useApiKeyCatalog()
|
|
273
438
|
|
|
274
|
-
|
|
275
|
-
|
|
439
|
+
// localEndpoint forces the key form regardless of `mode` (which a manual
|
|
440
|
+
// provider refresh may flip back to 'oauth'); it preselects the local option
|
|
441
|
+
// and hides the "back to sign in" link since the user came specifically to
|
|
442
|
+
// configure a custom endpoint.
|
|
443
|
+
if (localEndpoint || mode === 'apikey' || !hasOauth) {
|
|
444
|
+
return (
|
|
445
|
+
<div className="grid gap-3">
|
|
446
|
+
<ApiKeyForm
|
|
447
|
+
canGoBack={hasOauth && !localEndpoint}
|
|
448
|
+
initialEnvKey={localEndpoint ? 'OPENAI_BASE_URL' : undefined}
|
|
449
|
+
onBack={() => setOnboardingMode('oauth')}
|
|
450
|
+
onSave={(envKey, value, name, apiKey) => saveOnboardingApiKey(envKey, value, name, ctx, apiKey)}
|
|
451
|
+
options={apiKeyOptions}
|
|
452
|
+
/>
|
|
453
|
+
{manual ? null : (
|
|
454
|
+
<div className="flex justify-center border-t border-(--ui-stroke-tertiary) pt-3">
|
|
455
|
+
<ChooseLaterLink />
|
|
456
|
+
</div>
|
|
457
|
+
)}
|
|
458
|
+
</div>
|
|
459
|
+
)
|
|
276
460
|
}
|
|
277
461
|
|
|
278
462
|
if (providers === null) {
|
|
279
|
-
return <Status>
|
|
463
|
+
return <Status>{t.onboarding.lookingUpProviders}</Status>
|
|
280
464
|
}
|
|
281
465
|
|
|
282
466
|
const select = (p: OAuthProvider) => void startProviderOAuth(p, ctx)
|
|
@@ -289,134 +473,241 @@ export function Picker({ ctx }: { ctx: OnboardingContext }) {
|
|
|
289
473
|
|
|
290
474
|
return (
|
|
291
475
|
<div className="grid gap-2">
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
476
|
+
<div className="grid max-h-[60dvh] gap-2 overflow-y-auto p-1">
|
|
477
|
+
{featured ? <FeaturedProviderRow onSelect={select} provider={featured} /> : null}
|
|
478
|
+
{showRest ? (
|
|
479
|
+
<>
|
|
480
|
+
{rest.map(p => (
|
|
481
|
+
<ProviderRow key={p.id} onSelect={select} provider={p} />
|
|
482
|
+
))}
|
|
483
|
+
<KeyProviderRow onClick={() => setOnboardingMode('apikey')} />
|
|
484
|
+
</>
|
|
485
|
+
) : null}
|
|
486
|
+
</div>
|
|
301
487
|
{collapsible ? (
|
|
302
|
-
<
|
|
303
|
-
className="
|
|
488
|
+
<Button
|
|
489
|
+
className="mt-1 self-center font-medium"
|
|
304
490
|
onClick={() => setShowAll(persistShowAll(!showAll))}
|
|
491
|
+
size="xs"
|
|
305
492
|
type="button"
|
|
493
|
+
variant="text"
|
|
306
494
|
>
|
|
307
|
-
{showAll ?
|
|
495
|
+
{showAll ? t.onboarding.collapse : t.onboarding.otherProviders}
|
|
308
496
|
<ChevronDown className={cn('size-3.5 transition', showAll && 'rotate-180')} />
|
|
309
|
-
</
|
|
497
|
+
</Button>
|
|
310
498
|
) : null}
|
|
311
|
-
<div className="flex justify-
|
|
312
|
-
|
|
313
|
-
|
|
499
|
+
<div className="flex items-center justify-between gap-3 pt-1">
|
|
500
|
+
{/* First run only: let the user defer the choice and land in the app.
|
|
501
|
+
In manual mode the overlay already has a close affordance, so the
|
|
502
|
+
"choose later" escape would be redundant — hide it. */}
|
|
503
|
+
{manual ? <span /> : <ChooseLaterLink />}
|
|
504
|
+
<Button
|
|
505
|
+
className="-mr-2 font-medium"
|
|
314
506
|
onClick={() => setOnboardingMode('apikey')}
|
|
507
|
+
size="xs"
|
|
315
508
|
type="button"
|
|
509
|
+
variant="text"
|
|
316
510
|
>
|
|
317
|
-
|
|
318
|
-
</
|
|
511
|
+
{t.onboarding.haveApiKey}
|
|
512
|
+
</Button>
|
|
319
513
|
</div>
|
|
320
514
|
</div>
|
|
321
515
|
)
|
|
322
516
|
}
|
|
323
517
|
|
|
324
|
-
|
|
518
|
+
// "I'll choose a provider later" — dismisses the first-run picker and persists
|
|
519
|
+
// the skip so it never re-nags. The user connects a provider any time from
|
|
520
|
+
// Settings → Providers. Rendered only on the unconfigured first-run flow.
|
|
521
|
+
function ChooseLaterLink() {
|
|
522
|
+
const { t } = useI18n()
|
|
523
|
+
|
|
524
|
+
return (
|
|
525
|
+
<Button
|
|
526
|
+
className="font-medium"
|
|
527
|
+
onClick={() => dismissFirstRunOnboarding()}
|
|
528
|
+
size="xs"
|
|
529
|
+
type="button"
|
|
530
|
+
variant="text"
|
|
531
|
+
>
|
|
532
|
+
{t.onboarding.chooseLater}
|
|
533
|
+
</Button>
|
|
534
|
+
)
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
export function FeaturedProviderRow({
|
|
325
538
|
onSelect,
|
|
326
539
|
provider
|
|
327
540
|
}: {
|
|
328
541
|
onSelect: (provider: OAuthProvider) => void
|
|
329
542
|
provider: OAuthProvider
|
|
330
543
|
}) {
|
|
544
|
+
const { t } = useI18n()
|
|
331
545
|
const loggedIn = provider.status?.logged_in
|
|
332
546
|
|
|
333
547
|
return (
|
|
334
548
|
<button
|
|
335
|
-
className=
|
|
336
|
-
'group flex w-full items-center justify-between gap-4 rounded-2xl border-2 border-primary/50 bg-primary/5 p-4 text-left transition hover:border-primary hover:bg-primary/10',
|
|
337
|
-
loggedIn && 'border-primary'
|
|
338
|
-
)}
|
|
549
|
+
className="group relative flex w-full items-center justify-between gap-4 rounded-[8px] bg-primary/[0.06] px-3 py-2.5 text-left transition-colors hover:bg-primary/10"
|
|
339
550
|
onClick={() => onSelect(provider)}
|
|
340
551
|
type="button"
|
|
341
552
|
>
|
|
553
|
+
<span aria-hidden className="arc-border arc-reverse arc-nous" />
|
|
342
554
|
<div className="min-w-0">
|
|
343
555
|
<div className="flex items-center gap-2">
|
|
344
556
|
<img alt="" className="size-5 shrink-0 rounded" src={assetPath('apple-touch-icon.png')} />
|
|
345
|
-
<span className="text-
|
|
557
|
+
<span className="text-[length:var(--conversation-text-font-size)] font-semibold">
|
|
558
|
+
{providerTitle(provider)}
|
|
559
|
+
</span>
|
|
346
560
|
{loggedIn ? (
|
|
347
561
|
<ConnectedTag />
|
|
348
562
|
) : (
|
|
349
563
|
<span className="inline-flex items-center gap-1.5 bg-primary px-2 py-0.5 text-[0.64rem] font-semibold uppercase tracking-[0.16em] text-primary-foreground">
|
|
350
564
|
<span aria-hidden="true" className="dither inline-block size-2 shrink-0" />
|
|
351
|
-
|
|
565
|
+
{t.onboarding.recommended}
|
|
352
566
|
</span>
|
|
353
567
|
)}
|
|
354
568
|
</div>
|
|
355
|
-
<p className="mt-1 text-xs leading-5 text-muted-foreground">{
|
|
569
|
+
<p className="mt-1 text-xs leading-5 text-muted-foreground">{t.onboarding.featuredPitch}</p>
|
|
356
570
|
</div>
|
|
357
|
-
<ChevronRight className="size-
|
|
571
|
+
<ChevronRight className="size-4 shrink-0 text-primary transition group-hover:translate-x-0.5" />
|
|
358
572
|
</button>
|
|
359
573
|
)
|
|
360
574
|
}
|
|
361
575
|
|
|
362
576
|
function ConnectedTag() {
|
|
577
|
+
const { t } = useI18n()
|
|
578
|
+
|
|
363
579
|
return (
|
|
364
580
|
<span className="inline-flex items-center gap-1 bg-primary/10 px-2 py-0.5 text-xs font-medium text-primary">
|
|
365
581
|
<Check className="size-3" />
|
|
366
|
-
|
|
582
|
+
{t.onboarding.connected}
|
|
367
583
|
</span>
|
|
368
584
|
)
|
|
369
585
|
}
|
|
370
586
|
|
|
371
|
-
|
|
587
|
+
const PROVIDER_ROW_CLASS =
|
|
588
|
+
'group flex w-full items-center justify-between gap-3 rounded-[6px] px-3 py-2.5 text-left transition-colors hover:bg-(--ui-control-hover-background)'
|
|
589
|
+
|
|
590
|
+
export function KeyProviderRow({ onClick }: { onClick: () => void }) {
|
|
591
|
+
const { t } = useI18n()
|
|
592
|
+
|
|
372
593
|
return (
|
|
373
|
-
<button
|
|
374
|
-
className="group flex w-full items-center justify-between gap-3 rounded-2xl border border-border bg-background/60 p-3 text-left transition hover:border-primary/40 hover:bg-accent/40"
|
|
375
|
-
onClick={onClick}
|
|
376
|
-
type="button"
|
|
377
|
-
>
|
|
594
|
+
<button className={PROVIDER_ROW_CLASS} onClick={onClick} type="button">
|
|
378
595
|
<div className="min-w-0">
|
|
379
|
-
<span className="text-
|
|
380
|
-
<p className="mt-1 text-xs leading-5 text-muted-foreground">
|
|
596
|
+
<span className="text-[length:var(--conversation-text-font-size)] font-semibold">OpenRouter</span>
|
|
597
|
+
<p className="mt-1 text-xs leading-5 text-muted-foreground">{t.onboarding.openRouterPitch}</p>
|
|
381
598
|
</div>
|
|
382
599
|
<ChevronRight className="size-4 text-muted-foreground transition group-hover:text-foreground" />
|
|
383
600
|
</button>
|
|
384
601
|
)
|
|
385
602
|
}
|
|
386
603
|
|
|
387
|
-
function ProviderRow({
|
|
604
|
+
export function ProviderRow({
|
|
605
|
+
onSelect,
|
|
606
|
+
provider
|
|
607
|
+
}: {
|
|
608
|
+
onSelect: (provider: OAuthProvider) => void
|
|
609
|
+
provider: OAuthProvider
|
|
610
|
+
}) {
|
|
611
|
+
const { t } = useI18n()
|
|
388
612
|
const loggedIn = provider.status?.logged_in
|
|
389
613
|
const Trail = provider.flow === 'external' ? Terminal : ChevronRight
|
|
390
614
|
|
|
391
615
|
return (
|
|
392
|
-
<button
|
|
393
|
-
className={cn(
|
|
394
|
-
'group flex w-full items-center justify-between gap-3 rounded-2xl border border-border bg-background/60 p-3 text-left transition hover:border-primary/40 hover:bg-accent/40',
|
|
395
|
-
loggedIn && 'border-primary/30'
|
|
396
|
-
)}
|
|
397
|
-
onClick={() => onSelect(provider)}
|
|
398
|
-
type="button"
|
|
399
|
-
>
|
|
616
|
+
<button className={PROVIDER_ROW_CLASS} onClick={() => onSelect(provider)} type="button">
|
|
400
617
|
<div className="min-w-0">
|
|
401
618
|
<div className="flex items-center gap-2">
|
|
402
|
-
<span className="text-
|
|
619
|
+
<span className="text-[length:var(--conversation-text-font-size)] font-semibold">
|
|
620
|
+
{providerTitle(provider)}
|
|
621
|
+
</span>
|
|
403
622
|
{loggedIn ? <ConnectedTag /> : null}
|
|
404
623
|
</div>
|
|
405
|
-
<p className="mt-1 text-xs leading-5 text-muted-foreground">{
|
|
624
|
+
<p className="mt-1 text-xs leading-5 text-muted-foreground">{t.onboarding.flowSubtitles[provider.flow]}</p>
|
|
406
625
|
</div>
|
|
407
626
|
<Trail className="size-4 text-muted-foreground transition group-hover:text-foreground" />
|
|
408
627
|
</button>
|
|
409
628
|
)
|
|
410
629
|
}
|
|
411
630
|
|
|
412
|
-
|
|
413
|
-
|
|
631
|
+
// Presentational two-column key picker. Onboarding feeds it its curated
|
|
632
|
+
// options + a ctx-bound save; the Providers settings page feeds it the full
|
|
633
|
+
// provider catalog + a setEnvVar-backed save (plus `isSet`/`onClear` so it can
|
|
634
|
+
// double as a manage surface). Keep it free of store/ctx coupling so both
|
|
635
|
+
// surfaces render the identical form.
|
|
636
|
+
export function ApiKeyForm({
|
|
637
|
+
canGoBack,
|
|
638
|
+
initialEnvKey,
|
|
639
|
+
isSet,
|
|
640
|
+
onBack,
|
|
641
|
+
onClear,
|
|
642
|
+
onSave,
|
|
643
|
+
options = API_KEY_OPTIONS,
|
|
644
|
+
redactedValue
|
|
645
|
+
}: {
|
|
646
|
+
canGoBack: boolean
|
|
647
|
+
/** Preselect a specific option by env key (e.g. 'OPENAI_BASE_URL' to land on
|
|
648
|
+
* the local / custom endpoint form). Falls back to the first option. */
|
|
649
|
+
initialEnvKey?: string
|
|
650
|
+
isSet?: (envKey: string) => boolean
|
|
651
|
+
onBack: () => void
|
|
652
|
+
onClear?: (envKey: string) => void
|
|
653
|
+
onSave: (
|
|
654
|
+
envKey: string,
|
|
655
|
+
value: string,
|
|
656
|
+
name: string,
|
|
657
|
+
apiKey?: string
|
|
658
|
+
) => Promise<{ message?: string; ok: boolean }>
|
|
659
|
+
options?: ApiKeyOption[]
|
|
660
|
+
redactedValue?: (envKey: string) => null | string | undefined
|
|
661
|
+
}) {
|
|
662
|
+
const { t } = useI18n()
|
|
663
|
+
|
|
664
|
+
const [option, setOption] = useState<ApiKeyOption>(
|
|
665
|
+
() => options.find(o => o.envKey === initialEnvKey) ?? options[0]
|
|
666
|
+
)
|
|
667
|
+
|
|
414
668
|
const [value, setValue] = useState('')
|
|
669
|
+
// Optional endpoint API key, only used by the local / custom endpoint option
|
|
670
|
+
// (whose `value` is the base URL). Cleared whenever the option changes.
|
|
671
|
+
const [localKey, setLocalKey] = useState('')
|
|
415
672
|
const [saving, setSaving] = useState(false)
|
|
416
673
|
const [error, setError] = useState<null | string>(null)
|
|
674
|
+
// `options` can change at runtime when callers filter the catalog (e.g. the
|
|
675
|
+
// Providers page wiring its search into this grid). Keep the selection valid
|
|
676
|
+
// by snapping back to the first remaining option when the current one drops.
|
|
677
|
+
useEffect(() => {
|
|
678
|
+
if (options.length > 0 && !options.some(o => o.envKey === option.envKey)) {
|
|
679
|
+
setOption(options[0])
|
|
680
|
+
setValue('')
|
|
681
|
+
setLocalKey('')
|
|
682
|
+
setError(null)
|
|
683
|
+
}
|
|
684
|
+
}, [option.envKey, options])
|
|
685
|
+
// The catalog grid can be tall, leaving the entry field far below the fold.
|
|
686
|
+
// On selection we scroll the field into view and focus it so it's always
|
|
687
|
+
// obvious where to paste next.
|
|
688
|
+
const entryRef = useRef<HTMLDivElement>(null)
|
|
689
|
+
|
|
690
|
+
const pick = (o: ApiKeyOption) => {
|
|
691
|
+
setOption(o)
|
|
692
|
+
setValue('')
|
|
693
|
+
setLocalKey('')
|
|
694
|
+
setError(null)
|
|
695
|
+
requestAnimationFrame(() => {
|
|
696
|
+
entryRef.current?.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
|
697
|
+
entryRef.current?.querySelector('input')?.focus()
|
|
698
|
+
})
|
|
699
|
+
}
|
|
417
700
|
|
|
418
701
|
const isLocal = option.envKey === 'OPENAI_BASE_URL'
|
|
419
|
-
const
|
|
702
|
+
const alreadySet = isSet?.(option.envKey) ?? false
|
|
703
|
+
// When set, surface the backend's redacted value (e.g. "sk-12…wxyz") as the
|
|
704
|
+
// placeholder so users can eyeball that the right key is in place.
|
|
705
|
+
const currentRedacted = alreadySet ? (redactedValue?.(option.envKey) ?? null) : null
|
|
706
|
+
// Only require a non-empty value — no length/format validation, so a short
|
|
707
|
+
// or unusual key can't block the user from continuing.
|
|
708
|
+
const canSave = value.trim().length >= 1
|
|
709
|
+
const optionCopy = t.onboarding.apiKeyOptions[option.id]
|
|
710
|
+
const optionDescription = optionCopy?.description ?? option.description
|
|
420
711
|
|
|
421
712
|
const submit = async () => {
|
|
422
713
|
if (!canSave || saving) {
|
|
@@ -425,12 +716,13 @@ function ApiKeyForm({ canGoBack, ctx }: { canGoBack: boolean; ctx: OnboardingCon
|
|
|
425
716
|
|
|
426
717
|
setSaving(true)
|
|
427
718
|
setError(null)
|
|
428
|
-
const result = await
|
|
719
|
+
const result = await onSave(option.envKey, value, option.name, isLocal ? localKey : undefined)
|
|
429
720
|
|
|
430
721
|
if (result.ok) {
|
|
431
722
|
setValue('')
|
|
723
|
+
setLocalKey('')
|
|
432
724
|
} else {
|
|
433
|
-
setError(result.message ??
|
|
725
|
+
setError(result.message ?? t.onboarding.couldNotSave)
|
|
434
726
|
}
|
|
435
727
|
|
|
436
728
|
setSaving(false)
|
|
@@ -439,44 +731,44 @@ function ApiKeyForm({ canGoBack, ctx }: { canGoBack: boolean; ctx: OnboardingCon
|
|
|
439
731
|
return (
|
|
440
732
|
<div className="grid gap-4">
|
|
441
733
|
{canGoBack ? (
|
|
442
|
-
<
|
|
443
|
-
className="-mt-1
|
|
444
|
-
onClick={
|
|
734
|
+
<Button
|
|
735
|
+
className="-mt-1 self-start font-medium"
|
|
736
|
+
onClick={onBack}
|
|
737
|
+
size="xs"
|
|
445
738
|
type="button"
|
|
739
|
+
variant="text"
|
|
446
740
|
>
|
|
447
741
|
<ChevronLeft className="size-3" />
|
|
448
|
-
|
|
449
|
-
</
|
|
742
|
+
{t.onboarding.backToSignIn}
|
|
743
|
+
</Button>
|
|
450
744
|
) : null}
|
|
451
745
|
|
|
452
|
-
<div className="grid gap-2 sm:grid-cols-2">
|
|
453
|
-
{
|
|
746
|
+
<div className="grid max-h-[42dvh] gap-2 overflow-y-auto p-1 sm:grid-cols-2">
|
|
747
|
+
{options.map(o => (
|
|
454
748
|
<button
|
|
455
749
|
className={cn(
|
|
456
750
|
'rounded-2xl border bg-background/60 p-3 text-left transition hover:bg-accent/50',
|
|
457
|
-
option.
|
|
751
|
+
option.envKey === o.envKey ? 'border-primary ring-2 ring-primary/20' : 'border-transparent'
|
|
458
752
|
)}
|
|
459
|
-
key={o.
|
|
460
|
-
onClick={() =>
|
|
461
|
-
setOption(o)
|
|
462
|
-
setValue('')
|
|
463
|
-
setError(null)
|
|
464
|
-
}}
|
|
753
|
+
key={o.envKey}
|
|
754
|
+
onClick={() => pick(o)}
|
|
465
755
|
type="button"
|
|
466
756
|
>
|
|
467
757
|
<div className="flex items-center justify-between gap-2">
|
|
468
758
|
<span className="text-sm font-medium">{o.name}</span>
|
|
469
|
-
{
|
|
759
|
+
{isSet?.(o.envKey) ? <Check className="size-3.5 text-muted-foreground" /> : null}
|
|
470
760
|
</div>
|
|
471
|
-
{o.short
|
|
761
|
+
{(t.onboarding.apiKeyOptions[o.id]?.short ?? o.short) ? (
|
|
762
|
+
<p className="mt-1 text-xs text-muted-foreground">{t.onboarding.apiKeyOptions[o.id]?.short ?? o.short}</p>
|
|
763
|
+
) : null}
|
|
472
764
|
</button>
|
|
473
765
|
))}
|
|
474
766
|
</div>
|
|
475
767
|
|
|
476
|
-
<div className="grid gap-2">
|
|
768
|
+
<div className="grid scroll-mt-4 gap-2" ref={entryRef}>
|
|
477
769
|
<div className="flex items-center justify-between gap-3">
|
|
478
|
-
<p className="text-sm leading-6 text-muted-foreground">{
|
|
479
|
-
{option.docsUrl ? <DocsLink href={option.docsUrl}>
|
|
770
|
+
<p className="text-sm leading-6 text-muted-foreground">{optionDescription}</p>
|
|
771
|
+
{option.docsUrl ? <DocsLink href={option.docsUrl}>{t.onboarding.getKey}</DocsLink> : null}
|
|
480
772
|
</div>
|
|
481
773
|
<Input
|
|
482
774
|
autoComplete="off"
|
|
@@ -484,56 +776,86 @@ function ApiKeyForm({ canGoBack, ctx }: { canGoBack: boolean; ctx: OnboardingCon
|
|
|
484
776
|
className="font-mono"
|
|
485
777
|
onChange={e => setValue(e.target.value)}
|
|
486
778
|
onKeyDown={e => e.key === 'Enter' && void submit()}
|
|
487
|
-
placeholder={
|
|
779
|
+
placeholder={
|
|
780
|
+
currentRedacted ??
|
|
781
|
+
(alreadySet ? t.onboarding.replaceCurrent : option.placeholder || t.onboarding.pasteApiKey)
|
|
782
|
+
}
|
|
488
783
|
type={isLocal ? 'text' : 'password'}
|
|
489
784
|
value={value}
|
|
490
785
|
/>
|
|
786
|
+
{isLocal ? (
|
|
787
|
+
<Input
|
|
788
|
+
autoComplete="off"
|
|
789
|
+
className="font-mono"
|
|
790
|
+
onChange={e => setLocalKey(e.target.value)}
|
|
791
|
+
onKeyDown={e => e.key === 'Enter' && void submit()}
|
|
792
|
+
placeholder={t.onboarding.localApiKeyPlaceholder}
|
|
793
|
+
type="password"
|
|
794
|
+
value={localKey}
|
|
795
|
+
/>
|
|
796
|
+
) : null}
|
|
491
797
|
{error ? <p className="text-xs text-destructive">{error}</p> : null}
|
|
492
798
|
</div>
|
|
493
799
|
|
|
494
|
-
<div className="flex justify-
|
|
800
|
+
<div className="flex items-center justify-between gap-3">
|
|
801
|
+
<div>
|
|
802
|
+
{alreadySet && onClear ? (
|
|
803
|
+
<Button onClick={() => onClear(option.envKey)} size="sm" variant="ghost">
|
|
804
|
+
{t.common.remove}
|
|
805
|
+
</Button>
|
|
806
|
+
) : null}
|
|
807
|
+
</div>
|
|
495
808
|
<Button disabled={!canSave || saving} onClick={() => void submit()}>
|
|
496
|
-
{saving ? <Loader2 className="
|
|
497
|
-
{saving ?
|
|
809
|
+
{saving ? <Loader2 className="animate-spin" /> : <KeyRound />}
|
|
810
|
+
{saving ? t.onboarding.connecting : alreadySet ? t.onboarding.update : t.common.connect}
|
|
498
811
|
</Button>
|
|
499
812
|
</div>
|
|
500
813
|
</div>
|
|
501
814
|
)
|
|
502
815
|
}
|
|
503
816
|
|
|
504
|
-
function FlowPanel({
|
|
817
|
+
function FlowPanel({
|
|
818
|
+
ctx,
|
|
819
|
+
flow,
|
|
820
|
+
leaving,
|
|
821
|
+
onBegin
|
|
822
|
+
}: {
|
|
823
|
+
ctx: OnboardingContext
|
|
824
|
+
flow: OnboardingFlow
|
|
825
|
+
leaving: boolean
|
|
826
|
+
onBegin: () => void
|
|
827
|
+
}) {
|
|
828
|
+
const { t } = useI18n()
|
|
505
829
|
const title = 'provider' in flow && flow.provider ? providerTitle(flow.provider) : ''
|
|
506
830
|
|
|
507
831
|
if (flow.status === 'starting') {
|
|
508
|
-
return <Status>
|
|
832
|
+
return <Status>{t.onboarding.startingSignIn(title)}</Status>
|
|
509
833
|
}
|
|
510
834
|
|
|
511
835
|
if (flow.status === 'submitting') {
|
|
512
|
-
return <Status>
|
|
836
|
+
return <Status>{t.onboarding.verifyingCode(title)}</Status>
|
|
513
837
|
}
|
|
514
838
|
|
|
515
839
|
if (flow.status === 'success') {
|
|
516
840
|
return (
|
|
517
|
-
<
|
|
518
|
-
<Check className="size-4" />
|
|
519
|
-
{title} connected. Picking a default model...
|
|
520
|
-
</div>
|
|
841
|
+
<DecodedLabel text={t.onboarding.connectedPicking(title)} />
|
|
521
842
|
)
|
|
522
843
|
}
|
|
523
844
|
|
|
524
845
|
if (flow.status === 'confirming_model') {
|
|
525
|
-
return <ConfirmingModelPanel
|
|
846
|
+
return <ConfirmingModelPanel flow={flow} leaving={leaving} onBegin={onBegin} />
|
|
526
847
|
}
|
|
527
848
|
|
|
528
849
|
if (flow.status === 'error') {
|
|
529
850
|
return (
|
|
530
851
|
<div className="grid gap-3">
|
|
531
|
-
<div className="
|
|
532
|
-
|
|
852
|
+
<div className="flex items-center gap-1.5 text-sm text-destructive">
|
|
853
|
+
<ErrorIcon className="shrink-0" size="0.875rem" />
|
|
854
|
+
<span>{flow.message || t.onboarding.signInFailed}</span>
|
|
533
855
|
</div>
|
|
534
856
|
<div className="flex justify-end">
|
|
535
857
|
<Button onClick={cancelOnboardingFlow} variant="outline">
|
|
536
|
-
|
|
858
|
+
{t.onboarding.pickDifferentProvider}
|
|
537
859
|
</Button>
|
|
538
860
|
</div>
|
|
539
861
|
</div>
|
|
@@ -542,45 +864,58 @@ function FlowPanel({ ctx, flow }: { ctx: OnboardingContext; flow: OnboardingFlow
|
|
|
542
864
|
|
|
543
865
|
if (flow.status === 'awaiting_user') {
|
|
544
866
|
return (
|
|
545
|
-
<Step title={
|
|
867
|
+
<Step title={t.onboarding.signInWith(title)}>
|
|
546
868
|
<ol className="list-decimal space-y-1 pl-5 text-sm text-muted-foreground">
|
|
547
|
-
<li>
|
|
548
|
-
<li>
|
|
549
|
-
<li>
|
|
869
|
+
<li>{t.onboarding.openedBrowser(title)}</li>
|
|
870
|
+
<li>{t.onboarding.authorizeThere}</li>
|
|
871
|
+
<li>{t.onboarding.copyAuthCode}</li>
|
|
550
872
|
</ol>
|
|
551
873
|
<Input
|
|
552
874
|
autoFocus
|
|
553
875
|
onChange={e => setOnboardingCode(e.target.value)}
|
|
554
876
|
onKeyDown={e => e.key === 'Enter' && void submitOnboardingCode(ctx)}
|
|
555
|
-
placeholder=
|
|
877
|
+
placeholder={t.onboarding.pasteAuthCode}
|
|
556
878
|
value={flow.code}
|
|
557
879
|
/>
|
|
558
|
-
<FlowFooter left={<DocsLink href={flow.start.auth_url}>
|
|
880
|
+
<FlowFooter left={<DocsLink href={flow.start.auth_url}>{t.onboarding.reopenAuthPage}</DocsLink>}>
|
|
559
881
|
<CancelBtn />
|
|
560
882
|
<Button disabled={!flow.code.trim()} onClick={() => void submitOnboardingCode(ctx)}>
|
|
561
|
-
|
|
883
|
+
{t.common.continue}
|
|
562
884
|
</Button>
|
|
563
885
|
</FlowFooter>
|
|
564
886
|
</Step>
|
|
565
887
|
)
|
|
566
888
|
}
|
|
567
889
|
|
|
890
|
+
if (flow.status === 'awaiting_browser') {
|
|
891
|
+
return (
|
|
892
|
+
<Step title={t.onboarding.signInWith(title)}>
|
|
893
|
+
<p className="text-sm text-muted-foreground">{t.onboarding.autoBrowser(title)}</p>
|
|
894
|
+
<FlowFooter left={<DocsLink href={flow.start.auth_url}>{t.onboarding.reopenSignInPage}</DocsLink>}>
|
|
895
|
+
<span className="flex items-center gap-2 text-xs text-muted-foreground">
|
|
896
|
+
<Loader2 className="size-3 animate-spin" />
|
|
897
|
+
{t.onboarding.waitingAuthorize}
|
|
898
|
+
</span>
|
|
899
|
+
<CancelBtn size="sm" />
|
|
900
|
+
</FlowFooter>
|
|
901
|
+
</Step>
|
|
902
|
+
)
|
|
903
|
+
}
|
|
904
|
+
|
|
568
905
|
if (flow.status === 'external_pending') {
|
|
569
906
|
return (
|
|
570
|
-
<Step title={
|
|
571
|
-
<p className="text-sm text-muted-foreground">
|
|
572
|
-
{title} signs in through its own CLI. Run this command in a terminal, then come back and pick "I've signed
|
|
573
|
-
in":
|
|
574
|
-
</p>
|
|
907
|
+
<Step title={t.onboarding.signInWith(title)}>
|
|
908
|
+
<p className="text-sm text-muted-foreground">{t.onboarding.externalPending(title)}</p>
|
|
575
909
|
<CodeBlock copied={flow.copied} onCopy={() => void copyExternalCommand()} text={flow.provider.cli_command} />
|
|
576
910
|
<FlowFooter
|
|
577
|
-
left={
|
|
911
|
+
left={
|
|
912
|
+
flow.provider.docs_url ? (
|
|
913
|
+
<DocsLink href={flow.provider.docs_url}>{t.onboarding.docs(title)}</DocsLink>
|
|
914
|
+
) : null
|
|
915
|
+
}
|
|
578
916
|
>
|
|
579
917
|
<CancelBtn />
|
|
580
|
-
<Button onClick={() => void recheckExternalSignin(ctx)}>
|
|
581
|
-
<Check className="size-4" />
|
|
582
|
-
I've signed in
|
|
583
|
-
</Button>
|
|
918
|
+
<Button onClick={() => void recheckExternalSignin(ctx)}>{t.onboarding.signedIn}</Button>
|
|
584
919
|
</FlowFooter>
|
|
585
920
|
</Step>
|
|
586
921
|
)
|
|
@@ -591,13 +926,13 @@ function FlowPanel({ ctx, flow }: { ctx: OnboardingContext; flow: OnboardingFlow
|
|
|
591
926
|
}
|
|
592
927
|
|
|
593
928
|
return (
|
|
594
|
-
<Step title={
|
|
595
|
-
<p className="text-sm text-muted-foreground">
|
|
596
|
-
<
|
|
597
|
-
<FlowFooter left={<DocsLink href={flow.start.verification_url}>
|
|
929
|
+
<Step title={t.onboarding.signInWith(title)}>
|
|
930
|
+
<p className="text-sm text-muted-foreground">{t.onboarding.deviceCodeOpened(title)}</p>
|
|
931
|
+
<DeviceCode code={flow.start.user_code} copied={flow.copied} onCopy={() => void copyDeviceCode()} />
|
|
932
|
+
<FlowFooter left={<DocsLink href={flow.start.verification_url}>{t.onboarding.reopenVerification}</DocsLink>}>
|
|
598
933
|
<span className="flex items-center gap-2 text-xs text-muted-foreground">
|
|
599
934
|
<Loader2 className="size-3 animate-spin" />
|
|
600
|
-
|
|
935
|
+
{t.onboarding.waitingAuthorize}
|
|
601
936
|
</span>
|
|
602
937
|
<CancelBtn size="sm" />
|
|
603
938
|
</FlowFooter>
|
|
@@ -614,22 +949,53 @@ function Step({ children, title }: { children: React.ReactNode; title: string })
|
|
|
614
949
|
)
|
|
615
950
|
}
|
|
616
951
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
copied: boolean
|
|
624
|
-
large?: boolean
|
|
625
|
-
onCopy: () => void
|
|
626
|
-
text: string
|
|
627
|
-
}) {
|
|
952
|
+
// Device-code display: OTP-style — each character in its own readonly cell.
|
|
953
|
+
// The whole row is the copy button (no side button, no checkmark); on copy the
|
|
954
|
+
// cells flash emerald for feedback. Dashes render as quiet separators.
|
|
955
|
+
function DeviceCode({ code, copied, onCopy }: { code: string; copied: boolean; onCopy: () => void }) {
|
|
956
|
+
const { t } = useI18n()
|
|
957
|
+
|
|
628
958
|
return (
|
|
629
|
-
<
|
|
630
|
-
|
|
959
|
+
<button
|
|
960
|
+
aria-label={t.onboarding.copy}
|
|
961
|
+
className="group flex w-full items-center justify-center gap-1.5"
|
|
962
|
+
onClick={onCopy}
|
|
963
|
+
type="button"
|
|
964
|
+
>
|
|
965
|
+
{[...code].map((ch, i) =>
|
|
966
|
+
ch === '-' || ch === ' ' ? (
|
|
967
|
+
<span className="w-1.5 text-center text-lg text-muted-foreground" key={i}>
|
|
968
|
+
–
|
|
969
|
+
</span>
|
|
970
|
+
) : (
|
|
971
|
+
<span
|
|
972
|
+
className={cn(
|
|
973
|
+
'flex size-10 items-center justify-center rounded-md border font-mono text-xl font-semibold uppercase transition-colors',
|
|
974
|
+
copied
|
|
975
|
+
? 'border-primary/50 text-primary'
|
|
976
|
+
: 'border-(--stroke-nous) text-foreground group-hover:border-(--ui-stroke-secondary)'
|
|
977
|
+
)}
|
|
978
|
+
key={i}
|
|
979
|
+
>
|
|
980
|
+
{ch}
|
|
981
|
+
</span>
|
|
982
|
+
)
|
|
983
|
+
)}
|
|
984
|
+
</button>
|
|
985
|
+
)
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
function CodeBlock({ copied, onCopy, text }: { copied: boolean; onCopy: () => void; text: string }) {
|
|
989
|
+
const { t } = useI18n()
|
|
990
|
+
|
|
991
|
+
return (
|
|
992
|
+
<div className="flex items-center justify-between gap-3 rounded-md border border-(--stroke-nous) px-3 py-2">
|
|
993
|
+
<code className="min-w-0 flex-1 truncate font-mono text-sm">
|
|
994
|
+
<span className="mr-2 select-none text-muted-foreground">$</span>
|
|
995
|
+
{text}
|
|
996
|
+
</code>
|
|
631
997
|
<Button onClick={onCopy} size="sm" variant="outline">
|
|
632
|
-
{copied ?
|
|
998
|
+
{copied ? t.common.copied : t.onboarding.copy}
|
|
633
999
|
</Button>
|
|
634
1000
|
</div>
|
|
635
1001
|
)
|
|
@@ -645,20 +1011,193 @@ function FlowFooter({ children, left }: { children: React.ReactNode; left?: Reac
|
|
|
645
1011
|
}
|
|
646
1012
|
|
|
647
1013
|
function CancelBtn({ size = 'default' }: { size?: 'default' | 'sm' }) {
|
|
1014
|
+
const { t } = useI18n()
|
|
1015
|
+
|
|
648
1016
|
return (
|
|
649
1017
|
<Button onClick={cancelOnboardingFlow} size={size} variant="ghost">
|
|
650
|
-
|
|
1018
|
+
{t.common.cancel}
|
|
651
1019
|
</Button>
|
|
652
1020
|
)
|
|
653
1021
|
}
|
|
654
1022
|
|
|
1023
|
+
// Borrowed from the gateway "connecting" overlay: a mono, letter-spaced label
|
|
1024
|
+
// that decodes left-to-right from scrambled glyphs into the real text, with a
|
|
1025
|
+
// blinking block cursor. Ties onboarding's success moment to that same motif.
|
|
1026
|
+
// Cuneiform glyphs (array, since each is a surrogate pair) for the scramble.
|
|
1027
|
+
// Hero "X CONNECTED" decode uses the SAME ascii map as the connecting overlay.
|
|
1028
|
+
const ASCII_GLYPHS = [...'/\\|-_=+<>~:*']
|
|
1029
|
+
const pickAscii = () => ASCII_GLYPHS[(Math.random() * ASCII_GLYPHS.length) | 0]
|
|
1030
|
+
// Cuneiform is reserved for the subtle "other text" (model name + BEGIN) easter egg.
|
|
1031
|
+
const SCRAMBLE_GLYPHS = [...'𒀀𒀁𒀂𒀅𒀊𒀖𒀜𒀭𒀲𒀸𒁀𒁉𒁒𒁕𒁹𒂊𒃻𒄆𒄴𒅀𒆍𒇽𒈨𒉡']
|
|
1032
|
+
const GLYPH_SET = new Set(SCRAMBLE_GLYPHS)
|
|
1033
|
+
const pickGlyph = () => SCRAMBLE_GLYPHS[(Math.random() * SCRAMBLE_GLYPHS.length) | 0]
|
|
1034
|
+
// How many trailing characters of each word scramble during decode-in.
|
|
1035
|
+
const DECODE_TAIL = 4
|
|
1036
|
+
|
|
1037
|
+
// Renders text where cuneiform scramble-glyphs are dropped to a smaller em-size
|
|
1038
|
+
// (resolved Latin chars stay full size) — keeps the easter-egg glyphs subtle.
|
|
1039
|
+
function GlyphText({ text }: { text: string }) {
|
|
1040
|
+
return (
|
|
1041
|
+
<>
|
|
1042
|
+
{Array.from(text, (ch, i) =>
|
|
1043
|
+
GLYPH_SET.has(ch) ? (
|
|
1044
|
+
<span className="text-[0.62em]" key={i}>
|
|
1045
|
+
{ch}
|
|
1046
|
+
</span>
|
|
1047
|
+
) : (
|
|
1048
|
+
ch
|
|
1049
|
+
)
|
|
1050
|
+
)}
|
|
1051
|
+
</>
|
|
1052
|
+
)
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
function useDecoded(text: string): string {
|
|
1056
|
+
const [out, setOut] = useState(text)
|
|
1057
|
+
|
|
1058
|
+
useEffect(() => {
|
|
1059
|
+
if (typeof window !== 'undefined' && window.matchMedia?.('(prefers-reduced-motion: reduce)').matches) {
|
|
1060
|
+
setOut(text)
|
|
1061
|
+
|
|
1062
|
+
return
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
// Each WORD keeps its head static and only churns its tail (last few chars),
|
|
1066
|
+
// resolving left-to-right across all tails — same anchor-the-prefix trick the
|
|
1067
|
+
// connecting overlay uses ("CONN" static, "ECTING" churns), applied per word
|
|
1068
|
+
// so both the provider and "CONNECTED" decode and time stays constant.
|
|
1069
|
+
const chars = [...text]
|
|
1070
|
+
const scrambleable = chars.map(() => false)
|
|
1071
|
+
|
|
1072
|
+
for (let i = 0; i < chars.length; ) {
|
|
1073
|
+
if (!/[a-z0-9]/i.test(chars[i])) {
|
|
1074
|
+
i += 1
|
|
1075
|
+
|
|
1076
|
+
continue
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
let j = i
|
|
1080
|
+
|
|
1081
|
+
while (j < chars.length && /[a-z0-9]/i.test(chars[j])) {
|
|
1082
|
+
j += 1
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
for (let k = Math.max(i, j - DECODE_TAIL); k < j; k += 1) {
|
|
1086
|
+
scrambleable[k] = true
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
i = j
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
const tailIndices = chars.map((_, idx) => idx).filter(idx => scrambleable[idx])
|
|
1093
|
+
let resolved = 0
|
|
1094
|
+
|
|
1095
|
+
const id = window.setInterval(() => {
|
|
1096
|
+
resolved += 0.5
|
|
1097
|
+
const settled = new Set(tailIndices.slice(0, Math.floor(resolved)))
|
|
1098
|
+
|
|
1099
|
+
setOut(chars.map((ch, idx) => (scrambleable[idx] && !settled.has(idx) ? pickAscii() : ch)).join(''))
|
|
1100
|
+
|
|
1101
|
+
if (Math.floor(resolved) >= tailIndices.length) {
|
|
1102
|
+
window.clearInterval(id)
|
|
1103
|
+
}
|
|
1104
|
+
}, 45)
|
|
1105
|
+
|
|
1106
|
+
return () => window.clearInterval(id)
|
|
1107
|
+
}, [text])
|
|
1108
|
+
|
|
1109
|
+
return out
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
// Continuously scrambles alphanumeric chars while `active` (used on exit so the
|
|
1113
|
+
// model name / button decay into ascii noise as they fade).
|
|
1114
|
+
function useScramble(text: string, active: boolean): string {
|
|
1115
|
+
const [out, setOut] = useState(text)
|
|
1116
|
+
|
|
1117
|
+
useEffect(() => {
|
|
1118
|
+
if (!active) {
|
|
1119
|
+
setOut(text)
|
|
1120
|
+
|
|
1121
|
+
return
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
const id = window.setInterval(() => {
|
|
1125
|
+
setOut(Array.from(text, ch => (/[a-z0-9]/i.test(ch) ? pickGlyph() : ch)).join(''))
|
|
1126
|
+
}, 45)
|
|
1127
|
+
|
|
1128
|
+
return () => window.clearInterval(id)
|
|
1129
|
+
}, [text, active])
|
|
1130
|
+
|
|
1131
|
+
return out
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
function DecodedLabel({ leaving, text }: { leaving?: boolean; text: string }) {
|
|
1135
|
+
const decoded = useDecoded(text.toUpperCase())
|
|
1136
|
+
|
|
1137
|
+
return (
|
|
1138
|
+
<span
|
|
1139
|
+
className={cn(
|
|
1140
|
+
'inline-flex items-center font-mono text-xs font-semibold uppercase tracking-[0.28em] tabular-nums text-primary transition duration-[360ms] ease-out',
|
|
1141
|
+
leaving ? 'translate-y-2 opacity-0 saturate-0' : 'translate-y-0 opacity-100 saturate-100'
|
|
1142
|
+
)}
|
|
1143
|
+
>
|
|
1144
|
+
<GlyphText text={decoded} />
|
|
1145
|
+
<span
|
|
1146
|
+
aria-hidden="true"
|
|
1147
|
+
className="dither ml-1.5 -mr-[0.875rem] inline-block size-2 shrink-0 -translate-y-px rounded-[1px] text-primary"
|
|
1148
|
+
style={{ animation: 'ob-decode-cursor 1s step-end infinite' }}
|
|
1149
|
+
/>
|
|
1150
|
+
<style>{'@keyframes ob-decode-cursor { 0%, 49% { opacity: 1 } 50%, 100% { opacity: 0 } }'}</style>
|
|
1151
|
+
</span>
|
|
1152
|
+
)
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
// Terminal-flavored CTA to match the connecting overlay's hacker aesthetic:
|
|
1156
|
+
// mono, uppercase, letter-spaced, wrapped in primary brackets that light up on
|
|
1157
|
+
// hover. The whole onboarding "you're in" moment leans into this motif.
|
|
1158
|
+
function HackeryButton({
|
|
1159
|
+
disabled,
|
|
1160
|
+
label,
|
|
1161
|
+
loading,
|
|
1162
|
+
onClick
|
|
1163
|
+
}: {
|
|
1164
|
+
disabled?: boolean
|
|
1165
|
+
label: React.ReactNode
|
|
1166
|
+
loading?: boolean
|
|
1167
|
+
onClick: () => void
|
|
1168
|
+
}) {
|
|
1169
|
+
return (
|
|
1170
|
+
<button
|
|
1171
|
+
className={cn(
|
|
1172
|
+
'group inline-flex items-center gap-2 rounded-md border border-(--stroke-nous) px-6 py-2.5',
|
|
1173
|
+
'font-mono text-xs font-semibold uppercase text-primary',
|
|
1174
|
+
'transition-all duration-150 hover:border-primary/60 hover:bg-primary/[0.06]',
|
|
1175
|
+
'disabled:pointer-events-none disabled:opacity-50'
|
|
1176
|
+
)}
|
|
1177
|
+
disabled={disabled}
|
|
1178
|
+
onClick={onClick}
|
|
1179
|
+
type="button"
|
|
1180
|
+
>
|
|
1181
|
+
<span className="text-primary/40 transition-colors group-hover:text-primary">[</span>
|
|
1182
|
+
{loading ? <Loader2 className="size-3 animate-spin" /> : null}
|
|
1183
|
+
<span className="-mr-[0.25em] pl-[0.25em] tracking-[0.25em]">{label}</span>
|
|
1184
|
+
<span className="text-primary/40 transition-colors group-hover:text-primary">]</span>
|
|
1185
|
+
</button>
|
|
1186
|
+
)
|
|
1187
|
+
}
|
|
1188
|
+
|
|
655
1189
|
function ConfirmingModelPanel({
|
|
656
|
-
|
|
657
|
-
|
|
1190
|
+
flow,
|
|
1191
|
+
leaving,
|
|
1192
|
+
onBegin
|
|
658
1193
|
}: {
|
|
659
|
-
ctx: OnboardingContext
|
|
660
1194
|
flow: Extract<OnboardingFlow, { status: 'confirming_model' }>
|
|
1195
|
+
leaving: boolean
|
|
1196
|
+
onBegin: () => void
|
|
661
1197
|
}) {
|
|
1198
|
+
const { t } = useI18n()
|
|
1199
|
+
const scrambledModel = useScramble(flow.currentModel, leaving)
|
|
1200
|
+
const scrambledBegin = useScramble(t.onboarding.startChatting, leaving)
|
|
662
1201
|
// Local state controls whether the model picker dialog is open.
|
|
663
1202
|
// We reuse the existing ModelPickerDialog component (the same picker
|
|
664
1203
|
// available from the chat shell) rather than building an inline
|
|
@@ -672,53 +1211,70 @@ function ConfirmingModelPanel({
|
|
|
672
1211
|
queryKey: ['onboarding-model-options', flow.providerSlug],
|
|
673
1212
|
queryFn: () => getGlobalModelOptions()
|
|
674
1213
|
})
|
|
1214
|
+
|
|
675
1215
|
const providerRow = options.data?.providers?.find(
|
|
676
1216
|
p => String(p.slug).toLowerCase() === flow.providerSlug.toLowerCase()
|
|
677
1217
|
)
|
|
1218
|
+
|
|
678
1219
|
const price = providerRow?.pricing?.[flow.currentModel]
|
|
679
1220
|
const freeTier = providerRow?.free_tier
|
|
680
1221
|
|
|
681
1222
|
return (
|
|
682
|
-
<div className="grid gap-
|
|
683
|
-
<
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
</
|
|
704
|
-
|
|
705
|
-
{price && (price.input || price.output) && (
|
|
706
|
-
<p className="mt-1 font-mono text-xs text-muted-foreground">
|
|
707
|
-
{price.free ? 'Free' : `${price.input || '?'} in / ${price.output || '?'} out per Mtok`}
|
|
708
|
-
</p>
|
|
709
|
-
)}
|
|
710
|
-
</div>
|
|
711
|
-
<Button disabled={flow.saving} onClick={() => setPickerOpen(true)} size="sm" variant="outline">
|
|
712
|
-
Change
|
|
713
|
-
</Button>
|
|
1223
|
+
<div className="grid place-items-center gap-7 py-6 text-center">
|
|
1224
|
+
<DecodedLabel leaving={leaving} text={t.onboarding.connectedProvider(flow.label)} />
|
|
1225
|
+
|
|
1226
|
+
<div
|
|
1227
|
+
className={cn(
|
|
1228
|
+
'grid justify-items-center gap-1.5 transition duration-[360ms] ease-out',
|
|
1229
|
+
leaving ? 'opacity-0 saturate-0' : 'opacity-100 saturate-100'
|
|
1230
|
+
)}
|
|
1231
|
+
>
|
|
1232
|
+
<div className="flex items-center gap-2">
|
|
1233
|
+
<span className="font-mono text-[0.625rem] uppercase tracking-[0.2em] text-muted-foreground">
|
|
1234
|
+
{t.onboarding.defaultModel}
|
|
1235
|
+
</span>
|
|
1236
|
+
{freeTier === true && (
|
|
1237
|
+
<span className="rounded-sm bg-emerald-500/15 px-1 py-0.5 text-[0.6rem] font-semibold uppercase tracking-wide text-emerald-600 dark:text-emerald-400">
|
|
1238
|
+
{t.onboarding.freeTier}
|
|
1239
|
+
</span>
|
|
1240
|
+
)}
|
|
1241
|
+
{freeTier === false && (
|
|
1242
|
+
<span className="rounded-sm bg-primary/15 px-1 py-0.5 text-[0.6rem] font-semibold uppercase tracking-wide text-primary">
|
|
1243
|
+
{t.onboarding.pro}
|
|
1244
|
+
</span>
|
|
1245
|
+
)}
|
|
714
1246
|
</div>
|
|
1247
|
+
<p className="font-mono text-base">
|
|
1248
|
+
<GlyphText text={scrambledModel} />
|
|
1249
|
+
</p>
|
|
1250
|
+
{price && (price.input || price.output) && (
|
|
1251
|
+
<p className="font-mono text-xs text-muted-foreground">
|
|
1252
|
+
{price.free ? t.onboarding.free : t.onboarding.price(price.input || '?', price.output || '?')}
|
|
1253
|
+
</p>
|
|
1254
|
+
)}
|
|
1255
|
+
<Button
|
|
1256
|
+
className="mt-0.5 text-xs"
|
|
1257
|
+
disabled={flow.saving}
|
|
1258
|
+
onClick={() => setPickerOpen(true)}
|
|
1259
|
+
size="inline"
|
|
1260
|
+
variant="text"
|
|
1261
|
+
>
|
|
1262
|
+
{t.onboarding.change}
|
|
1263
|
+
</Button>
|
|
715
1264
|
</div>
|
|
716
1265
|
|
|
717
|
-
<div
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
1266
|
+
<div
|
|
1267
|
+
className={cn(
|
|
1268
|
+
'transition duration-[360ms] ease-out',
|
|
1269
|
+
leaving ? 'opacity-0 saturate-0' : 'opacity-100 saturate-100'
|
|
1270
|
+
)}
|
|
1271
|
+
>
|
|
1272
|
+
<HackeryButton
|
|
1273
|
+
disabled={flow.saving}
|
|
1274
|
+
label={<GlyphText text={scrambledBegin} />}
|
|
1275
|
+
loading={flow.saving}
|
|
1276
|
+
onClick={onBegin}
|
|
1277
|
+
/>
|
|
722
1278
|
</div>
|
|
723
1279
|
|
|
724
1280
|
{/*
|
|
@@ -746,7 +1302,7 @@ function ConfirmingModelPanel({
|
|
|
746
1302
|
|
|
747
1303
|
function DocsLink({ children, href }: { children: React.ReactNode; href: string }) {
|
|
748
1304
|
return (
|
|
749
|
-
<Button asChild size="xs" variant="
|
|
1305
|
+
<Button asChild size="xs" variant="text">
|
|
750
1306
|
<a href={href} rel="noreferrer" target="_blank">
|
|
751
1307
|
<ExternalLink className="size-3" />
|
|
752
1308
|
{children}
|
|
@@ -757,8 +1313,8 @@ function DocsLink({ children, href }: { children: React.ReactNode; href: string
|
|
|
757
1313
|
|
|
758
1314
|
function Status({ children }: { children: React.ReactNode }) {
|
|
759
1315
|
return (
|
|
760
|
-
<div className="flex items-center gap-
|
|
761
|
-
<
|
|
1316
|
+
<div className="flex items-center gap-2.5 py-1 text-sm text-muted-foreground" role="status">
|
|
1317
|
+
<Loader className="size-7" type="lemniscate-bloom" />
|
|
762
1318
|
{children}
|
|
763
1319
|
</div>
|
|
764
1320
|
)
|