apex-code 0.0.1-alpha.7 → 0.0.1-alpha.9
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/CHANGELOG.md +229 -0
- package/README.md +5 -0
- package/dist/cli/args.d.ts +4 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +44 -9
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/concurrent-session.d.ts +3 -0
- package/dist/cli/concurrent-session.d.ts.map +1 -0
- package/dist/cli/concurrent-session.js +13 -0
- package/dist/cli/concurrent-session.js.map +1 -0
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +2 -1
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -2
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +3 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +21 -13
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +20 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +41 -9
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +172 -101
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +4 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +12 -9
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +10 -9
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +3 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +4 -3
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +41 -32
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/defaults.d.ts +1 -0
- package/dist/core/defaults.d.ts.map +1 -1
- package/dist/core/defaults.js +9 -0
- package/dist/core/defaults.js.map +1 -1
- package/dist/core/durable-state/sqlite.d.ts.map +1 -1
- package/dist/core/durable-state/sqlite.js +26 -2
- package/dist/core/durable-state/sqlite.js.map +1 -1
- package/dist/core/experimental.d.ts +4 -0
- package/dist/core/experimental.d.ts.map +1 -1
- package/dist/core/experimental.js +4 -0
- package/dist/core/experimental.js.map +1 -1
- package/dist/core/extensions/index.d.ts +2 -2
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +115 -48
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/types.d.ts +42 -9
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +3 -0
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +1 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +1 -1
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts +1 -2
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +2 -17
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/http-idle-timeout.d.ts +12 -0
- package/dist/core/http-idle-timeout.d.ts.map +1 -0
- package/dist/core/http-idle-timeout.js +27 -0
- package/dist/core/http-idle-timeout.js.map +1 -0
- package/dist/core/keybindings.d.ts +38 -13
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +26 -5
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/lsp/client.d.ts +2 -0
- package/dist/core/lsp/client.d.ts.map +1 -1
- package/dist/core/lsp/client.js +27 -2
- package/dist/core/lsp/client.js.map +1 -1
- package/dist/core/model-config.d.ts +10 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +4 -1
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-resolver.d.ts +1 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +14 -8
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/models-store.d.ts.map +1 -1
- package/dist/core/models-store.js +2 -1
- package/dist/core/models-store.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +22 -14
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/pi-manifest.d.ts.map +1 -1
- package/dist/core/pi-manifest.js +2 -1
- package/dist/core/pi-manifest.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +2 -1
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +3 -2
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sandbox/child-entry.d.ts.map +1 -1
- package/dist/core/sandbox/child-entry.js +8 -1
- package/dist/core/sandbox/child-entry.js.map +1 -1
- package/dist/core/sandbox/cli-launch.d.ts +3 -0
- package/dist/core/sandbox/cli-launch.d.ts.map +1 -1
- package/dist/core/sandbox/cli-launch.js +25 -4
- package/dist/core/sandbox/cli-launch.js.map +1 -1
- package/dist/core/sandbox/cli-supervisor.d.ts.map +1 -1
- package/dist/core/sandbox/cli-supervisor.js +53 -13
- package/dist/core/sandbox/cli-supervisor.js.map +1 -1
- package/dist/core/sandbox/linux-backend.d.ts.map +1 -1
- package/dist/core/sandbox/linux-backend.js +15 -0
- package/dist/core/sandbox/linux-backend.js.map +1 -1
- package/dist/core/sandbox/macos-backend.d.ts.map +1 -1
- package/dist/core/sandbox/macos-backend.js +24 -1
- package/dist/core/sandbox/macos-backend.js.map +1 -1
- package/dist/core/sandbox/rpc/credential-client.d.ts +20 -0
- package/dist/core/sandbox/rpc/credential-client.d.ts.map +1 -0
- package/dist/core/sandbox/rpc/credential-client.js +170 -0
- package/dist/core/sandbox/rpc/credential-client.js.map +1 -0
- package/dist/core/sandbox/rpc/credential-proxy.d.ts +44 -0
- package/dist/core/sandbox/rpc/credential-proxy.d.ts.map +1 -0
- package/dist/core/sandbox/rpc/credential-proxy.js +381 -0
- package/dist/core/sandbox/rpc/credential-proxy.js.map +1 -0
- package/dist/core/sandbox/supervisor.d.ts +11 -0
- package/dist/core/sandbox/supervisor.d.ts.map +1 -1
- package/dist/core/sandbox/supervisor.js.map +1 -1
- package/dist/core/sandbox/terminal-size.d.ts +40 -0
- package/dist/core/sandbox/terminal-size.d.ts.map +1 -0
- package/dist/core/sandbox/terminal-size.js +105 -0
- package/dist/core/sandbox/terminal-size.js.map +1 -0
- package/dist/core/sdk.d.ts +8 -5
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +26 -7
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-export.d.ts +4 -0
- package/dist/core/session-export.d.ts.map +1 -0
- package/dist/core/session-export.js +32 -0
- package/dist/core/session-export.js.map +1 -0
- package/dist/core/session-lease.d.ts +31 -0
- package/dist/core/session-lease.d.ts.map +1 -0
- package/dist/core/session-lease.js +138 -0
- package/dist/core/session-lease.js.map +1 -0
- package/dist/core/session-manager.d.ts +5 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +5 -4
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-diagnostics.d.ts +9 -0
- package/dist/core/settings-diagnostics.d.ts.map +1 -0
- package/dist/core/settings-diagnostics.js +21 -0
- package/dist/core/settings-diagnostics.js.map +1 -0
- package/dist/core/settings-manager.d.ts +35 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +93 -12
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +51 -33
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +4 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts +2 -2
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +53 -62
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +14 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +38 -20
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +24 -0
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts +0 -5
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js +2 -5
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +22 -4
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +1 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts +2 -0
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +35 -10
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +4 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +9 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts +2 -0
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +5 -1
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/powershell.d.ts +16 -0
- package/dist/core/tools/powershell.d.ts.map +1 -0
- package/dist/core/tools/powershell.js +39 -0
- package/dist/core/tools/powershell.js.map +1 -0
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +2 -0
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/web-search-exa.d.ts +33 -0
- package/dist/core/tools/web-search-exa.d.ts.map +1 -0
- package/dist/core/tools/web-search-exa.js +128 -0
- package/dist/core/tools/web-search-exa.js.map +1 -0
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +2 -0
- package/dist/core/tools/write.js.map +1 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +2 -1
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/core/web-search-provider.d.ts +64 -0
- package/dist/core/web-search-provider.d.ts.map +1 -0
- package/dist/core/web-search-provider.js +149 -0
- package/dist/core/web-search-provider.js.map +1 -0
- package/dist/extensions/llama/index.d.ts.map +1 -1
- package/dist/extensions/llama/index.js +2 -0
- package/dist/extensions/llama/index.js.map +1 -1
- package/dist/extensions/llama/provider.d.ts.map +1 -1
- package/dist/extensions/llama/provider.js +6 -2
- package/dist/extensions/llama/provider.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +5 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +38 -16
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +4 -3
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +23 -0
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +105 -2
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.d.ts +7 -1
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.js +11 -2
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts +4 -0
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +24 -6
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.js +6 -3
- package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
- package/dist/modes/interactive/components/first-use-hints.d.ts +8 -0
- package/dist/modes/interactive/components/first-use-hints.d.ts.map +1 -0
- package/dist/modes/interactive/components/first-use-hints.js +22 -0
- package/dist/modes/interactive/components/first-use-hints.js.map +1 -0
- package/dist/modes/interactive/components/footer.d.ts +12 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +128 -9
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +12 -3
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +71 -14
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +10 -3
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +128 -56
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
- package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
- package/dist/modes/interactive/components/settings-submenu.js +165 -0
- package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
- package/dist/modes/interactive/components/splash-header.d.ts +29 -4
- package/dist/modes/interactive/components/splash-header.d.ts.map +1 -1
- package/dist/modes/interactive/components/splash-header.js +58 -23
- package/dist/modes/interactive/components/splash-header.js.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +74 -17
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +14 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +113 -35
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tool-panel.d.ts +30 -0
- package/dist/modes/interactive/components/tool-panel.d.ts.map +1 -0
- package/dist/modes/interactive/components/tool-panel.js +130 -0
- package/dist/modes/interactive/components/tool-panel.js.map +1 -0
- package/dist/modes/interactive/external-editor.d.ts.map +1 -1
- package/dist/modes/interactive/external-editor.js +2 -1
- package/dist/modes/interactive/external-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +55 -9
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +446 -87
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/model-catalog-refresh.d.ts +7 -0
- package/dist/modes/interactive/model-catalog-refresh.d.ts.map +1 -0
- package/dist/modes/interactive/model-catalog-refresh.js +34 -0
- package/dist/modes/interactive/model-catalog-refresh.js.map +1 -0
- package/dist/modes/interactive/theme/apex.json +62 -63
- package/dist/modes/interactive/theme/dark.json +2 -0
- package/dist/modes/interactive/theme/light.json +2 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts +10 -2
- package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-controller.js +25 -11
- package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
- package/dist/modes/interactive/theme/theme-schema.json +9 -1
- package/dist/modes/interactive/theme/theme.d.ts +14 -3
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +60 -10
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/json-event.d.ts +18 -12
- package/dist/modes/json-event.d.ts.map +1 -1
- package/dist/modes/json-event.js +22 -5
- package/dist/modes/json-event.js.map +1 -1
- package/dist/package-manager-cli.d.ts +1 -0
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +197 -5
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/server/create-harness.d.ts.map +1 -1
- package/dist/server/create-harness.js +2 -0
- package/dist/server/create-harness.js.map +1 -1
- package/dist/themes/apex-logo.d.ts +36 -16
- package/dist/themes/apex-logo.d.ts.map +1 -1
- package/dist/themes/apex-logo.js +32 -28
- package/dist/themes/apex-logo.js.map +1 -1
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js +2 -1
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/management-http.d.ts +5 -3
- package/dist/utils/management-http.d.ts.map +1 -1
- package/dist/utils/management-http.js +14 -10
- package/dist/utils/management-http.js.map +1 -1
- package/dist/utils/shell.d.ts +3 -0
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +17 -5
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts +1 -0
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js +60 -1
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/dist/utils/text.d.ts +8 -0
- package/dist/utils/text.d.ts.map +1 -0
- package/dist/utils/text.js +9 -0
- package/dist/utils/text.js.map +1 -0
- package/dist/utils/tools-manager.d.ts +10 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +15 -20
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/compaction.md +18 -3
- package/docs/custom-provider.md +6 -3
- package/docs/environment-variables.md +1 -0
- package/docs/extensions.md +21 -7
- package/docs/json.md +12 -5
- package/docs/keybindings.md +15 -9
- package/docs/llama-cpp.md +2 -0
- package/docs/models.md +8 -2
- package/docs/packages.md +2 -2
- package/docs/rpc.md +24 -7
- package/docs/sdk.md +16 -2
- package/docs/settings.md +73 -2
- package/docs/skills.md +4 -3
- package/docs/terminal-setup.md +34 -9
- package/docs/themes.md +24 -3
- package/docs/tui.md +2 -2
- package/docs/usage.md +9 -4
- package/docs/windows.md +24 -2
- package/examples/extensions/border-status-editor.ts +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/git-checkpoint.ts +2 -2
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/notify.ts +3 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/subagent/README.md +3 -1
- package/examples/extensions/subagent/agents.ts +40 -9
- package/examples/extensions/subagent/index.ts +27 -4
- package/examples/extensions/titlebar-spinner.ts +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/rpc-extension-ui.ts +1 -1
- package/examples/sdk/06-extensions.ts +1 -1
- package/examples/sdk/README.md +1 -1
- package/npm-shrinkwrap.json +280 -445
- package/package.json +9 -11
|
@@ -14,6 +14,7 @@ import { APP_NAME, APP_TITLE, CONFIG_DIR_NAME, getAgentDir, getAuthPath, getDebu
|
|
|
14
14
|
import { parseSkillBlock } from "../../core/agent-session.js";
|
|
15
15
|
import { SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js";
|
|
16
16
|
import { CACHE_TTL_MS, collectCacheMisses, computeCacheWaste, detectCacheMiss, } from "../../core/cache-stats.js";
|
|
17
|
+
import { DEFAULT_THINKING_LEVEL, THINKING_LEVEL_OPTIONS } from "../../core/defaults.js";
|
|
17
18
|
import { getApexEnvironment } from "../../core/environment.js";
|
|
18
19
|
import { FooterDataProvider } from "../../core/footer-data-provider.js";
|
|
19
20
|
import { configureHttpDispatcher, formatHttpIdleTimeoutMs } from "../../core/http-dispatcher.js";
|
|
@@ -37,6 +38,7 @@ import { parseGitUrl } from "../../utils/git.js";
|
|
|
37
38
|
import { openBrowser } from "../../utils/open-browser.js";
|
|
38
39
|
import { getCwdRelativePath } from "../../utils/paths.js";
|
|
39
40
|
import { killTrackedDetachedChildren } from "../../utils/shell.js";
|
|
41
|
+
import { loadAllHighlightLanguages } from "../../utils/syntax-highlight.js";
|
|
40
42
|
import { ensureTool } from "../../utils/tools-manager.js";
|
|
41
43
|
import { checkForNewApexCodeVersion } from "../../utils/version-check.js";
|
|
42
44
|
import { ArminComponent } from "./components/armin.js";
|
|
@@ -54,6 +56,7 @@ import { EarendilAnnouncementComponent } from "./components/earendil-announcemen
|
|
|
54
56
|
import { ExtensionEditorComponent } from "./components/extension-editor.js";
|
|
55
57
|
import { ExtensionInputComponent } from "./components/extension-input.js";
|
|
56
58
|
import { ExtensionSelectorComponent } from "./components/extension-selector.js";
|
|
59
|
+
import { FirstUseHints } from "./components/first-use-hints.js";
|
|
57
60
|
import { FooterComponent, formatTokens } from "./components/footer.js";
|
|
58
61
|
import { formatKeyText, keyDisplayText, keyHint, keyText, rawKeyHint } from "./components/keybinding-hints.js";
|
|
59
62
|
import { LoginDialogComponent } from "./components/login-dialog.js";
|
|
@@ -66,12 +69,14 @@ import { PERMISSION_MODE_OVERRIDE_HINTS, SettingsSelectorComponent } from "./com
|
|
|
66
69
|
import { SkillInvocationMessageComponent } from "./components/skill-invocation-message.js";
|
|
67
70
|
import { ApexSplashHeader } from "./components/splash-header.js";
|
|
68
71
|
import { BranchSummaryStatusIndicator, CompactionStatusIndicator, IdleStatus, RetryStatusIndicator, WorkingStatusIndicator, } from "./components/status-indicator.js";
|
|
72
|
+
import { ThinkingSelectorComponent } from "./components/thinking-selector.js";
|
|
69
73
|
import { ToolExecutionComponent } from "./components/tool-execution.js";
|
|
70
74
|
import { TreeSelectorComponent } from "./components/tree-selector.js";
|
|
71
75
|
import { TrustSelectorComponent } from "./components/trust-selector.js";
|
|
72
76
|
import { UserMessageComponent } from "./components/user-message.js";
|
|
73
77
|
import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
|
|
74
78
|
import { editInExternalEditor } from "./external-editor.js";
|
|
79
|
+
import { refreshModelCatalogs } from "./model-catalog-refresh.js";
|
|
75
80
|
import { getModelSearchText } from "./model-search.js";
|
|
76
81
|
import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, onThemeChange, setRegisteredThemes, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
|
|
77
82
|
import { InteractiveThemeController } from "./theme/theme-controller.js";
|
|
@@ -93,6 +98,9 @@ class ExpandableText extends Text {
|
|
|
93
98
|
function isCustomSessionEntry(item) {
|
|
94
99
|
return "type" in item && item.type === "custom";
|
|
95
100
|
}
|
|
101
|
+
function isCompactionCostNotice(item) {
|
|
102
|
+
return "type" in item && item.type === "compaction_cost";
|
|
103
|
+
}
|
|
96
104
|
const DEAD_TERMINAL_ERROR_CODES = new Set(["EIO", "EPIPE", "ENOTCONN"]);
|
|
97
105
|
function isDeadTerminalError(error) {
|
|
98
106
|
if (!error || typeof error !== "object" || !("code" in error)) {
|
|
@@ -132,6 +140,11 @@ export function formatResumeCommand(sessionManager) {
|
|
|
132
140
|
function hasDefaultModelProvider(providerId) {
|
|
133
141
|
return providerId in defaultModelPerProvider;
|
|
134
142
|
}
|
|
143
|
+
function llamaCppPostLoginGuidance(actionLabel, loadedModelCount) {
|
|
144
|
+
return loadedModelCount === 0
|
|
145
|
+
? `${actionLabel}. No llama.cpp models are loaded. Use /llama to load a model, then /model to select it.`
|
|
146
|
+
: `${actionLabel}. Use /model to select a loaded llama.cpp model, or /llama to manage models.`;
|
|
147
|
+
}
|
|
135
148
|
const AUTH_TYPE_ORDER = { oauth: 0, api_key: 1 };
|
|
136
149
|
function createFuzzyAutocompleteItems(items, prefix, getSearchText, toAutocompleteItem) {
|
|
137
150
|
const filtered = fuzzyFilter(items, prefix, getSearchText);
|
|
@@ -172,9 +185,21 @@ function formatLoginProviderCompletionDescription(provider) {
|
|
|
172
185
|
export function createInteractiveTui(options) {
|
|
173
186
|
const terminal = options.terminal ?? new ProcessTerminal();
|
|
174
187
|
if (options.tuiMode === "fullscreen") {
|
|
188
|
+
const styleSearchMatch = (text) => theme.bg("searchMatchBg", theme.fg("searchMatchText", text));
|
|
175
189
|
return new TuiAltScreen(terminal, options.showHardwareCursor, options.logDirectory, {
|
|
190
|
+
searchMatchStyle: (text) => theme.underline(styleSearchMatch(text)),
|
|
191
|
+
searchCurrentMatchStyle: (text) => theme.bold(theme.inverse(styleSearchMatch(text))),
|
|
176
192
|
openUrl: openBrowser,
|
|
177
193
|
onRightClickPaste: options.onRightClickPaste,
|
|
194
|
+
copySelection: async (text) => {
|
|
195
|
+
try {
|
|
196
|
+
await copyToClipboard(text);
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
},
|
|
178
203
|
});
|
|
179
204
|
}
|
|
180
205
|
return new TuiMainScreen(terminal, options.showHardwareCursor, options.logDirectory);
|
|
@@ -236,6 +261,14 @@ export class InteractiveMode {
|
|
|
236
261
|
footer;
|
|
237
262
|
footerContainer;
|
|
238
263
|
footerDataProvider;
|
|
264
|
+
/**
|
|
265
|
+
* Whether the startup screen is showing its counted inventory line.
|
|
266
|
+
*
|
|
267
|
+
* Warnings are only allowed to collapse into that count when the count is
|
|
268
|
+
* actually on screen. Under a quiet startup there is no splash header, so
|
|
269
|
+
* collapsing them would hide conflicts entirely rather than summarise them.
|
|
270
|
+
*/
|
|
271
|
+
startupInventoryVisible = false;
|
|
239
272
|
// Stored so the same manager can be injected into custom editors, selectors, and extension UI.
|
|
240
273
|
keybindings;
|
|
241
274
|
version;
|
|
@@ -258,6 +291,7 @@ export class InteractiveMode {
|
|
|
258
291
|
// Status line tracking (for mutating immediately-sequential status updates)
|
|
259
292
|
lastStatusSpacer = undefined;
|
|
260
293
|
lastStatusText = undefined;
|
|
294
|
+
managedToolStatusStarted = false;
|
|
261
295
|
// Streaming message tracking
|
|
262
296
|
streamingComponent = undefined;
|
|
263
297
|
streamingMessage = undefined;
|
|
@@ -279,6 +313,7 @@ export class InteractiveMode {
|
|
|
279
313
|
signalCleanupHandlers = [];
|
|
280
314
|
// Track if editor is in bash mode (text starts with !)
|
|
281
315
|
isBashMode = false;
|
|
316
|
+
firstUseHints;
|
|
282
317
|
// Track current bash execution component
|
|
283
318
|
bashComponent = undefined;
|
|
284
319
|
// Track pending bash components (shown in pending area, moved to chat on submit)
|
|
@@ -328,6 +363,14 @@ export class InteractiveMode {
|
|
|
328
363
|
get settingsManager() {
|
|
329
364
|
return this.session.settingsManager;
|
|
330
365
|
}
|
|
366
|
+
offerFirstUseHint(id) {
|
|
367
|
+
this.firstUseHints ??= new FirstUseHints(this.settingsManager.getFirstUseHints());
|
|
368
|
+
const hint = this.firstUseHints.offer(id);
|
|
369
|
+
if (!hint)
|
|
370
|
+
return;
|
|
371
|
+
this.settingsManager.setFirstUseHints(this.firstUseHints.getSeen());
|
|
372
|
+
this.chatContainer.addChild(new Text(theme.fg("dim", hint), this.outputPad, 0));
|
|
373
|
+
}
|
|
331
374
|
constructor(runtimeHost, options = {}) {
|
|
332
375
|
this.runtimeHost = runtimeHost;
|
|
333
376
|
const tuiMode = options.tuiMode ?? this.settingsManager.getTuiMode();
|
|
@@ -338,6 +381,7 @@ export class InteractiveMode {
|
|
|
338
381
|
});
|
|
339
382
|
this.runtimeHost.setRebindSession(async () => {
|
|
340
383
|
await this.rebindCurrentSession({ renderBeforeBind: true });
|
|
384
|
+
await this.themeController.applyFromSettings();
|
|
341
385
|
});
|
|
342
386
|
this.version = VERSION;
|
|
343
387
|
this.renderer = createInteractiveTui({
|
|
@@ -366,11 +410,14 @@ export class InteractiveMode {
|
|
|
366
410
|
this.defaultEditor = new CustomEditor(this.ui, getEditorTheme(), this.keybindings, {
|
|
367
411
|
paddingX: editorPaddingX,
|
|
368
412
|
autocompleteMaxVisible,
|
|
369
|
-
promptPrefix: "
|
|
413
|
+
promptPrefix: "› ",
|
|
370
414
|
promptColor: (text) => theme.fg("accent", text),
|
|
371
|
-
placeholder: "Ask anything
|
|
415
|
+
placeholder: "Ask anything",
|
|
372
416
|
placeholderColor: (text) => theme.fg("dim", text),
|
|
373
417
|
commandColor: (text) => theme.fg("accent", text),
|
|
418
|
+
surfaceColor: (text) => theme.bg("userMessageBg", text),
|
|
419
|
+
autocompleteRule: (width) => theme.fg("borderMuted", (this.settingsManager.getSymbolPreset() === "ascii" ? "-" : "┄").repeat(Math.max(1, width))),
|
|
420
|
+
autocompleteFooter: () => this.buildAutocompleteFooter(),
|
|
374
421
|
});
|
|
375
422
|
this.editor = this.defaultEditor;
|
|
376
423
|
this.editorContainer = new Container();
|
|
@@ -385,7 +432,12 @@ export class InteractiveMode {
|
|
|
385
432
|
this.outputPad = this.settingsManager.getOutputPad();
|
|
386
433
|
// Register themes from resource loader and initialize
|
|
387
434
|
setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
|
|
388
|
-
this.themeController = new InteractiveThemeController(this.ui,
|
|
435
|
+
this.themeController = new InteractiveThemeController(this.ui, {
|
|
436
|
+
getSettingsManager: () => this.settingsManager,
|
|
437
|
+
showError: (message) => this.showError(message),
|
|
438
|
+
onChanged: () => this.updateEditorBorderColor(),
|
|
439
|
+
initialThemeSetting: options.initialThemeSetting,
|
|
440
|
+
});
|
|
389
441
|
}
|
|
390
442
|
getAutocompleteSourceTag(sourceInfo) {
|
|
391
443
|
if (!sourceInfo) {
|
|
@@ -455,6 +507,15 @@ export class InteractiveMode {
|
|
|
455
507
|
}));
|
|
456
508
|
};
|
|
457
509
|
}
|
|
510
|
+
const thinkingCommand = slashCommands.find((command) => command.name === "thinking");
|
|
511
|
+
if (thinkingCommand) {
|
|
512
|
+
thinkingCommand.getArgumentCompletions = (prefix) => {
|
|
513
|
+
return createFuzzyAutocompleteItems(this.session.getAvailableThinkingLevels(), prefix, (level) => level, (level) => ({
|
|
514
|
+
value: level,
|
|
515
|
+
label: level,
|
|
516
|
+
}));
|
|
517
|
+
};
|
|
518
|
+
}
|
|
458
519
|
const loginCommand = slashCommands.find((command) => command.name === "login");
|
|
459
520
|
if (loginCommand) {
|
|
460
521
|
loginCommand.getArgumentCompletions = (prefix) => {
|
|
@@ -548,14 +609,14 @@ export class InteractiveMode {
|
|
|
548
609
|
tui.setLayoutRoot(this.fullscreenLayoutRoot);
|
|
549
610
|
}
|
|
550
611
|
}
|
|
551
|
-
stopInteractiveTui() {
|
|
552
|
-
if (this.renderer.mode === "fullscreen") {
|
|
612
|
+
stopInteractiveTui(fullscreenExitOutput) {
|
|
613
|
+
if (this.renderer.mode === "fullscreen" && fullscreenExitOutput === "transcript") {
|
|
553
614
|
while (this.renderer.hasOverlayEntries)
|
|
554
615
|
this.renderer.hideOverlay();
|
|
555
616
|
this.switchTuiMode("regular", false, false);
|
|
556
617
|
this.renderer.renderNow();
|
|
557
618
|
}
|
|
558
|
-
this.ui.stop();
|
|
619
|
+
this.ui.stop({ preserveScreen: this.renderer.mode === "fullscreen" });
|
|
559
620
|
}
|
|
560
621
|
switchTuiMode(mode, restoreProgress = true, startRenderer = true) {
|
|
561
622
|
const previousUi = this.renderer;
|
|
@@ -612,10 +673,9 @@ export class InteractiveMode {
|
|
|
612
673
|
this.registerSignalHandlers();
|
|
613
674
|
// Load changelog (only show new entries, skip for resumed sessions)
|
|
614
675
|
this.changelogMarkdown = this.getChangelogForDisplay();
|
|
615
|
-
//
|
|
616
|
-
//
|
|
617
|
-
|
|
618
|
-
this.fdPath = fdPath;
|
|
676
|
+
// Resolve the effective mode before mounting or focusing components. Both
|
|
677
|
+
// operations can schedule a frame even before the renderer is started.
|
|
678
|
+
await this.refreshFooterPermissionMode();
|
|
619
679
|
if (this.session.scopedModels.length > 0 && (this.options.verbose || !this.settingsManager.getQuietStartup())) {
|
|
620
680
|
const modelList = this.session.scopedModels
|
|
621
681
|
.map((sm) => {
|
|
@@ -659,9 +719,11 @@ export class InteractiveMode {
|
|
|
659
719
|
this.widgetContainerBelow,
|
|
660
720
|
this.footerContainer,
|
|
661
721
|
]);
|
|
722
|
+
// Accept text while startup completes, but only enable interrupt, exit, and submission feedback.
|
|
723
|
+
this.defaultEditor.onAction("app.clear", () => this.handleCtrlC());
|
|
724
|
+
this.defaultEditor.onCtrlD = () => this.handleCtrlD();
|
|
725
|
+
this.defaultEditor.onSubmit = (text) => this.handleStartupSubmit(text);
|
|
662
726
|
this.ui.setFocus(this.editor);
|
|
663
|
-
this.setupKeyHandlers();
|
|
664
|
-
this.setupEditorSubmitHandler();
|
|
665
727
|
// Start the UI before initializing extensions so session_start handlers can use interactive dialogs
|
|
666
728
|
this.ui.start();
|
|
667
729
|
this.isInitialized = true;
|
|
@@ -698,18 +760,42 @@ export class InteractiveMode {
|
|
|
698
760
|
this.builtInHeader = new ApexSplashHeader(this.version, () => this.session.state.model?.id, () => this.session.sessionManager.getCwd(), this.options.verbose ? expandedInstructions : undefined, {
|
|
699
761
|
topPadding: true,
|
|
700
762
|
getSymbolPreset: () => this.settingsManager.getSymbolPreset(),
|
|
763
|
+
getExtraMetadata: () => {
|
|
764
|
+
// Which branch the session is rooted on differs between launches
|
|
765
|
+
// and is not derivable from the prompt, so it belongs here. The
|
|
766
|
+
// tray carries it too, but drops it first when space is tight.
|
|
767
|
+
const branch = this.footerDataProvider.getGitBranch();
|
|
768
|
+
return branch ? [{ label: "branch", value: branch }] : [];
|
|
769
|
+
},
|
|
770
|
+
getInventory: () => this.buildStartupInventory(),
|
|
771
|
+
inventoryHint: "/resources",
|
|
772
|
+
getShortcuts: () => this.buildStartupShortcuts(),
|
|
701
773
|
getHint: () => "Apex Code can explain its own features and look up its docs.",
|
|
702
774
|
});
|
|
703
775
|
// Setup UI layout
|
|
776
|
+
this.startupInventoryVisible = true;
|
|
704
777
|
this.headerContainer.addChild(this.builtInHeader);
|
|
705
778
|
this.headerContainer.addChild(new Spacer(1));
|
|
706
779
|
}
|
|
707
780
|
else {
|
|
708
781
|
// Minimal header when silenced
|
|
782
|
+
this.startupInventoryVisible = false;
|
|
709
783
|
this.builtInHeader = new Text("", 0, 0);
|
|
710
784
|
this.headerContainer.addChild(this.builtInHeader);
|
|
711
785
|
}
|
|
712
786
|
this.ui.requestRender();
|
|
787
|
+
// Ensure fd and rg are available after mounting the TUI (downloads if missing, adds to PATH via getBinDir)
|
|
788
|
+
// so slow downloads do not make startup appear frozen.
|
|
789
|
+
// Both are needed: fd for autocomplete, rg for grep tool and bash commands.
|
|
790
|
+
const [fdPath] = await Promise.all([
|
|
791
|
+
ensureTool("fd", (status) => this.showManagedToolStatus(status)),
|
|
792
|
+
ensureTool("rg", (status) => this.showManagedToolStatus(status)),
|
|
793
|
+
]);
|
|
794
|
+
this.fdPath = fdPath;
|
|
795
|
+
// Enable the remaining input handlers only after managed-tool setup completes.
|
|
796
|
+
this.setupKeyHandlers();
|
|
797
|
+
this.setupEditorSubmitHandler();
|
|
798
|
+
this.ui.requestRender();
|
|
713
799
|
// Initialize extensions first so resources are shown before messages
|
|
714
800
|
await this.rebindCurrentSession();
|
|
715
801
|
// Render initial messages AFTER showing loaded resources
|
|
@@ -726,6 +812,14 @@ export class InteractiveMode {
|
|
|
726
812
|
});
|
|
727
813
|
// Initialize available provider count for footer display
|
|
728
814
|
await this.updateAvailableProviderCount();
|
|
815
|
+
// Flush the completed startup state before loading the remaining syntax grammars.
|
|
816
|
+
this.ui.renderNow();
|
|
817
|
+
void loadAllHighlightLanguages().then(() => {
|
|
818
|
+
if (!this.isInitialized)
|
|
819
|
+
return;
|
|
820
|
+
this.ui.invalidate();
|
|
821
|
+
this.ui.requestRender();
|
|
822
|
+
});
|
|
729
823
|
}
|
|
730
824
|
/**
|
|
731
825
|
* Update terminal title with session name and cwd.
|
|
@@ -749,8 +843,7 @@ export class InteractiveMode {
|
|
|
749
843
|
if (!getApexEnvironment("APEX_CODE_OFFLINE")) {
|
|
750
844
|
const controller = new AbortController();
|
|
751
845
|
const timeout = setTimeout(() => controller.abort(), 15_000);
|
|
752
|
-
void this.session.modelRuntime
|
|
753
|
-
.refresh({ signal: controller.signal })
|
|
846
|
+
void refreshModelCatalogs(this.session.modelRuntime, controller.signal)
|
|
754
847
|
.then(() => this.updateAvailableProviderCount())
|
|
755
848
|
.catch(() => { })
|
|
756
849
|
.finally(() => clearTimeout(timeout));
|
|
@@ -782,7 +875,18 @@ export class InteractiveMode {
|
|
|
782
875
|
}
|
|
783
876
|
});
|
|
784
877
|
// Show startup warnings
|
|
785
|
-
const { migratedProviders, modelFallbackMessage, initialMessage, initialImages, initialMessages } = this.options;
|
|
878
|
+
const { migratedProviders, startupDiagnostics, modelFallbackMessage, initialMessage, initialImages, initialMessages, } = this.options;
|
|
879
|
+
for (const diagnostic of startupDiagnostics ?? []) {
|
|
880
|
+
if (diagnostic.type === "error") {
|
|
881
|
+
this.showError(diagnostic.message);
|
|
882
|
+
}
|
|
883
|
+
else if (diagnostic.type === "warning") {
|
|
884
|
+
this.showWarning(diagnostic.message);
|
|
885
|
+
}
|
|
886
|
+
else {
|
|
887
|
+
this.showStatus(diagnostic.message);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
786
890
|
if (migratedProviders && migratedProviders.length > 0) {
|
|
787
891
|
this.showWarning(`Migrated credentials to auth.json: ${migratedProviders.join(", ")}`);
|
|
788
892
|
}
|
|
@@ -1205,7 +1309,7 @@ export class InteractiveMode {
|
|
|
1205
1309
|
showLoadedResources(options) {
|
|
1206
1310
|
// Resource rendering is idempotent; chat clears no longer clear this separate container.
|
|
1207
1311
|
this.loadedResourcesContainer.clear();
|
|
1208
|
-
const showListing = options?.force || this.options.verbose
|
|
1312
|
+
const showListing = options?.force || this.options.verbose;
|
|
1209
1313
|
const showDiagnostics = showListing || options?.showDiagnosticsWhenQuiet === true;
|
|
1210
1314
|
if (!showListing && !showDiagnostics) {
|
|
1211
1315
|
return;
|
|
@@ -1322,43 +1426,105 @@ export class InteractiveMode {
|
|
|
1322
1426
|
addLoadedSection("Themes", themeCompactList, themeList);
|
|
1323
1427
|
}
|
|
1324
1428
|
}
|
|
1429
|
+
// Warnings collapse into the counted line when that line is on screen; the
|
|
1430
|
+
// full trace lives behind /resources. Errors always surface inline,
|
|
1431
|
+
// because an error means a resource did not load at all.
|
|
1325
1432
|
if (showDiagnostics) {
|
|
1326
|
-
const
|
|
1327
|
-
|
|
1328
|
-
const
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
const
|
|
1335
|
-
this.loadedResourcesContainer.addChild(new Text(`${theme.fg(
|
|
1336
|
-
this.loadedResourcesContainer.addChild(new Spacer(1));
|
|
1337
|
-
}
|
|
1338
|
-
const extensionDiagnostics = [];
|
|
1339
|
-
const extensionErrors = this.session.resourceLoader.getExtensions().errors;
|
|
1340
|
-
if (extensionErrors.length > 0) {
|
|
1341
|
-
for (const error of extensionErrors) {
|
|
1342
|
-
extensionDiagnostics.push({ type: "error", message: error.error, path: error.path });
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
const commandDiagnostics = this.session.extensionRunner.getCommandDiagnostics();
|
|
1346
|
-
extensionDiagnostics.push(...commandDiagnostics);
|
|
1347
|
-
extensionDiagnostics.push(...this.getBuiltInCommandConflictDiagnostics(this.session.extensionRunner));
|
|
1348
|
-
const shortcutDiagnostics = this.session.extensionRunner.getShortcutDiagnostics();
|
|
1349
|
-
extensionDiagnostics.push(...shortcutDiagnostics);
|
|
1350
|
-
if (extensionDiagnostics.length > 0) {
|
|
1351
|
-
const warningLines = this.formatDiagnostics(extensionDiagnostics, sourceInfos);
|
|
1352
|
-
this.loadedResourcesContainer.addChild(new Text(`${theme.fg("warning", "[Extension issues]")}\n${warningLines}`, 0, 0));
|
|
1353
|
-
this.loadedResourcesContainer.addChild(new Spacer(1));
|
|
1354
|
-
}
|
|
1355
|
-
const themeDiagnostics = themesResult.diagnostics;
|
|
1356
|
-
if (themeDiagnostics.length > 0) {
|
|
1357
|
-
const warningLines = this.formatDiagnostics(themeDiagnostics, sourceInfos);
|
|
1358
|
-
this.loadedResourcesContainer.addChild(new Text(`${theme.fg("warning", "[Theme conflicts]")}\n${warningLines}`, 0, 0));
|
|
1433
|
+
const collapseWarnings = !showListing && this.startupInventoryVisible;
|
|
1434
|
+
for (const group of this.collectDiagnosticGroups()) {
|
|
1435
|
+
const visible = collapseWarnings
|
|
1436
|
+
? group.diagnostics.filter((diagnostic) => diagnostic.type === "error")
|
|
1437
|
+
: group.diagnostics;
|
|
1438
|
+
if (visible.length === 0)
|
|
1439
|
+
continue;
|
|
1440
|
+
const severity = visible.some((diagnostic) => diagnostic.type === "error") ? "error" : "warning";
|
|
1441
|
+
const body = this.formatDiagnostics(visible, sourceInfos);
|
|
1442
|
+
this.loadedResourcesContainer.addChild(new Text(`${theme.fg(severity, `[${group.label}]`)}\n${body}`, 0, 0));
|
|
1359
1443
|
this.loadedResourcesContainer.addChild(new Spacer(1));
|
|
1360
1444
|
}
|
|
1361
1445
|
}
|
|
1446
|
+
// Counts on the startup screen are read at render time, so they fill in
|
|
1447
|
+
// once resources finish loading without an explicit refresh here.
|
|
1448
|
+
this.builtInHeader?.invalidate?.();
|
|
1449
|
+
}
|
|
1450
|
+
/**
|
|
1451
|
+
* Every startup diagnostic, grouped by the resource kind that produced it.
|
|
1452
|
+
*
|
|
1453
|
+
* One list feeds both the counted line in the header and the detailed
|
|
1454
|
+
* listing, so the two can never disagree about how many issues exist.
|
|
1455
|
+
*/
|
|
1456
|
+
collectDiagnosticGroups() {
|
|
1457
|
+
const extensionDiagnostics = [
|
|
1458
|
+
...this.session.resourceLoader
|
|
1459
|
+
.getExtensions()
|
|
1460
|
+
.errors.map((error) => ({ type: "error", message: error.error, path: error.path })),
|
|
1461
|
+
...this.session.extensionRunner.getCommandDiagnostics(),
|
|
1462
|
+
...this.getBuiltInCommandConflictDiagnostics(this.session.extensionRunner),
|
|
1463
|
+
...this.session.extensionRunner.getShortcutDiagnostics(),
|
|
1464
|
+
];
|
|
1465
|
+
return [
|
|
1466
|
+
{ label: "Skill conflicts", diagnostics: this.session.resourceLoader.getSkills().diagnostics },
|
|
1467
|
+
{ label: "Prompt conflicts", diagnostics: this.session.resourceLoader.getPrompts().diagnostics },
|
|
1468
|
+
{ label: "Extension issues", diagnostics: extensionDiagnostics },
|
|
1469
|
+
{ label: "Theme conflicts", diagnostics: this.session.resourceLoader.getThemes().diagnostics },
|
|
1470
|
+
].filter((group) => group.diagnostics.length > 0);
|
|
1471
|
+
}
|
|
1472
|
+
/**
|
|
1473
|
+
* The counted line in the ruled band under the mark.
|
|
1474
|
+
*
|
|
1475
|
+
* The startup screen used to print all 152 skill names across fifteen rows.
|
|
1476
|
+
* A count answers the only question that screen can usefully answer, which
|
|
1477
|
+
* is what got loaded; the names live behind /resources now.
|
|
1478
|
+
*/
|
|
1479
|
+
/**
|
|
1480
|
+
* The sigil row under the hint: how to reach commands, bash, and files.
|
|
1481
|
+
*
|
|
1482
|
+
* These are the only three characters that change what the composer does, so
|
|
1483
|
+
* they take the accent and everything around them stays quiet.
|
|
1484
|
+
*/
|
|
1485
|
+
/** Live keybindings under the autocomplete dropdown. */
|
|
1486
|
+
buildAutocompleteFooter() {
|
|
1487
|
+
const separator = theme.fg("borderMuted", this.settingsManager.getSymbolPreset() === "ascii" ? " - " : " · ");
|
|
1488
|
+
return [
|
|
1489
|
+
rawKeyHint(`${keyText("tui.select.up")}/${keyText("tui.select.down")}`, "move"),
|
|
1490
|
+
keyHint("tui.select.confirm", "select"),
|
|
1491
|
+
keyHint("tui.input.tab", "complete"),
|
|
1492
|
+
keyHint("tui.select.cancel", "dismiss"),
|
|
1493
|
+
].join(separator);
|
|
1494
|
+
}
|
|
1495
|
+
buildStartupShortcuts() {
|
|
1496
|
+
const entry = (sigil, label) => `${theme.fg("accent", sigil)} ${theme.fg("muted", label)}`;
|
|
1497
|
+
return [
|
|
1498
|
+
entry("/", "commands"),
|
|
1499
|
+
entry("!", "bash"),
|
|
1500
|
+
entry("@", "files"),
|
|
1501
|
+
`${theme.fg("dim", keyText("app.interrupt"))} ${theme.fg("muted", "interrupt")}`,
|
|
1502
|
+
].join(" ");
|
|
1503
|
+
}
|
|
1504
|
+
buildStartupInventory() {
|
|
1505
|
+
const counted = (total, singular) => `${total} ${singular}${total === 1 ? "" : "s"}`;
|
|
1506
|
+
const parts = [];
|
|
1507
|
+
const skills = this.session.resourceLoader.getSkills().skills.length;
|
|
1508
|
+
const extensions = this.session.resourceLoader.getExtensions().extensions.filter((e) => !e.hidden).length;
|
|
1509
|
+
const prompts = this.session.resourceLoader.getPrompts().prompts.length;
|
|
1510
|
+
if (skills > 0)
|
|
1511
|
+
parts.push(theme.fg("muted", counted(skills, "skill")));
|
|
1512
|
+
if (extensions > 0)
|
|
1513
|
+
parts.push(theme.fg("muted", counted(extensions, "extension")));
|
|
1514
|
+
if (prompts > 0)
|
|
1515
|
+
parts.push(theme.fg("muted", counted(prompts, "prompt")));
|
|
1516
|
+
const diagnostics = this.collectDiagnosticGroups().flatMap((group) => group.diagnostics);
|
|
1517
|
+
const errors = diagnostics.filter((diagnostic) => diagnostic.type === "error").length;
|
|
1518
|
+
const conflicts = diagnostics.length - errors;
|
|
1519
|
+
if (errors > 0)
|
|
1520
|
+
parts.push(theme.fg("error", counted(errors, "error")));
|
|
1521
|
+
if (conflicts > 0)
|
|
1522
|
+
parts.push(theme.fg("warning", counted(conflicts, "conflict")));
|
|
1523
|
+
if (parts.length === 0)
|
|
1524
|
+
return undefined;
|
|
1525
|
+
const ascii = this.settingsManager.getSymbolPreset() === "ascii";
|
|
1526
|
+
const separator = theme.fg("borderMuted", ascii ? " - " : " · ");
|
|
1527
|
+
return parts.join(separator);
|
|
1362
1528
|
}
|
|
1363
1529
|
/**
|
|
1364
1530
|
* Initialize the extension system with TUI-based UI context.
|
|
@@ -1446,7 +1612,7 @@ export class InteractiveMode {
|
|
|
1446
1612
|
this.applyFullscreenScrollbarSetting();
|
|
1447
1613
|
this.footer.setSession(this.session);
|
|
1448
1614
|
this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
|
|
1449
|
-
this.refreshFooterPermissionMode();
|
|
1615
|
+
void this.refreshFooterPermissionMode();
|
|
1450
1616
|
this.footerDataProvider.setCwd(this.sessionManager.getCwd());
|
|
1451
1617
|
this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
|
|
1452
1618
|
this.outputPad = this.settingsManager.getOutputPad();
|
|
@@ -1489,7 +1655,7 @@ export class InteractiveMode {
|
|
|
1489
1655
|
const message = error instanceof Error ? error.message : String(error);
|
|
1490
1656
|
this.showError(`${prefix}: ${message}`);
|
|
1491
1657
|
stopThemeWatcher();
|
|
1492
|
-
this.stop();
|
|
1658
|
+
this.stop("transcript");
|
|
1493
1659
|
process.exit(1);
|
|
1494
1660
|
}
|
|
1495
1661
|
renderCurrentSessionState() {
|
|
@@ -1499,9 +1665,14 @@ export class InteractiveMode {
|
|
|
1499
1665
|
this.compactionQueuedMessages = [];
|
|
1500
1666
|
this.streamingComponent = undefined;
|
|
1501
1667
|
this.streamingMessage = undefined;
|
|
1502
|
-
this.
|
|
1668
|
+
this.clearPendingTools();
|
|
1503
1669
|
this.renderInitialMessages();
|
|
1504
1670
|
}
|
|
1671
|
+
clearPendingTools() {
|
|
1672
|
+
for (const component of this.pendingTools.values())
|
|
1673
|
+
component.dispose();
|
|
1674
|
+
this.pendingTools.clear();
|
|
1675
|
+
}
|
|
1505
1676
|
/**
|
|
1506
1677
|
* Get a registered tool definition by name (for custom rendering).
|
|
1507
1678
|
*/
|
|
@@ -1884,7 +2055,7 @@ export class InteractiveMode {
|
|
|
1884
2055
|
/**
|
|
1885
2056
|
* Show a selector for extensions.
|
|
1886
2057
|
*/
|
|
1887
|
-
showExtensionSelector(title, options, opts) {
|
|
2058
|
+
showExtensionSelector(title, options, opts, enableSearch = false) {
|
|
1888
2059
|
return new Promise((resolve) => {
|
|
1889
2060
|
if (opts?.signal?.aborted) {
|
|
1890
2061
|
resolve(undefined);
|
|
@@ -1903,7 +2074,12 @@ export class InteractiveMode {
|
|
|
1903
2074
|
opts?.signal?.removeEventListener("abort", onAbort);
|
|
1904
2075
|
this.hideExtensionSelector();
|
|
1905
2076
|
resolve(undefined);
|
|
1906
|
-
}, {
|
|
2077
|
+
}, {
|
|
2078
|
+
tui: this.ui,
|
|
2079
|
+
timeout: opts?.timeout,
|
|
2080
|
+
onToggleToolsExpanded: () => this.toggleToolOutputExpansion(),
|
|
2081
|
+
enableSearch,
|
|
2082
|
+
});
|
|
1907
2083
|
this.disposeActiveSelector();
|
|
1908
2084
|
this.editorContainer.clear();
|
|
1909
2085
|
this.editorContainer.addChild(this.extensionSelector);
|
|
@@ -1922,6 +2098,40 @@ export class InteractiveMode {
|
|
|
1922
2098
|
this.ui.setFocus(this.editor);
|
|
1923
2099
|
this.ui.requestRender();
|
|
1924
2100
|
}
|
|
2101
|
+
async showConfigurationIndex() {
|
|
2102
|
+
const choice = await this.showExtensionSelector("Configuration", [
|
|
2103
|
+
"Settings",
|
|
2104
|
+
"Model",
|
|
2105
|
+
"Model cycling",
|
|
2106
|
+
"Provider login",
|
|
2107
|
+
"Provider logout",
|
|
2108
|
+
"Project trust",
|
|
2109
|
+
"Resources, extensions, and MCP adapters",
|
|
2110
|
+
], undefined, true);
|
|
2111
|
+
switch (choice) {
|
|
2112
|
+
case "Settings":
|
|
2113
|
+
await this.showSettingsSelector();
|
|
2114
|
+
break;
|
|
2115
|
+
case "Model":
|
|
2116
|
+
this.showModelSelector();
|
|
2117
|
+
break;
|
|
2118
|
+
case "Model cycling":
|
|
2119
|
+
await this.showModelsSelector();
|
|
2120
|
+
break;
|
|
2121
|
+
case "Provider login":
|
|
2122
|
+
await this.handleLoginCommand();
|
|
2123
|
+
break;
|
|
2124
|
+
case "Provider logout":
|
|
2125
|
+
this.showOAuthSelector("logout");
|
|
2126
|
+
break;
|
|
2127
|
+
case "Project trust":
|
|
2128
|
+
this.showTrustSelector();
|
|
2129
|
+
break;
|
|
2130
|
+
case "Resources, extensions, and MCP adapters":
|
|
2131
|
+
this.showStatus("Run apex-code config to manage resources, extensions, and MCP adapters.");
|
|
2132
|
+
break;
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
1925
2135
|
/**
|
|
1926
2136
|
* Show a confirmation dialog for extensions.
|
|
1927
2137
|
*/
|
|
@@ -2233,6 +2443,8 @@ export class InteractiveMode {
|
|
|
2233
2443
|
const wasBashMode = this.isBashMode;
|
|
2234
2444
|
this.isBashMode = text.trimStart().startsWith("!");
|
|
2235
2445
|
if (wasBashMode !== this.isBashMode) {
|
|
2446
|
+
if (this.isBashMode)
|
|
2447
|
+
this.offerFirstUseHint("bash");
|
|
2236
2448
|
this.updateEditorBorderColor();
|
|
2237
2449
|
}
|
|
2238
2450
|
};
|
|
@@ -2281,6 +2493,10 @@ export class InteractiveMode {
|
|
|
2281
2493
|
// Silently ignore clipboard errors (may not have permission, etc.)
|
|
2282
2494
|
}
|
|
2283
2495
|
}
|
|
2496
|
+
handleStartupSubmit(text) {
|
|
2497
|
+
this.editor.setText(text);
|
|
2498
|
+
this.showStatus("Startup is still in progress");
|
|
2499
|
+
}
|
|
2284
2500
|
setupEditorSubmitHandler() {
|
|
2285
2501
|
this.defaultEditor.onSubmit = async (text) => {
|
|
2286
2502
|
text = text.trim();
|
|
@@ -2294,6 +2510,11 @@ export class InteractiveMode {
|
|
|
2294
2510
|
this.pendingPermissionModeWrite = undefined;
|
|
2295
2511
|
}
|
|
2296
2512
|
// Handle commands
|
|
2513
|
+
if (text === "/config") {
|
|
2514
|
+
this.editor.setText("");
|
|
2515
|
+
await this.showConfigurationIndex();
|
|
2516
|
+
return;
|
|
2517
|
+
}
|
|
2297
2518
|
if (text === "/settings") {
|
|
2298
2519
|
this.editor.setText("");
|
|
2299
2520
|
await this.showSettingsSelector();
|
|
@@ -2310,6 +2531,12 @@ export class InteractiveMode {
|
|
|
2310
2531
|
await this.handleModelCommand(searchTerm);
|
|
2311
2532
|
return;
|
|
2312
2533
|
}
|
|
2534
|
+
if (text === "/thinking" || text.startsWith("/thinking ")) {
|
|
2535
|
+
const searchTerm = text.startsWith("/thinking ") ? text.slice(10).trim() : undefined;
|
|
2536
|
+
this.editor.setText("");
|
|
2537
|
+
this.handleThinkingCommand(searchTerm);
|
|
2538
|
+
return;
|
|
2539
|
+
}
|
|
2313
2540
|
if (text === "/export" || text.startsWith("/export ")) {
|
|
2314
2541
|
await this.handleExportCommand(text);
|
|
2315
2542
|
this.editor.setText("");
|
|
@@ -2345,6 +2572,11 @@ export class InteractiveMode {
|
|
|
2345
2572
|
this.editor.setText("");
|
|
2346
2573
|
return;
|
|
2347
2574
|
}
|
|
2575
|
+
if (text === "/resources") {
|
|
2576
|
+
this.showLoadedResources({ force: true });
|
|
2577
|
+
this.editor.setText("");
|
|
2578
|
+
return;
|
|
2579
|
+
}
|
|
2348
2580
|
if (text === "/hotkeys") {
|
|
2349
2581
|
this.handleHotkeysCommand();
|
|
2350
2582
|
this.editor.setText("");
|
|
@@ -2485,7 +2717,7 @@ export class InteractiveMode {
|
|
|
2485
2717
|
this.footer.invalidate();
|
|
2486
2718
|
switch (event.type) {
|
|
2487
2719
|
case "agent_start":
|
|
2488
|
-
this.
|
|
2720
|
+
this.clearPendingTools();
|
|
2489
2721
|
if (this.settingsManager.getShowTerminalProgress()) {
|
|
2490
2722
|
this.ui.terminal.setProgress(true);
|
|
2491
2723
|
}
|
|
@@ -2537,6 +2769,7 @@ export class InteractiveMode {
|
|
|
2537
2769
|
this.streamingMessage = event.message;
|
|
2538
2770
|
this.chatContainer.addChild(this.streamingComponent);
|
|
2539
2771
|
this.streamingComponent.updateContent(this.streamingMessage, true);
|
|
2772
|
+
this.updateEditorBorderColor();
|
|
2540
2773
|
this.ui.requestRender();
|
|
2541
2774
|
}
|
|
2542
2775
|
break;
|
|
@@ -2554,6 +2787,7 @@ export class InteractiveMode {
|
|
|
2554
2787
|
component.setExpanded(this.toolOutputExpanded);
|
|
2555
2788
|
this.chatContainer.addChild(component);
|
|
2556
2789
|
this.pendingTools.set(content.id, component);
|
|
2790
|
+
this.offerFirstUseHint("tool-expand");
|
|
2557
2791
|
}
|
|
2558
2792
|
else {
|
|
2559
2793
|
const component = this.pendingTools.get(content.id);
|
|
@@ -2591,7 +2825,7 @@ export class InteractiveMode {
|
|
|
2591
2825
|
isError: true,
|
|
2592
2826
|
});
|
|
2593
2827
|
}
|
|
2594
|
-
this.
|
|
2828
|
+
this.clearPendingTools();
|
|
2595
2829
|
}
|
|
2596
2830
|
else {
|
|
2597
2831
|
// Args are now complete - trigger diff computation for edit tools
|
|
@@ -2603,6 +2837,7 @@ export class InteractiveMode {
|
|
|
2603
2837
|
this.streamingComponent = undefined;
|
|
2604
2838
|
this.streamingMessage = undefined;
|
|
2605
2839
|
this.footer.invalidate();
|
|
2840
|
+
this.updateEditorBorderColor();
|
|
2606
2841
|
}
|
|
2607
2842
|
this.ui.requestRender();
|
|
2608
2843
|
break;
|
|
@@ -2651,7 +2886,7 @@ export class InteractiveMode {
|
|
|
2651
2886
|
this.streamingComponent = undefined;
|
|
2652
2887
|
this.streamingMessage = undefined;
|
|
2653
2888
|
}
|
|
2654
|
-
this.
|
|
2889
|
+
this.clearPendingTools();
|
|
2655
2890
|
this.ui.requestRender();
|
|
2656
2891
|
break;
|
|
2657
2892
|
case "agent_settled":
|
|
@@ -2688,9 +2923,21 @@ export class InteractiveMode {
|
|
|
2688
2923
|
}
|
|
2689
2924
|
}
|
|
2690
2925
|
else if (event.result) {
|
|
2926
|
+
const entries = this.sessionManager.buildContextEntries();
|
|
2927
|
+
if (entries[0]?.type !== "compaction") {
|
|
2928
|
+
throw new Error("Completed compaction is missing from the session context");
|
|
2929
|
+
}
|
|
2691
2930
|
this.chatContainer.clear();
|
|
2692
|
-
|
|
2931
|
+
// The latest compaction is prepended for model context; append it below at its chronological position.
|
|
2932
|
+
this.renderSessionEntries(entries.slice(1));
|
|
2693
2933
|
this.addMessageToChat(createCompactionSummaryMessage(event.result.summary, event.result.tokensBefore, new Date().toISOString()));
|
|
2934
|
+
if (event.result.usage) {
|
|
2935
|
+
this.addCompactionCostNotice({
|
|
2936
|
+
type: "compaction_cost",
|
|
2937
|
+
kind: "compaction",
|
|
2938
|
+
usage: event.result.usage,
|
|
2939
|
+
});
|
|
2940
|
+
}
|
|
2694
2941
|
this.footer.invalidate();
|
|
2695
2942
|
}
|
|
2696
2943
|
else if (event.errorMessage) {
|
|
@@ -2763,6 +3010,19 @@ export class InteractiveMode {
|
|
|
2763
3010
|
: message.content.filter((c) => c.type === "text");
|
|
2764
3011
|
return textBlocks.map((c) => c.text).join("");
|
|
2765
3012
|
}
|
|
3013
|
+
/** Show a managed-tool status update in the chat. */
|
|
3014
|
+
showManagedToolStatus(status) {
|
|
3015
|
+
if (!this.managedToolStatusStarted) {
|
|
3016
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3017
|
+
this.managedToolStatusStarted = true;
|
|
3018
|
+
}
|
|
3019
|
+
const message = status.type === "warning" ? `Warning: ${status.message}` : status.message;
|
|
3020
|
+
const color = status.type === "warning" ? "warning" : "dim";
|
|
3021
|
+
this.chatContainer.addChild(new Text(theme.fg(color, message), 1, 0));
|
|
3022
|
+
this.lastStatusSpacer = undefined;
|
|
3023
|
+
this.lastStatusText = undefined;
|
|
3024
|
+
this.ui.requestRender();
|
|
3025
|
+
}
|
|
2766
3026
|
/**
|
|
2767
3027
|
* Show a status message in the chat.
|
|
2768
3028
|
*
|
|
@@ -2883,7 +3143,7 @@ export class InteractiveMode {
|
|
|
2883
3143
|
}
|
|
2884
3144
|
}
|
|
2885
3145
|
renderSessionItems(items, options = {}) {
|
|
2886
|
-
this.
|
|
3146
|
+
this.clearPendingTools();
|
|
2887
3147
|
const renderedPendingTools = new Map();
|
|
2888
3148
|
// Cache-miss notices are not persisted; re-derive them from the full entry
|
|
2889
3149
|
// list and re-inject them after the assistant messages that paid for them.
|
|
@@ -2899,6 +3159,10 @@ export class InteractiveMode {
|
|
|
2899
3159
|
this.addCustomEntryToChat(item);
|
|
2900
3160
|
continue;
|
|
2901
3161
|
}
|
|
3162
|
+
if (isCompactionCostNotice(item)) {
|
|
3163
|
+
this.addCompactionCostNotice(item);
|
|
3164
|
+
continue;
|
|
3165
|
+
}
|
|
2902
3166
|
const message = item;
|
|
2903
3167
|
// Assistant messages need special handling for tool calls
|
|
2904
3168
|
if (message.role === "assistant") {
|
|
@@ -2966,10 +3230,28 @@ export class InteractiveMode {
|
|
|
2966
3230
|
if (entry.type === "custom") {
|
|
2967
3231
|
return [entry];
|
|
2968
3232
|
}
|
|
2969
|
-
|
|
3233
|
+
const messages = sessionEntryToContextMessages(entry);
|
|
3234
|
+
if ((entry.type === "compaction" || entry.type === "branch_summary") && entry.usage && messages.length > 0) {
|
|
3235
|
+
return [...messages, { type: "compaction_cost", kind: entry.type, usage: entry.usage }];
|
|
3236
|
+
}
|
|
3237
|
+
return messages;
|
|
2970
3238
|
});
|
|
2971
3239
|
this.renderSessionItems(items, options);
|
|
2972
3240
|
}
|
|
3241
|
+
/**
|
|
3242
|
+
* Render billing usage for a compaction or branch summary. The notice is derived
|
|
3243
|
+
* from persisted summary usage and is not stored as a separate session entry.
|
|
3244
|
+
*/
|
|
3245
|
+
addCompactionCostNotice(notice) {
|
|
3246
|
+
if (!this.settingsManager.getShowCacheMissNotices())
|
|
3247
|
+
return;
|
|
3248
|
+
const { usage } = notice;
|
|
3249
|
+
const tokens = usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
|
|
3250
|
+
const cost = usage.cost.total >= 0.01 ? ` (~$${usage.cost.total.toFixed(2)})` : "";
|
|
3251
|
+
const label = notice.kind === "compaction" ? "Compaction" : "Branch summary";
|
|
3252
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3253
|
+
this.chatContainer.addChild(new Text(theme.fg("warning", `${label}: ${formatTokens(tokens)} tokens billed${cost}`), 1, 0));
|
|
3254
|
+
}
|
|
2973
3255
|
/**
|
|
2974
3256
|
* Show a transcript notice when a completed assistant message paid for a
|
|
2975
3257
|
* significant cache miss. Only states observable facts: the miss itself,
|
|
@@ -3241,6 +3523,7 @@ export class InteractiveMode {
|
|
|
3241
3523
|
this.editor.addToHistory?.(text);
|
|
3242
3524
|
this.editor.setText("");
|
|
3243
3525
|
await this.session.prompt(text, { streamingBehavior: "followUp" });
|
|
3526
|
+
this.offerFirstUseHint("queue");
|
|
3244
3527
|
this.updatePendingMessagesDisplay();
|
|
3245
3528
|
this.ui.requestRender();
|
|
3246
3529
|
}
|
|
@@ -3260,6 +3543,7 @@ export class InteractiveMode {
|
|
|
3260
3543
|
}
|
|
3261
3544
|
}
|
|
3262
3545
|
updateEditorBorderColor() {
|
|
3546
|
+
this.defaultEditor.setModeLabel(this.isBashMode ? "bash" : this.session.isStreaming ? "busy" : undefined);
|
|
3263
3547
|
if (this.isBashMode) {
|
|
3264
3548
|
this.editor.borderColor = theme.getBashModeBorderColor();
|
|
3265
3549
|
}
|
|
@@ -3320,6 +3604,7 @@ export class InteractiveMode {
|
|
|
3320
3604
|
this.showStatus(`Tool output: ${expanded ? "expanded" : "collapsed"}`);
|
|
3321
3605
|
}
|
|
3322
3606
|
toggleThinkingBlockVisibility() {
|
|
3607
|
+
this.offerFirstUseHint("thinking");
|
|
3323
3608
|
this.hideThinkingBlock = !this.hideThinkingBlock;
|
|
3324
3609
|
this.settingsManager.setHideThinkingBlock(this.hideThinkingBlock);
|
|
3325
3610
|
// Rebuild chat from session messages
|
|
@@ -3610,11 +3895,12 @@ export class InteractiveMode {
|
|
|
3610
3895
|
* between two sessions in one process. A footer left showing the old value
|
|
3611
3896
|
* would under-report bypassPermissions, which is the one direction that matters.
|
|
3612
3897
|
*/
|
|
3613
|
-
refreshFooterPermissionMode() {
|
|
3614
|
-
|
|
3615
|
-
|
|
3898
|
+
async refreshFooterPermissionMode() {
|
|
3899
|
+
const resolution = await this.session.getPermissionMode();
|
|
3900
|
+
this.footer.setPermissionMode(resolution?.mode ?? "default");
|
|
3901
|
+
if (this.isInitialized) {
|
|
3616
3902
|
this.ui.requestRender();
|
|
3617
|
-
}
|
|
3903
|
+
}
|
|
3618
3904
|
}
|
|
3619
3905
|
/**
|
|
3620
3906
|
* Writes the mode to user scope, then reports what is actually in force. The
|
|
@@ -3651,8 +3937,14 @@ export class InteractiveMode {
|
|
|
3651
3937
|
const permissionMode = await this.session.getPermissionMode();
|
|
3652
3938
|
this.showSelector((done) => {
|
|
3653
3939
|
let selector;
|
|
3940
|
+
const defaultProvider = this.settingsManager.getDefaultProvider();
|
|
3941
|
+
const defaultModelId = this.settingsManager.getDefaultModel();
|
|
3942
|
+
const defaultModel = defaultProvider && defaultModelId ? `${defaultProvider}/${defaultModelId}` : "not set";
|
|
3654
3943
|
selector = new SettingsSelectorComponent({
|
|
3655
3944
|
autoCompact: this.session.autoCompactionEnabled,
|
|
3945
|
+
defaultModel,
|
|
3946
|
+
currentModel: this.session.model,
|
|
3947
|
+
availableDefaultModels: this.session.modelRuntime.getAvailableSnapshot(),
|
|
3656
3948
|
showImages: this.settingsManager.getShowImages(),
|
|
3657
3949
|
imageWidthCells: this.settingsManager.getImageWidthCells(),
|
|
3658
3950
|
autoResizeImages: this.settingsManager.getImageAutoResize(),
|
|
@@ -3662,9 +3954,10 @@ export class InteractiveMode {
|
|
|
3662
3954
|
followUpMode: this.session.followUpMode,
|
|
3663
3955
|
transport: this.settingsManager.getTransport(),
|
|
3664
3956
|
httpIdleTimeoutMs: this.settingsManager.getHttpIdleTimeoutMs(),
|
|
3665
|
-
thinkingLevel: this.
|
|
3666
|
-
availableThinkingLevels:
|
|
3667
|
-
|
|
3957
|
+
thinkingLevel: this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL,
|
|
3958
|
+
availableThinkingLevels: [...THINKING_LEVEL_OPTIONS],
|
|
3959
|
+
modelThinkingLevels: this.settingsManager.getAllModelThinkingLevels(),
|
|
3960
|
+
currentTheme: this.themeController.getThemeSelection() || "dark",
|
|
3668
3961
|
terminalTheme: this.themeController.getTerminalTheme(),
|
|
3669
3962
|
availableThemes: getAvailableThemes(),
|
|
3670
3963
|
hideThinkingBlock: this.hideThinkingBlock,
|
|
@@ -3684,6 +3977,7 @@ export class InteractiveMode {
|
|
|
3684
3977
|
clearOnShrink: this.settingsManager.getClearOnShrink(),
|
|
3685
3978
|
showTerminalProgress: this.settingsManager.getShowTerminalProgress(),
|
|
3686
3979
|
tuiMode: this.ui.mode,
|
|
3980
|
+
fullscreenExitOutput: this.settingsManager.getFullscreenExitOutput(),
|
|
3687
3981
|
fullscreenScrollbar: this.settingsManager.getFullscreenScrollbar(),
|
|
3688
3982
|
warnings: this.settingsManager.getWarnings(),
|
|
3689
3983
|
}, {
|
|
@@ -3732,14 +4026,30 @@ export class InteractiveMode {
|
|
|
3732
4026
|
configureHttpDispatcher(timeoutMs);
|
|
3733
4027
|
this.showStatus(`HTTP idle timeout: ${formatHttpIdleTimeoutMs(timeoutMs)}`);
|
|
3734
4028
|
},
|
|
3735
|
-
|
|
3736
|
-
this.
|
|
3737
|
-
|
|
3738
|
-
this.
|
|
4029
|
+
onModelThinkingLevelChange: (provider, modelId, level) => {
|
|
4030
|
+
this.settingsManager.setModelThinkingLevel(provider, modelId, level);
|
|
4031
|
+
// If the override is for the current model, apply it to the session too
|
|
4032
|
+
const current = this.session.model;
|
|
4033
|
+
if (current && current.provider === provider && current.id === modelId) {
|
|
4034
|
+
this.session.setThinkingLevel(level);
|
|
4035
|
+
this.footer.invalidate();
|
|
4036
|
+
this.updateEditorBorderColor();
|
|
4037
|
+
}
|
|
4038
|
+
},
|
|
4039
|
+
onModelThinkingLevelRemove: (provider, modelId) => {
|
|
4040
|
+
this.settingsManager.removeModelThinkingLevel(provider, modelId);
|
|
4041
|
+
// If the override was for the current model, revert to global default
|
|
4042
|
+
const current = this.session.model;
|
|
4043
|
+
if (current && current.provider === provider && current.id === modelId) {
|
|
4044
|
+
const globalDefault = this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
|
|
4045
|
+
this.session.setThinkingLevel(globalDefault);
|
|
4046
|
+
this.footer.invalidate();
|
|
4047
|
+
this.updateEditorBorderColor();
|
|
4048
|
+
}
|
|
3739
4049
|
},
|
|
3740
4050
|
onThemeChange: (themeSetting) => {
|
|
3741
4051
|
this.settingsManager.setTheme(themeSetting);
|
|
3742
|
-
void this.themeController.
|
|
4052
|
+
void this.themeController.setThemeSetting(themeSetting);
|
|
3743
4053
|
},
|
|
3744
4054
|
onThemePreview: (themeName) => this.themeController.preview(themeName),
|
|
3745
4055
|
onHideThinkingBlockChange: (hidden) => {
|
|
@@ -3841,6 +4151,9 @@ export class InteractiveMode {
|
|
|
3841
4151
|
this.statusContainer.clear();
|
|
3842
4152
|
this.showStatus(`TUI mode: ${mode}`);
|
|
3843
4153
|
},
|
|
4154
|
+
onFullscreenExitOutputChange: (output) => {
|
|
4155
|
+
this.settingsManager.setFullscreenExitOutput(output);
|
|
4156
|
+
},
|
|
3844
4157
|
onFullscreenScrollbarChange: (mode) => {
|
|
3845
4158
|
this.settingsManager.setFullscreenScrollbar(mode);
|
|
3846
4159
|
this.applyFullscreenScrollbarSetting();
|
|
@@ -3856,6 +4169,44 @@ export class InteractiveMode {
|
|
|
3856
4169
|
return { component: selector, focus: selector.getSettingsList() };
|
|
3857
4170
|
});
|
|
3858
4171
|
}
|
|
4172
|
+
handleThinkingCommand(searchTerm) {
|
|
4173
|
+
const availableLevels = this.session.getAvailableThinkingLevels();
|
|
4174
|
+
if (!searchTerm) {
|
|
4175
|
+
this.showThinkingSelector();
|
|
4176
|
+
return;
|
|
4177
|
+
}
|
|
4178
|
+
const normalized = searchTerm.trim().toLowerCase();
|
|
4179
|
+
const level = availableLevels.find((candidate) => candidate.toLowerCase() === normalized);
|
|
4180
|
+
if (!level) {
|
|
4181
|
+
this.showError(`Unknown thinking level "${searchTerm}". Available levels: ${availableLevels.join(", ")}.`);
|
|
4182
|
+
return;
|
|
4183
|
+
}
|
|
4184
|
+
this.selectThinkingLevel(level, false);
|
|
4185
|
+
}
|
|
4186
|
+
selectThinkingLevel(level, persist) {
|
|
4187
|
+
try {
|
|
4188
|
+
this.session.setThinkingLevel(level, { persist });
|
|
4189
|
+
this.footer.invalidate();
|
|
4190
|
+
this.updateEditorBorderColor();
|
|
4191
|
+
this.showStatus(persist ? `Default thinking level: ${level}` : `Thinking level: ${level}`);
|
|
4192
|
+
}
|
|
4193
|
+
catch (error) {
|
|
4194
|
+
this.showError(error instanceof Error ? error.message : String(error));
|
|
4195
|
+
}
|
|
4196
|
+
}
|
|
4197
|
+
showThinkingSelector() {
|
|
4198
|
+
this.showSelector((done) => {
|
|
4199
|
+
const selectLevel = (level, persist) => {
|
|
4200
|
+
this.selectThinkingLevel(level, persist);
|
|
4201
|
+
done();
|
|
4202
|
+
};
|
|
4203
|
+
const selector = new ThinkingSelectorComponent(this.session.thinkingLevel ?? DEFAULT_THINKING_LEVEL, this.session.getAvailableThinkingLevels(), (level) => selectLevel(level, false), () => {
|
|
4204
|
+
done();
|
|
4205
|
+
this.ui.requestRender();
|
|
4206
|
+
}, (level) => selectLevel(level, true), this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);
|
|
4207
|
+
return { component: selector, focus: selector };
|
|
4208
|
+
});
|
|
4209
|
+
}
|
|
3859
4210
|
async handleModelCommand(searchTerm) {
|
|
3860
4211
|
if (!searchTerm) {
|
|
3861
4212
|
this.showModelSelector();
|
|
@@ -3864,7 +4215,7 @@ export class InteractiveMode {
|
|
|
3864
4215
|
const model = await this.findExactModelMatch(searchTerm);
|
|
3865
4216
|
if (model) {
|
|
3866
4217
|
try {
|
|
3867
|
-
await this.session.setModel(model);
|
|
4218
|
+
await this.session.setModel(model, { persist: false });
|
|
3868
4219
|
this.footer.invalidate();
|
|
3869
4220
|
this.updateEditorBorderColor();
|
|
3870
4221
|
this.showStatus(`Model: ${model.id}`);
|
|
@@ -3893,7 +4244,7 @@ export class InteractiveMode {
|
|
|
3893
4244
|
controller.abort();
|
|
3894
4245
|
}, 15_000);
|
|
3895
4246
|
try {
|
|
3896
|
-
const result = await this.session.modelRuntime
|
|
4247
|
+
const result = await refreshModelCatalogs(this.session.modelRuntime, controller.signal);
|
|
3897
4248
|
if (result.aborted && timedOut) {
|
|
3898
4249
|
this.showWarning("Model refresh timed out; searching cached models.");
|
|
3899
4250
|
}
|
|
@@ -3981,7 +4332,7 @@ export class InteractiveMode {
|
|
|
3981
4332
|
onSelect: (selection) => {
|
|
3982
4333
|
trustStore.setMany(selection.updates);
|
|
3983
4334
|
done();
|
|
3984
|
-
this.showStatus(`Saved trust decision: ${selection.trusted ? "trusted" : "untrusted"}. Restart
|
|
4335
|
+
this.showStatus(`Saved trust decision: ${selection.trusted ? "trusted" : "untrusted"}. Restart ${APP_NAME} for this to take effect.`);
|
|
3985
4336
|
},
|
|
3986
4337
|
onCancel: () => {
|
|
3987
4338
|
done();
|
|
@@ -3993,13 +4344,13 @@ export class InteractiveMode {
|
|
|
3993
4344
|
}
|
|
3994
4345
|
showModelSelector(initialSearchInput) {
|
|
3995
4346
|
this.showSelector((done) => {
|
|
3996
|
-
const
|
|
4347
|
+
const selectModel = async (model, persist) => {
|
|
3997
4348
|
try {
|
|
3998
|
-
await this.session.setModel(model);
|
|
4349
|
+
await this.session.setModel(model, { persist });
|
|
3999
4350
|
this.footer.invalidate();
|
|
4000
4351
|
this.updateEditorBorderColor();
|
|
4001
4352
|
done();
|
|
4002
|
-
this.showStatus(`Model: ${model.id}`);
|
|
4353
|
+
this.showStatus(persist ? `Default model: ${model.provider}/${model.id}` : `Model: ${model.id}`);
|
|
4003
4354
|
void this.maybeWarnAboutAnthropicSubscriptionAuth(model);
|
|
4004
4355
|
this.checkDaxnutsEasterEgg(model);
|
|
4005
4356
|
}
|
|
@@ -4007,10 +4358,13 @@ export class InteractiveMode {
|
|
|
4007
4358
|
done();
|
|
4008
4359
|
this.showError(error instanceof Error ? error.message : String(error));
|
|
4009
4360
|
}
|
|
4010
|
-
}
|
|
4361
|
+
};
|
|
4362
|
+
const defaultProvider = this.settingsManager.getDefaultProvider();
|
|
4363
|
+
const defaultModel = this.settingsManager.getDefaultModel();
|
|
4364
|
+
const selector = new ModelSelectorComponent(this.ui, this.session.model, this.session.modelRuntime, this.session.scopedModels, (model) => selectModel(model, false), () => {
|
|
4011
4365
|
done();
|
|
4012
4366
|
this.ui.requestRender();
|
|
4013
|
-
}, initialSearchInput);
|
|
4367
|
+
}, initialSearchInput, (model) => selectModel(model, true), defaultProvider && defaultModel ? { provider: defaultProvider, id: defaultModel } : undefined);
|
|
4014
4368
|
return { component: selector, focus: selector, dispose: () => selector.dispose() };
|
|
4015
4369
|
});
|
|
4016
4370
|
}
|
|
@@ -4081,8 +4435,7 @@ export class InteractiveMode {
|
|
|
4081
4435
|
this.ui.requestRender();
|
|
4082
4436
|
},
|
|
4083
4437
|
});
|
|
4084
|
-
void this.session.modelRuntime
|
|
4085
|
-
.refresh({ signal: controller.signal })
|
|
4438
|
+
void refreshModelCatalogs(this.session.modelRuntime, controller.signal)
|
|
4086
4439
|
.then((result) => {
|
|
4087
4440
|
if (disposed)
|
|
4088
4441
|
return;
|
|
@@ -4567,7 +4920,11 @@ export class InteractiveMode {
|
|
|
4567
4920
|
if (isUnknownModel(previousModel)) {
|
|
4568
4921
|
const availableModels = this.session.modelRuntime.getAvailableSnapshot();
|
|
4569
4922
|
const providerModels = availableModels.filter((model) => model.provider === providerId);
|
|
4570
|
-
|
|
4923
|
+
// Matches LLAMA_PROVIDER_ID from extensions/llama/provider.ts; kept inline to avoid coupling interactive mode to the built-in extension.
|
|
4924
|
+
if (providerId === "llama.cpp") {
|
|
4925
|
+
selectionError = llamaCppPostLoginGuidance(actionLabel, providerModels.length);
|
|
4926
|
+
}
|
|
4927
|
+
else if (!hasDefaultModelProvider(providerId)) {
|
|
4571
4928
|
selectionError = `${actionLabel}, but no default model is configured for provider "${providerId}". Use /model to select a model.`;
|
|
4572
4929
|
}
|
|
4573
4930
|
else if (providerModels.length === 0) {
|
|
@@ -4581,7 +4938,7 @@ export class InteractiveMode {
|
|
|
4581
4938
|
}
|
|
4582
4939
|
else {
|
|
4583
4940
|
try {
|
|
4584
|
-
await this.session.setModel(selectedModel);
|
|
4941
|
+
await this.session.setModel(selectedModel, { persist: true });
|
|
4585
4942
|
}
|
|
4586
4943
|
catch (error) {
|
|
4587
4944
|
selectedModel = undefined;
|
|
@@ -4873,7 +5230,9 @@ export class InteractiveMode {
|
|
|
4873
5230
|
this.showStatus(`Session exported to: ${filePath}`);
|
|
4874
5231
|
}
|
|
4875
5232
|
else {
|
|
4876
|
-
const filePath = await this.session.exportToHtml(outputPath
|
|
5233
|
+
const filePath = await this.session.exportToHtml(outputPath, {
|
|
5234
|
+
themeName: theme.name,
|
|
5235
|
+
});
|
|
4877
5236
|
this.showStatus(`Session exported to: ${filePath}`);
|
|
4878
5237
|
}
|
|
4879
5238
|
}
|
|
@@ -5415,7 +5774,7 @@ export class InteractiveMode {
|
|
|
5415
5774
|
// Ignore, will be emitted as an event
|
|
5416
5775
|
}
|
|
5417
5776
|
}
|
|
5418
|
-
stop() {
|
|
5777
|
+
stop(fullscreenExitOutput = this.settingsManager.getFullscreenExitOutput()) {
|
|
5419
5778
|
this.disposeActiveSelector();
|
|
5420
5779
|
if (this.settingsManager.getShowTerminalProgress()) {
|
|
5421
5780
|
this.ui.terminal.setProgress(false);
|
|
@@ -5429,7 +5788,7 @@ export class InteractiveMode {
|
|
|
5429
5788
|
this.unsubscribe();
|
|
5430
5789
|
}
|
|
5431
5790
|
if (this.isInitialized) {
|
|
5432
|
-
this.stopInteractiveTui();
|
|
5791
|
+
this.stopInteractiveTui(fullscreenExitOutput);
|
|
5433
5792
|
this.isInitialized = false;
|
|
5434
5793
|
}
|
|
5435
5794
|
this.unregisterSignalHandlers();
|