apex-code 0.4.0 → 0.5.1
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 +280 -6
- package/README.md +624 -0
- package/dist/bun/cli.d.ts +3 -1
- package/dist/bun/cli.d.ts.map +1 -1
- package/dist/bun/cli.js +3 -9
- package/dist/bun/cli.js.map +1 -1
- package/dist/bun/runtime-setup.d.ts +2 -0
- package/dist/bun/runtime-setup.d.ts.map +1 -0
- package/dist/bun/runtime-setup.js +9 -0
- package/dist/bun/runtime-setup.js.map +1 -0
- package/dist/bun/sandbox-env-setup.d.ts +2 -0
- package/dist/bun/sandbox-env-setup.d.ts.map +1 -0
- package/dist/bun/sandbox-env-setup.js +4 -0
- package/dist/bun/sandbox-env-setup.js.map +1 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +16 -5
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config-selector.d.ts.map +1 -1
- package/dist/cli/config-selector.js +2 -1
- package/dist/cli/config-selector.js.map +1 -1
- package/dist/cli/file-processor.d.ts +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli/session-picker.d.ts +1 -1
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/cli/setup.d.ts +2 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +13 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +16 -8
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session.d.ts +85 -15
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +685 -266
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +4 -0
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +55 -0
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bug-report.d.ts +176 -0
- package/dist/core/bug-report.d.ts.map +1 -0
- package/dist/core/bug-report.js +291 -0
- package/dist/core/bug-report.js.map +1 -0
- package/dist/core/cache-stats.d.ts.map +1 -1
- package/dist/core/cache-stats.js +12 -1
- package/dist/core/cache-stats.js.map +1 -1
- package/dist/core/cache-warmer.d.ts +103 -0
- package/dist/core/cache-warmer.d.ts.map +1 -0
- package/dist/core/cache-warmer.js +356 -0
- package/dist/core/cache-warmer.js.map +1 -0
- package/dist/core/compaction/branch-summarization.d.ts +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +4 -3
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +5 -3
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +163 -66
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/context/pipeline.d.ts +8 -8
- package/dist/core/context/pipeline.d.ts.map +1 -1
- package/dist/core/context/pipeline.js +21 -12
- package/dist/core/context/pipeline.js.map +1 -1
- package/dist/core/crash-log.d.ts +27 -0
- package/dist/core/crash-log.d.ts.map +1 -0
- package/dist/core/crash-log.js +139 -0
- package/dist/core/crash-log.js.map +1 -0
- package/dist/core/export-html/template.js +6 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/jiti-loader.d.ts +2 -0
- package/dist/core/extensions/jiti-loader.d.ts.map +1 -0
- package/dist/core/extensions/jiti-loader.js +4 -0
- package/dist/core/extensions/jiti-loader.js.map +1 -0
- package/dist/core/extensions/jiti-static-loader.d.ts +2 -0
- package/dist/core/extensions/jiti-static-loader.d.ts.map +1 -0
- package/dist/core/extensions/jiti-static-loader.js +4 -0
- package/dist/core/extensions/jiti-static-loader.js.map +1 -0
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +40 -59
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +24 -8
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +195 -72
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +142 -55
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/extensions/virtual-modules.d.ts +3 -0
- package/dist/core/extensions/virtual-modules.d.ts.map +1 -0
- package/dist/core/extensions/virtual-modules.js +41 -0
- package/dist/core/extensions/virtual-modules.js.map +1 -0
- package/dist/core/extensions/wrapper.d.ts.map +1 -1
- package/dist/core/extensions/wrapper.js +1 -20
- package/dist/core/extensions/wrapper.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +2 -0
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/keybindings.d.ts +6 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +5 -1
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/messages.d.ts +1 -1
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +1 -0
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-config.d.ts +168 -20
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +44 -18
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-registry.d.ts +5 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +8 -0
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +4 -3
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +1 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +12 -6
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/permissions/store.d.ts.map +1 -1
- package/dist/core/permissions/store.js +11 -8
- package/dist/core/permissions/store.js.map +1 -1
- package/dist/core/prompt-templates.d.ts +6 -1
- package/dist/core/prompt-templates.d.ts.map +1 -1
- package/dist/core/prompt-templates.js +61 -35
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/provider-composer.d.ts +4 -2
- package/dist/core/provider-composer.d.ts.map +1 -1
- package/dist/core/provider-composer.js +29 -2
- package/dist/core/provider-composer.js.map +1 -1
- package/dist/core/radius.d.ts +3 -0
- package/dist/core/radius.d.ts.map +1 -1
- package/dist/core/radius.js +6 -0
- package/dist/core/radius.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +6 -2
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +63 -42
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-export.d.ts +6 -2
- package/dist/core/session-export.d.ts.map +1 -1
- package/dist/core/session-export.js +14 -13
- package/dist/core/session-export.js.map +1 -1
- package/dist/core/session-manager.d.ts +66 -15
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +295 -115
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +27 -4
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +56 -8
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +5 -4
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts +49 -6
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +140 -79
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +9 -15
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +20 -137
- 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 +2 -2
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/core/tools/edit.d.ts +1 -12
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +6 -204
- 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 +4 -55
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +4 -60
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +4 -49
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/read.d.ts +4 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +10 -122
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/renderers/bash.d.ts +18 -0
- package/dist/core/tools/renderers/bash.d.ts.map +1 -0
- package/dist/core/tools/renderers/bash.js +126 -0
- package/dist/core/tools/renderers/bash.js.map +1 -0
- package/dist/core/tools/renderers/edit.d.ts +23 -0
- package/dist/core/tools/renderers/edit.d.ts.map +1 -0
- package/dist/core/tools/renderers/edit.js +207 -0
- package/dist/core/tools/renderers/edit.js.map +1 -0
- package/dist/core/tools/renderers/find.d.ts +10 -0
- package/dist/core/tools/renderers/find.d.ts.map +1 -0
- package/dist/core/tools/renderers/find.js +64 -0
- package/dist/core/tools/renderers/find.js.map +1 -0
- package/dist/core/tools/renderers/grep.d.ts +10 -0
- package/dist/core/tools/renderers/grep.d.ts.map +1 -0
- package/dist/core/tools/renderers/grep.js +69 -0
- package/dist/core/tools/renderers/grep.js.map +1 -0
- package/dist/core/tools/renderers/index.d.ts +34 -0
- package/dist/core/tools/renderers/index.d.ts.map +1 -0
- package/dist/core/tools/renderers/index.js +47 -0
- package/dist/core/tools/renderers/index.js.map +1 -0
- package/dist/core/tools/renderers/ls.d.ts +10 -0
- package/dist/core/tools/renderers/ls.d.ts.map +1 -0
- package/dist/core/tools/renderers/ls.js +58 -0
- package/dist/core/tools/renderers/ls.js.map +1 -0
- package/dist/core/tools/renderers/read.d.ts +11 -0
- package/dist/core/tools/renderers/read.d.ts.map +1 -0
- package/dist/core/tools/renderers/read.js +132 -0
- package/dist/core/tools/renderers/read.js.map +1 -0
- package/dist/core/tools/renderers/write.d.ts +10 -0
- package/dist/core/tools/renderers/write.d.ts.map +1 -0
- package/dist/core/tools/renderers/write.js +152 -0
- package/dist/core/tools/renderers/write.js.map +1 -0
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +6 -147
- package/dist/core/tools/write.js.map +1 -1
- package/dist/core/usage-totals.d.ts +1 -1
- package/dist/core/usage-totals.d.ts.map +1 -1
- package/dist/core/usage-totals.js +5 -1
- package/dist/core/usage-totals.js.map +1 -1
- package/dist/extensions/llama/client.d.ts +2 -0
- package/dist/extensions/llama/client.d.ts.map +1 -1
- package/dist/extensions/llama/client.js +7 -3
- package/dist/extensions/llama/client.js.map +1 -1
- package/dist/extensions/llama/provider.d.ts.map +1 -1
- package/dist/extensions/llama/provider.js +20 -5
- package/dist/extensions/llama/provider.js.map +1 -1
- package/dist/extensions/llama/ui.d.ts.map +1 -1
- package/dist/extensions/llama/ui.js +3 -2
- package/dist/extensions/llama/ui.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +21 -13
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/bug-report.d.ts +16 -0
- package/dist/modes/interactive/bug-report.d.ts.map +1 -0
- package/dist/modes/interactive/bug-report.js +167 -0
- package/dist/modes/interactive/bug-report.js.map +1 -0
- package/dist/modes/interactive/chat-viewport.d.ts +20 -0
- package/dist/modes/interactive/chat-viewport.d.ts.map +1 -0
- package/dist/modes/interactive/chat-viewport.js +28 -0
- package/dist/modes/interactive/chat-viewport.js.map +1 -0
- package/dist/modes/interactive/components/assistant-message.d.ts +7 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +63 -27
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/autocomplete-source.d.ts +8 -0
- package/dist/modes/interactive/components/autocomplete-source.d.ts.map +1 -0
- package/dist/modes/interactive/components/autocomplete-source.js +23 -0
- package/dist/modes/interactive/components/autocomplete-source.js.map +1 -0
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +6 -4
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.js +1 -1
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +12 -5
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +12 -5
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/composer-dock.d.ts +12 -0
- package/dist/modes/interactive/components/composer-dock.d.ts.map +1 -0
- package/dist/modes/interactive/components/composer-dock.js +24 -0
- package/dist/modes/interactive/components/composer-dock.js.map +1 -0
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +11 -9
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +12 -0
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +38 -0
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/error-summary.d.ts +11 -0
- package/dist/modes/interactive/components/error-summary.d.ts.map +1 -0
- package/dist/modes/interactive/components/error-summary.js +31 -0
- package/dist/modes/interactive/components/error-summary.js.map +1 -0
- package/dist/modes/interactive/components/extension-editor.d.ts +4 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +15 -10
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/extension-input.d.ts +2 -0
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-input.js +15 -5
- package/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts +2 -0
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +15 -10
- 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 -6
- package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +12 -4
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -1
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts +21 -0
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js +34 -0
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +27 -23
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +16 -20
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +7 -6
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/permission-preview.d.ts.map +1 -1
- package/dist/modes/interactive/components/permission-preview.js +3 -3
- package/dist/modes/interactive/components/permission-preview.js.map +1 -1
- package/dist/modes/interactive/components/prompt-input.d.ts +12 -0
- package/dist/modes/interactive/components/prompt-input.d.ts.map +1 -0
- package/dist/modes/interactive/components/prompt-input.js +18 -0
- package/dist/modes/interactive/components/prompt-input.js.map +1 -0
- package/dist/modes/interactive/components/scoped-models-selector.d.ts +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +17 -18
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts +5 -5
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +100 -67
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +5 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +74 -16
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-submenu.js +8 -6
- package/dist/modes/interactive/components/settings-submenu.js.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.js +11 -4
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/dist/modes/interactive/components/status-indicator.d.ts +3 -1
- package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
- package/dist/modes/interactive/components/status-indicator.js +10 -3
- package/dist/modes/interactive/components/status-indicator.js.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +14 -9
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +28 -16
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +73 -80
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tool-panel.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-panel.js +7 -2
- package/dist/modes/interactive/components/tool-panel.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +18 -5
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/trust-selector.js +12 -12
- package/dist/modes/interactive/components/trust-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.js +2 -2
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +6 -3
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +50 -31
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +494 -229
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/dark.json +2 -1
- package/dist/modes/interactive/theme/light.json +2 -1
- package/dist/modes/interactive/theme/theme-controller.d.ts +1 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-controller.js +5 -0
- package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
- package/dist/modes/interactive/theme/theme-json.d.ts +84 -0
- package/dist/modes/interactive/theme/theme-json.d.ts.map +1 -0
- package/dist/modes/interactive/theme/theme-json.js +130 -0
- package/dist/modes/interactive/theme/theme-json.js.map +1 -0
- package/dist/modes/interactive/theme/theme-schema.json +6 -2
- package/dist/modes/interactive/theme/theme.d.ts +14 -5
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +18 -119
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/interactive/tui-renderer.d.ts +21 -0
- package/dist/modes/interactive/tui-renderer.d.ts.map +1 -0
- package/dist/modes/interactive/tui-renderer.js +66 -0
- package/dist/modes/interactive/tui-renderer.js.map +1 -0
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +49 -23
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +2 -2
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/testing/replay/recorded-provider.d.ts +2 -2
- package/dist/testing/replay/recorded-provider.d.ts.map +1 -1
- package/dist/testing/replay/recorded-provider.js +2 -5
- package/dist/testing/replay/recorded-provider.js.map +1 -1
- package/dist/testing/replay/runner.d.ts.map +1 -1
- package/dist/testing/replay/runner.js +0 -1
- package/dist/testing/replay/runner.js.map +1 -1
- package/dist/utils/clipboard-command.d.ts +7 -0
- package/dist/utils/clipboard-command.d.ts.map +1 -0
- package/dist/utils/clipboard-command.js +45 -0
- package/dist/utils/clipboard-command.js.map +1 -0
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +53 -81
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +111 -121
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/exif-orientation.d.ts.map +1 -1
- package/dist/utils/exif-orientation.js +2 -3
- package/dist/utils/exif-orientation.js.map +1 -1
- package/dist/utils/mime.d.ts.map +1 -1
- package/dist/utils/mime.js +1 -1
- package/dist/utils/mime.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js +21 -21
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/dist/utils/tool-result-images.d.ts +3 -1
- package/dist/utils/tool-result-images.d.ts.map +1 -1
- package/dist/utils/tool-result-images.js +4 -1
- package/dist/utils/tool-result-images.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +11 -2
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/wsl.d.ts +3 -0
- package/dist/utils/wsl.d.ts.map +1 -0
- package/dist/utils/wsl.js +15 -0
- package/dist/utils/wsl.js.map +1 -0
- package/dist/utils/zip.d.ts +7 -0
- package/dist/utils/zip.d.ts.map +1 -0
- package/dist/utils/zip.js +60 -0
- package/dist/utils/zip.js.map +1 -0
- package/docs/cli-integration.md +107 -0
- package/docs/cli.md +269 -0
- package/docs/compaction.md +73 -24
- package/docs/configuration.md +45 -0
- package/docs/containerization.md +22 -21
- package/docs/custom-provider.md +21 -12
- package/docs/development.md +19 -0
- package/docs/docs.json +139 -95
- package/docs/extensions.md +243 -371
- package/docs/how-pi-works.md +49 -0
- package/docs/images/interactive-mode.png +0 -0
- package/docs/index.md +3 -3
- package/docs/json.md +43 -3
- package/docs/keybindings.md +57 -54
- package/docs/llama-cpp.md +2 -2
- package/docs/message-types.md +261 -0
- package/docs/models.md +103 -49
- package/docs/packages.md +49 -47
- package/docs/prompt-templates.md +29 -56
- package/docs/providers.md +91 -139
- package/docs/quickstart.md +42 -14
- package/docs/rpc-commands.md +854 -0
- package/docs/rpc-extension-ui.md +200 -0
- package/docs/rpc.md +121 -223
- package/docs/sdk.md +99 -124
- package/docs/security.md +7 -7
- package/docs/session-format.md +85 -82
- package/docs/sessions.md +41 -57
- package/docs/settings.md +95 -88
- package/docs/shell-aliases.md +68 -3
- package/docs/skills.md +62 -49
- package/docs/slash-commands.md +60 -0
- package/docs/terminal-setup.md +86 -55
- package/docs/termux.md +65 -75
- package/docs/themes.md +64 -86
- package/docs/tmux.md +29 -7
- package/docs/tui.md +67 -122
- package/docs/usage.md +31 -42
- package/docs/windows.md +41 -13
- package/examples/README.md +16 -2
- package/examples/extensions/README.md +0 -1
- package/examples/extensions/custom-provider-anthropic/index.ts +18 -12
- 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/index.ts +2 -2
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/dynamic-resources/dynamic.json +2 -0
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/prompt-customizer.ts +19 -67
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/plugins/pi-example-plugin/README.md +38 -0
- package/examples/plugins/pi-example-plugin/package.json +10 -0
- package/examples/plugins/pi-example-plugin/src/contract.ts +13 -0
- package/examples/plugins/pi-example-plugin/src/session.ts +24 -0
- package/examples/plugins/pi-example-plugin/src/tui.ts +31 -0
- package/examples/rpc-client.ts +35 -0
- package/examples/rpc-extension-ui.ts +25 -5
- package/examples/sdk/README.md +1 -1
- package/npm-shrinkwrap.json +861 -513
- package/package.json +27 -18
- package/dist/bun/register-bedrock.d.ts +0 -2
- package/dist/bun/register-bedrock.d.ts.map +0 -1
- package/dist/bun/register-bedrock.js +0 -4
- package/dist/bun/register-bedrock.js.map +0 -1
- package/dist/cli/experimental/auth.d.ts +0 -16
- package/dist/cli/experimental/auth.d.ts.map +0 -1
- package/dist/cli/experimental/auth.js +0 -13
- package/dist/cli/experimental/auth.js.map +0 -1
- package/dist/cli/experimental/cli.d.ts +0 -6
- package/dist/cli/experimental/cli.d.ts.map +0 -1
- package/dist/cli/experimental/cli.js +0 -5
- package/dist/cli/experimental/cli.js.map +0 -1
- package/dist/cli/experimental/command-options.d.ts +0 -17
- package/dist/cli/experimental/command-options.d.ts.map +0 -1
- package/dist/cli/experimental/command-options.js +0 -35
- package/dist/cli/experimental/command-options.js.map +0 -1
- package/dist/cli/experimental/command.d.ts +0 -63
- package/dist/cli/experimental/command.d.ts.map +0 -1
- package/dist/cli/experimental/command.js +0 -130
- package/dist/cli/experimental/command.js.map +0 -1
- package/dist/cli/experimental/commands/client.d.ts +0 -13
- package/dist/cli/experimental/commands/client.d.ts.map +0 -1
- package/dist/cli/experimental/commands/client.js +0 -25
- package/dist/cli/experimental/commands/client.js.map +0 -1
- package/dist/cli/experimental/commands/pi.d.ts +0 -15
- package/dist/cli/experimental/commands/pi.d.ts.map +0 -1
- package/dist/cli/experimental/commands/pi.js +0 -28
- package/dist/cli/experimental/commands/pi.js.map +0 -1
- package/dist/cli/experimental/commands/server.d.ts +0 -13
- package/dist/cli/experimental/commands/server.d.ts.map +0 -1
- package/dist/cli/experimental/commands/server.js +0 -25
- package/dist/cli/experimental/commands/server.js.map +0 -1
- package/dist/cli/experimental/transport-address.d.ts +0 -10
- package/dist/cli/experimental/transport-address.d.ts.map +0 -1
- package/dist/cli/experimental/transport-address.js +0 -38
- package/dist/cli/experimental/transport-address.js.map +0 -1
- package/dist/client/index.d.ts +0 -3
- package/dist/client/index.d.ts.map +0 -1
- package/dist/client/index.js +0 -3
- package/dist/client/index.js.map +0 -1
- package/dist/client/remote-session.d.ts +0 -53
- package/dist/client/remote-session.d.ts.map +0 -1
- package/dist/client/remote-session.js +0 -340
- package/dist/client/remote-session.js.map +0 -1
- package/dist/client/transcript.d.ts +0 -12
- package/dist/client/transcript.d.ts.map +0 -1
- package/dist/client/transcript.js +0 -98
- package/dist/client/transcript.js.map +0 -1
- package/dist/modes/interactive/components/message-spine.d.ts +0 -14
- package/dist/modes/interactive/components/message-spine.d.ts.map +0 -1
- package/dist/modes/interactive/components/message-spine.js +0 -19
- package/dist/modes/interactive/components/message-spine.js.map +0 -1
- package/dist/utils/clipboard-native.d.ts +0 -11
- package/dist/utils/clipboard-native.d.ts.map +0 -1
- package/dist/utils/clipboard-native.js +0 -20
- package/dist/utils/clipboard-native.js.map +0 -1
|
@@ -6,30 +6,35 @@ import * as crypto from "node:crypto";
|
|
|
6
6
|
import * as fs from "node:fs";
|
|
7
7
|
import * as os from "node:os";
|
|
8
8
|
import * as path from "node:path";
|
|
9
|
+
import { isRetryableAssistantError, } from "@earendil-works/pi-ai/compat";
|
|
9
10
|
import * as TuiLayouts from "@earendil-works/pi-tui";
|
|
10
|
-
import { CombinedAutocompleteProvider, Container, fuzzyFilter, getCapabilities, hyperlink, Markdown, matchesKey,
|
|
11
|
+
import { CombinedAutocompleteProvider, Container, fuzzyFilter, getCapabilities, hyperlink, Markdown, matchesKey, Spacer, setCapabilityOverrides, setKeybindings, Text, TruncatedText, TuiAltScreen, TuiMainScreen, visibleWidth, } from "@earendil-works/pi-tui";
|
|
11
12
|
import chalk from "chalk";
|
|
12
13
|
import { spawn, spawnSync } from "child_process";
|
|
13
14
|
import { APP_NAME, APP_TITLE, CONFIG_DIR_NAME, getAgentDir, getAuthPath, getDebugLogPath, getDocsPath, getShareViewerUrl, VERSION, } from "../../config.js";
|
|
14
15
|
import { parseSkillBlock } from "../../core/agent-session.js";
|
|
15
16
|
import { SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js";
|
|
16
17
|
import { CACHE_TTL_MS, collectCacheMisses, computeCacheWaste, detectCacheMiss, } from "../../core/cache-stats.js";
|
|
18
|
+
import { formatCacheWarmingStatus, formatCacheWarmingUsage } from "../../core/cache-warmer.js";
|
|
19
|
+
import { findExtensionStackMatches, recordCrash, takeUnnotifiedCrash } from "../../core/crash-log.js";
|
|
17
20
|
import { DEFAULT_THINKING_LEVEL, THINKING_LEVEL_OPTIONS } from "../../core/defaults.js";
|
|
18
21
|
import { getApexEnvironment } from "../../core/environment.js";
|
|
19
22
|
import { FooterDataProvider } from "../../core/footer-data-provider.js";
|
|
20
23
|
import { configureHttpDispatcher, formatHttpIdleTimeoutMs } from "../../core/http-dispatcher.js";
|
|
21
24
|
import { KeybindingsManager } from "../../core/keybindings.js";
|
|
22
25
|
import { authorizeConfiguredServer } from "../../core/mcp/oauth/authorize.js";
|
|
23
|
-
import { createCompactionSummaryMessage } from "../../core/messages.js";
|
|
26
|
+
import { createCompactionSummaryMessage, createCustomMessage } from "../../core/messages.js";
|
|
24
27
|
import { defaultModelPerProvider, findExactModelReferenceMatch, resolveModelScopeFromModels, } from "../../core/model-resolver.js";
|
|
25
28
|
import { CredentialSynchronizationError } from "../../core/model-runtime.js";
|
|
26
29
|
import { aggregateUsagePerformance } from "../../core/observability/aggregate.js";
|
|
27
30
|
import { DefaultPackageManager } from "../../core/package-manager.js";
|
|
28
31
|
import { formatMissingSessionCwdPrompt, MissingSessionCwdError } from "../../core/session-cwd.js";
|
|
29
|
-
import { SessionManager, sessionEntryToContextMessages } from "../../core/session-manager.js";
|
|
32
|
+
import { SessionManager, sessionEntryToContextMessages, } from "../../core/session-manager.js";
|
|
30
33
|
import { formatShareUnavailableMessage, publishSessionShare } from "../../core/session-share.js";
|
|
34
|
+
import { CHAT_DETAILS } from "../../core/settings-manager.js";
|
|
31
35
|
import { slugifySkillCommandName } from "../../core/skills.js";
|
|
32
36
|
import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
|
|
37
|
+
import { withBuiltInRenderers } from "../../core/tools/renderers/index.js";
|
|
33
38
|
import { hasTrustRequiringProjectResources, ProjectTrustStore } from "../../core/trust-manager.js";
|
|
34
39
|
import { getUsageCostBreakdown } from "../../core/usage-totals.js";
|
|
35
40
|
import { getChangelogPath, getNewEntries, normalizeChangelogLinks, parseChangelog } from "../../utils/changelog.js";
|
|
@@ -42,24 +47,29 @@ import { killTrackedDetachedChildren } from "../../utils/shell.js";
|
|
|
42
47
|
import { loadAllHighlightLanguages } from "../../utils/syntax-highlight.js";
|
|
43
48
|
import { ensureTool } from "../../utils/tools-manager.js";
|
|
44
49
|
import { checkForNewApexCodeVersion } from "../../utils/version-check.js";
|
|
50
|
+
import { reportBug } from "./bug-report.js";
|
|
51
|
+
import { createChatViewport } from "./chat-viewport.js";
|
|
45
52
|
import { ArminComponent } from "./components/armin.js";
|
|
46
53
|
import { AssistantMessageComponent } from "./components/assistant-message.js";
|
|
54
|
+
import { describeWithSource } from "./components/autocomplete-source.js";
|
|
47
55
|
import { BashExecutionComponent } from "./components/bash-execution.js";
|
|
48
56
|
import { BorderedLoader } from "./components/bordered-loader.js";
|
|
49
57
|
import { BranchSummaryMessageComponent } from "./components/branch-summary-message.js";
|
|
50
58
|
import { CompactionSummaryMessageComponent } from "./components/compaction-summary-message.js";
|
|
59
|
+
import { ComposerDock } from "./components/composer-dock.js";
|
|
51
60
|
import { CustomEditor } from "./components/custom-editor.js";
|
|
52
61
|
import { CustomEntryComponent } from "./components/custom-entry.js";
|
|
53
62
|
import { CustomMessageComponent } from "./components/custom-message.js";
|
|
54
63
|
import { DaxnutsComponent } from "./components/daxnuts.js";
|
|
55
64
|
import { DynamicBorder } from "./components/dynamic-border.js";
|
|
56
65
|
import { EarendilAnnouncementComponent } from "./components/earendil-announcement.js";
|
|
66
|
+
import { collapsedErrorLine, summarizeError } from "./components/error-summary.js";
|
|
57
67
|
import { ExtensionEditorComponent } from "./components/extension-editor.js";
|
|
58
68
|
import { ExtensionInputComponent } from "./components/extension-input.js";
|
|
59
69
|
import { ExtensionSelectorComponent } from "./components/extension-selector.js";
|
|
60
70
|
import { FirstUseHints } from "./components/first-use-hints.js";
|
|
61
71
|
import { FooterComponent, formatTokens } from "./components/footer.js";
|
|
62
|
-
import { formatKeyText, keyDisplayText, keyHint, keyText, rawKeyHint } from "./components/keybinding-hints.js";
|
|
72
|
+
import { formatKeyText, hintRow, keyDisplayText, keyHint, keyText, rawKeyHint } from "./components/keybinding-hints.js";
|
|
63
73
|
import { LoginDialogComponent } from "./components/login-dialog.js";
|
|
64
74
|
import { createMermaidMarkdownTransformer } from "./components/mermaid.js";
|
|
65
75
|
import { ModelSelectorComponent } from "./components/model-selector.js";
|
|
@@ -82,19 +92,28 @@ import { refreshModelCatalogs } from "./model-catalog-refresh.js";
|
|
|
82
92
|
import { getModelSearchText } from "./model-search.js";
|
|
83
93
|
import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, onThemeChange, setRegisteredThemes, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
|
|
84
94
|
import { InteractiveThemeController } from "./theme/theme-controller.js";
|
|
85
|
-
|
|
95
|
+
import { createInteractiveTui, createInteractiveTuiReference } from "./tui-renderer.js";
|
|
96
|
+
export { createInteractiveTui, createInteractiveTuiReference } from "./tui-renderer.js";
|
|
97
|
+
/** A session opens with everything collapsed; the expand key opens it rung by rung. */
|
|
98
|
+
export const INITIAL_CHAT_DETAIL = "overview";
|
|
86
99
|
/** The next rung, wrapping from `all` back to `overview`. */
|
|
87
100
|
export function nextChatDetail(detail) {
|
|
88
|
-
return
|
|
101
|
+
return CHAT_DETAILS[(CHAT_DETAILS.indexOf(detail) + 1) % CHAT_DETAILS.length] ?? INITIAL_CHAT_DETAIL;
|
|
89
102
|
}
|
|
90
103
|
/** The single place the three rungs turn into rendering flags. */
|
|
91
104
|
export function chatDetailView(detail) {
|
|
92
105
|
return {
|
|
93
106
|
toolOutputExpanded: detail === "all",
|
|
94
107
|
editDiffsExpanded: detail !== "overview",
|
|
95
|
-
|
|
108
|
+
thinking: detail === "overview" ? "collapsed" : detail === "details" ? "preference" : "revealed",
|
|
96
109
|
};
|
|
97
110
|
}
|
|
111
|
+
function isWorkingStatusEditor(editor) {
|
|
112
|
+
return ("embedWorkingStatus" in editor &&
|
|
113
|
+
editor.embedWorkingStatus === true &&
|
|
114
|
+
"setWorkingStatusIndicator" in editor &&
|
|
115
|
+
typeof editor.setWorkingStatusIndicator === "function");
|
|
116
|
+
}
|
|
98
117
|
function isExpandable(obj) {
|
|
99
118
|
return typeof obj === "object" && obj !== null && "setExpanded" in obj && typeof obj.setExpanded === "function";
|
|
100
119
|
}
|
|
@@ -116,12 +135,27 @@ class ExpandableText extends Text {
|
|
|
116
135
|
this.setText(expanded ? this.getExpandedText() : this.getCollapsedText());
|
|
117
136
|
}
|
|
118
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* An error line that joins the detail cycle: a multi-line error folds to its
|
|
140
|
+
* summary until the transcript is fully expanded. `outcome` trails both forms,
|
|
141
|
+
* so what happened to the request stays visible while the detail is folded.
|
|
142
|
+
*/
|
|
143
|
+
function errorText(message, expanded, paddingX, outcome = "") {
|
|
144
|
+
const summary = summarizeError(message);
|
|
145
|
+
const full = () => theme.fg("error", `${message}${outcome}`);
|
|
146
|
+
if (summary === undefined)
|
|
147
|
+
return new Text(full(), paddingX, 0);
|
|
148
|
+
return new ExpandableText(() => collapsedErrorLine(theme.fg("error", `${summary}${outcome}`)), full, expanded, paddingX, 0);
|
|
149
|
+
}
|
|
119
150
|
function isCustomSessionEntry(item) {
|
|
120
151
|
return "type" in item && item.type === "custom";
|
|
121
152
|
}
|
|
122
153
|
function isCompactionCostNotice(item) {
|
|
123
154
|
return "type" in item && item.type === "compaction_cost";
|
|
124
155
|
}
|
|
156
|
+
function isUsageSessionEntry(item) {
|
|
157
|
+
return "type" in item && item.type === "usage";
|
|
158
|
+
}
|
|
125
159
|
const DEAD_TERMINAL_ERROR_CODES = new Set(["EIO", "EPIPE", "ENOTCONN"]);
|
|
126
160
|
function isDeadTerminalError(error) {
|
|
127
161
|
if (!error || typeof error !== "object" || !("code" in error)) {
|
|
@@ -130,6 +164,22 @@ function isDeadTerminalError(error) {
|
|
|
130
164
|
const code = error.code;
|
|
131
165
|
return code !== undefined && DEAD_TERMINAL_ERROR_CODES.has(code);
|
|
132
166
|
}
|
|
167
|
+
export function formatCrashExtensionHint(extensionMatches) {
|
|
168
|
+
const matches = Array.isArray(extensionMatches)
|
|
169
|
+
? extensionMatches.filter((match) => typeof match === "string" && match.length > 0)
|
|
170
|
+
: [];
|
|
171
|
+
if (matches.length === 0)
|
|
172
|
+
return undefined;
|
|
173
|
+
const quoted = matches.map((match) => `\`${match}\``);
|
|
174
|
+
const labels = quoted.length === 1
|
|
175
|
+
? quoted[0]
|
|
176
|
+
: quoted.length === 2
|
|
177
|
+
? quoted.join(" and ")
|
|
178
|
+
: `${quoted.slice(0, -1).join(", ")}, and ${quoted[quoted.length - 1]}`;
|
|
179
|
+
const noun = matches.length === 1 ? "extension" : "extensions";
|
|
180
|
+
const pronoun = matches.length === 1 ? "it" : "them";
|
|
181
|
+
return `A stack frame came from loaded ${noun} ${labels}, which may be involved. Try disabling ${pronoun} with \`${APP_NAME} config\`, or run \`${APP_NAME} -ne\` to confirm.`;
|
|
182
|
+
}
|
|
133
183
|
const ANTHROPIC_SUBSCRIPTION_AUTH_WARNING = "Anthropic subscription auth is active. Third-party harness usage draws from extra usage and is billed per token, not your Claude plan limits. Manage extra usage at https://claude.ai/settings/usage. Disable this warning in /settings.";
|
|
134
184
|
function isAnthropicSubscriptionAuthKey(apiKey) {
|
|
135
185
|
return typeof apiKey === "string" && apiKey.startsWith("sk-ant-oat");
|
|
@@ -202,61 +252,6 @@ function formatLoginProviderCompletionDescription(provider) {
|
|
|
202
252
|
const authTypes = provider.authTypes.map(formatAuthSelectorProviderType).join("/");
|
|
203
253
|
return provider.name === provider.id ? authTypes : `${provider.name} · ${authTypes}`;
|
|
204
254
|
}
|
|
205
|
-
/** Composition root for selecting the interactive terminal renderer. */
|
|
206
|
-
export function createInteractiveTui(options) {
|
|
207
|
-
const terminal = options.terminal ?? new ProcessTerminal();
|
|
208
|
-
if (options.tuiMode === "fullscreen") {
|
|
209
|
-
const styleSearchMatch = (text) => theme.bg("searchMatchBg", theme.fg("searchMatchText", text));
|
|
210
|
-
return new TuiAltScreen(terminal, options.showHardwareCursor, options.logDirectory, {
|
|
211
|
-
searchMatchStyle: (text) => theme.underline(styleSearchMatch(text)),
|
|
212
|
-
searchCurrentMatchStyle: (text) => theme.bold(theme.inverse(styleSearchMatch(text))),
|
|
213
|
-
openUrl: openBrowser,
|
|
214
|
-
onRightClickPaste: options.onRightClickPaste,
|
|
215
|
-
copyOnSelect: options.fullscreenCopyOnSelect,
|
|
216
|
-
copySelection: async (text) => {
|
|
217
|
-
try {
|
|
218
|
-
await copyToClipboard(text);
|
|
219
|
-
return true;
|
|
220
|
-
}
|
|
221
|
-
catch {
|
|
222
|
-
return false;
|
|
223
|
-
}
|
|
224
|
-
},
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
return new TuiMainScreen(terminal, options.showHardwareCursor, options.logDirectory);
|
|
228
|
-
}
|
|
229
|
-
/** Stable reference for components while InteractiveMode replaces the active renderer. */
|
|
230
|
-
export function createInteractiveTuiReference(getTui) {
|
|
231
|
-
return new Proxy({}, {
|
|
232
|
-
get: (_target, property) => {
|
|
233
|
-
const tui = getTui();
|
|
234
|
-
const value = Reflect.get(tui, property, tui);
|
|
235
|
-
if (typeof value !== "function")
|
|
236
|
-
return value;
|
|
237
|
-
let methodTui = tui;
|
|
238
|
-
let method = value;
|
|
239
|
-
return (...args) => {
|
|
240
|
-
const currentTui = getTui();
|
|
241
|
-
if (currentTui !== methodTui) {
|
|
242
|
-
const currentMethod = Reflect.get(currentTui, property, currentTui);
|
|
243
|
-
if (typeof currentMethod !== "function") {
|
|
244
|
-
throw new TypeError(`TUI property ${String(property)} is not callable`);
|
|
245
|
-
}
|
|
246
|
-
methodTui = currentTui;
|
|
247
|
-
method = currentMethod;
|
|
248
|
-
}
|
|
249
|
-
return Reflect.apply(method, methodTui, args);
|
|
250
|
-
};
|
|
251
|
-
},
|
|
252
|
-
set: (_target, property, value) => {
|
|
253
|
-
const tui = getTui();
|
|
254
|
-
return Reflect.set(tui, property, value, tui);
|
|
255
|
-
},
|
|
256
|
-
has: (_target, property) => Reflect.has(getTui(), property),
|
|
257
|
-
getPrototypeOf: () => Reflect.getPrototypeOf(getTui()),
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
255
|
/**
|
|
261
256
|
* Working status rides in the footer tray; everything else keeps its own rows.
|
|
262
257
|
*
|
|
@@ -315,11 +310,12 @@ export class InteractiveMode {
|
|
|
315
310
|
onInputCallback;
|
|
316
311
|
pendingUserInputs = [];
|
|
317
312
|
activeStatusIndicator = undefined;
|
|
313
|
+
activeWorkingIndicatorEmbedded = false;
|
|
318
314
|
idleStatus = new IdleStatus();
|
|
319
315
|
workingMessage = undefined;
|
|
320
316
|
workingVisible = true;
|
|
321
317
|
workingIndicatorOptions = undefined;
|
|
322
|
-
defaultWorkingMessage = "Working
|
|
318
|
+
defaultWorkingMessage = "Working";
|
|
323
319
|
defaultHiddenThinkingLabel = "Thinking...";
|
|
324
320
|
hiddenThinkingLabel = this.defaultHiddenThinkingLabel;
|
|
325
321
|
lastSigintTime = 0;
|
|
@@ -333,13 +329,14 @@ export class InteractiveMode {
|
|
|
333
329
|
managedToolStatusStarted = false;
|
|
334
330
|
// Streaming message tracking
|
|
335
331
|
streamingComponent = undefined;
|
|
332
|
+
entriesRenderedByBoundaryCompaction = new Set();
|
|
336
333
|
streamingMessage = undefined;
|
|
337
334
|
// Tool execution tracking: toolCallId -> component
|
|
338
335
|
pendingTools = new Map();
|
|
339
336
|
// Tool output expansion state
|
|
340
|
-
chatDetail =
|
|
341
|
-
toolOutputExpanded =
|
|
342
|
-
editDiffsExpanded =
|
|
337
|
+
chatDetail = INITIAL_CHAT_DETAIL;
|
|
338
|
+
toolOutputExpanded = chatDetailView(INITIAL_CHAT_DETAIL).toolOutputExpanded;
|
|
339
|
+
editDiffsExpanded = chatDetailView(INITIAL_CHAT_DETAIL).editDiffsExpanded;
|
|
343
340
|
// Thinking block visibility state
|
|
344
341
|
hideThinkingBlock = false;
|
|
345
342
|
outputPad = 1;
|
|
@@ -368,10 +365,19 @@ export class InteractiveMode {
|
|
|
368
365
|
autoCompactionEscapeHandler;
|
|
369
366
|
// Auto-retry state
|
|
370
367
|
retryEscapeHandler;
|
|
368
|
+
/**
|
|
369
|
+
* The failed attempt an auto-retry would supersede. `component` is set while its error is
|
|
370
|
+
* still on screen, and a retry removes it from the chat.
|
|
371
|
+
*/
|
|
372
|
+
retriedAttempt;
|
|
373
|
+
/** Set when the user presses Escape during a retry wait, which is the only way to cancel one. */
|
|
374
|
+
retryCancelled = false;
|
|
371
375
|
// Messages queued while compaction is running
|
|
372
376
|
compactionQueuedMessages = [];
|
|
373
377
|
// Shutdown state
|
|
374
378
|
shutdownRequested = false;
|
|
379
|
+
/** The `/bug` hint is shown at most once per session so error output stays readable. */
|
|
380
|
+
bugReportHintShown = false;
|
|
375
381
|
// Extension UI state
|
|
376
382
|
extensionSelector = undefined;
|
|
377
383
|
extensionInput = undefined;
|
|
@@ -435,6 +441,7 @@ export class InteractiveMode {
|
|
|
435
441
|
tuiMode,
|
|
436
442
|
showHardwareCursor: this.settingsManager.getShowHardwareCursor(),
|
|
437
443
|
logDirectory: getAgentDir(),
|
|
444
|
+
terminal: options.terminal,
|
|
438
445
|
onRightClickPaste: this.onRightClickPaste,
|
|
439
446
|
fullscreenCopyOnSelect: this.settingsManager.getFullscreenCopyOnSelect(),
|
|
440
447
|
});
|
|
@@ -470,17 +477,20 @@ export class InteractiveMode {
|
|
|
470
477
|
surfaceColor: (text) => theme.bg("userMessageBg", text),
|
|
471
478
|
autocompleteRule: (width) => theme.fg("borderMuted", (this.settingsManager.getSymbolPreset() === "ascii" ? "-" : "┄").repeat(Math.max(1, width))),
|
|
472
479
|
autocompleteFooter: () => this.buildAutocompleteFooter(),
|
|
480
|
+
// Upstream embeds the working status in this editor's top border. The Apex
|
|
481
|
+
// composer draws a filled surface with no top border and the footer tray
|
|
482
|
+
// carries that status, so the default editor leaves `embedWorkingStatus` off.
|
|
473
483
|
});
|
|
474
484
|
this.editor = this.defaultEditor;
|
|
475
|
-
this.editorContainer = new
|
|
485
|
+
this.editorContainer = new ComposerDock(() => this.editor);
|
|
476
486
|
this.editorContainer.addChild(this.editor);
|
|
477
487
|
this.footerDataProvider = new FooterDataProvider(this.sessionManager.getCwd());
|
|
478
488
|
this.footer = new FooterComponent(this.session, this.footerDataProvider, this.settingsManager);
|
|
479
489
|
this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
|
|
480
490
|
this.footerContainer = new Container();
|
|
481
491
|
this.footerContainer.addChild(this.footer);
|
|
482
|
-
// Load hide thinking block setting
|
|
483
492
|
this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
|
|
493
|
+
this.loadChatDetail(this.settingsManager.getChatDetail());
|
|
484
494
|
this.outputPad = this.settingsManager.getOutputPad();
|
|
485
495
|
// Register themes from resource loader and initialize
|
|
486
496
|
setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
|
|
@@ -491,32 +501,6 @@ export class InteractiveMode {
|
|
|
491
501
|
initialThemeSetting: options.initialThemeSetting,
|
|
492
502
|
});
|
|
493
503
|
}
|
|
494
|
-
getAutocompleteSourceTag(sourceInfo) {
|
|
495
|
-
if (!sourceInfo) {
|
|
496
|
-
return undefined;
|
|
497
|
-
}
|
|
498
|
-
const scopePrefix = sourceInfo.scope === "user" ? "u" : sourceInfo.scope === "project" ? "p" : "t";
|
|
499
|
-
const source = sourceInfo.source.trim();
|
|
500
|
-
if (source === "auto" || source === "local" || source === "cli") {
|
|
501
|
-
return scopePrefix;
|
|
502
|
-
}
|
|
503
|
-
if (source.startsWith("npm:")) {
|
|
504
|
-
return `${scopePrefix}:${source}`;
|
|
505
|
-
}
|
|
506
|
-
const gitSource = parseGitUrl(source);
|
|
507
|
-
if (gitSource) {
|
|
508
|
-
const ref = gitSource.ref ? `@${gitSource.ref}` : "";
|
|
509
|
-
return `${scopePrefix}:git:${gitSource.host}/${gitSource.path}${ref}`;
|
|
510
|
-
}
|
|
511
|
-
return scopePrefix;
|
|
512
|
-
}
|
|
513
|
-
prefixAutocompleteDescription(description, sourceInfo) {
|
|
514
|
-
const sourceTag = this.getAutocompleteSourceTag(sourceInfo);
|
|
515
|
-
if (!sourceTag) {
|
|
516
|
-
return description;
|
|
517
|
-
}
|
|
518
|
-
return description ? `[${sourceTag}] ${description}` : `[${sourceTag}]`;
|
|
519
|
-
}
|
|
520
504
|
getBuiltInCommandConflictDiagnostics(extensionRunner) {
|
|
521
505
|
const builtinNames = new Set(BUILTIN_SLASH_COMMANDS.map((command) => command.name));
|
|
522
506
|
return extensionRunner
|
|
@@ -595,7 +579,7 @@ export class InteractiveMode {
|
|
|
595
579
|
// Convert prompt templates to SlashCommand format for autocomplete
|
|
596
580
|
const templateCommands = this.session.promptTemplates.map((cmd) => ({
|
|
597
581
|
name: cmd.name,
|
|
598
|
-
description:
|
|
582
|
+
description: describeWithSource(cmd.description, cmd.sourceInfo),
|
|
599
583
|
...(cmd.argumentHint && { argumentHint: cmd.argumentHint }),
|
|
600
584
|
}));
|
|
601
585
|
// Convert extension commands to SlashCommand format
|
|
@@ -605,7 +589,7 @@ export class InteractiveMode {
|
|
|
605
589
|
.filter((cmd) => !builtinCommandNames.has(cmd.name))
|
|
606
590
|
.map((cmd) => ({
|
|
607
591
|
name: cmd.invocationName,
|
|
608
|
-
description:
|
|
592
|
+
description: describeWithSource(cmd.description, cmd.sourceInfo),
|
|
609
593
|
getArgumentCompletions: cmd.getArgumentCompletions,
|
|
610
594
|
}));
|
|
611
595
|
// Build skill commands from session.skills (if enabled)
|
|
@@ -617,7 +601,7 @@ export class InteractiveMode {
|
|
|
617
601
|
this.skillCommands.set(commandName, skill.filePath);
|
|
618
602
|
skillCommandList.push({
|
|
619
603
|
name: commandName,
|
|
620
|
-
description:
|
|
604
|
+
description: describeWithSource(skill.description, skill.sourceInfo),
|
|
621
605
|
});
|
|
622
606
|
}
|
|
623
607
|
}
|
|
@@ -661,7 +645,7 @@ export class InteractiveMode {
|
|
|
661
645
|
this.chatContainer.addChild(new Text(condensedText, 1, 0));
|
|
662
646
|
}
|
|
663
647
|
else {
|
|
664
|
-
this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "What's
|
|
648
|
+
this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "What's new")), 1, 0));
|
|
665
649
|
this.chatContainer.addChild(new Spacer(1));
|
|
666
650
|
this.chatContainer.addChild(new Markdown(this.changelogMarkdown.trim(), 1, 0, this.getMarkdownThemeWithSettings()));
|
|
667
651
|
this.chatContainer.addChild(new Spacer(1));
|
|
@@ -760,25 +744,20 @@ export class InteractiveMode {
|
|
|
760
744
|
}
|
|
761
745
|
// Keep one component tree and remount it when changing renderers.
|
|
762
746
|
this.renderWidgets(); // Initialize with default spacer
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
747
|
+
const viewport = createChatViewport({
|
|
748
|
+
document: this.documentContainer,
|
|
749
|
+
pendingMessages: this.pendingMessagesContainer,
|
|
750
|
+
status: this.statusContainer,
|
|
751
|
+
widgetsAbove: this.widgetContainerAbove,
|
|
752
|
+
editor: this.editorContainer,
|
|
753
|
+
widgetsBelow: this.widgetContainerBelow,
|
|
754
|
+
footer: this.footerContainer,
|
|
767
755
|
scrollbar: this.settingsManager.getFullscreenScrollbar(),
|
|
768
|
-
|
|
756
|
+
scrollbarTrackStyle: (text) => theme.fg("scrollbarTrack", text),
|
|
757
|
+
scrollbarThumbStyle: (text) => theme.fg("scrollbarThumb", text),
|
|
769
758
|
});
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
{ component: this.statusContainer, shrink: 1, minSize: 0 },
|
|
773
|
-
{ component: this.widgetContainerAbove, shrink: 1, minSize: 0 },
|
|
774
|
-
{ component: this.editorContainer, shrink: 1, minSize: 3 },
|
|
775
|
-
{ component: this.widgetContainerBelow, shrink: 1, minSize: 0 },
|
|
776
|
-
{ component: this.footerContainer, shrink: 1, minSize: 1 },
|
|
777
|
-
]);
|
|
778
|
-
this.fullscreenLayoutRoot = new TuiLayouts.VStack([
|
|
779
|
-
{ component: this.transcriptScrollView, basis: 0, grow: 1, shrink: 1, minSize: 1 },
|
|
780
|
-
{ component: dock, basis: "auto", grow: 0, shrink: 1, minSize: 1 },
|
|
781
|
-
]);
|
|
759
|
+
this.transcriptScrollView = viewport.transcript;
|
|
760
|
+
this.fullscreenLayoutRoot = viewport.root;
|
|
782
761
|
this.mountInteractiveTui(this.renderer, [
|
|
783
762
|
this.documentContainer,
|
|
784
763
|
this.pendingMessagesContainer,
|
|
@@ -806,25 +785,25 @@ export class InteractiveMode {
|
|
|
806
785
|
// The full cheatsheet is verbose-only. The default screen leads with the
|
|
807
786
|
// mark and the runtime facts that actually differ between launches.
|
|
808
787
|
const expandedInstructions = [
|
|
809
|
-
hint("app.interrupt", "
|
|
810
|
-
hint("app.clear", "
|
|
811
|
-
rawKeyHint(`${keyText("app.clear")} twice`, "
|
|
812
|
-
hint("app.exit", "
|
|
813
|
-
hint("app.suspend", "
|
|
814
|
-
keyHint("tui.editor.deleteToLineEnd", "
|
|
815
|
-
hint("app.thinking.cycle", "
|
|
816
|
-
rawKeyHint(`${keyText("app.model.cycleForward")}/${keyText("app.model.cycleBackward")}`, "
|
|
817
|
-
hint("app.model.select", "
|
|
818
|
-
hint("app.tools.expand", "
|
|
819
|
-
hint("app.thinking.toggle", "
|
|
820
|
-
hint("app.editor.external", "
|
|
821
|
-
rawKeyHint("/", "
|
|
822
|
-
rawKeyHint("!", "
|
|
823
|
-
rawKeyHint("!!", "
|
|
824
|
-
hint("app.message.followUp", "
|
|
825
|
-
hint("app.message.dequeue", "
|
|
826
|
-
hint("app.clipboard.pasteImage", "
|
|
827
|
-
rawKeyHint("drop files", "
|
|
788
|
+
hint("app.interrupt", "interrupt"),
|
|
789
|
+
hint("app.clear", "clear"),
|
|
790
|
+
rawKeyHint(`${keyText("app.clear")} twice`, "exit"),
|
|
791
|
+
hint("app.exit", "exit (empty)"),
|
|
792
|
+
hint("app.suspend", "suspend"),
|
|
793
|
+
keyHint("tui.editor.deleteToLineEnd", "delete to end"),
|
|
794
|
+
hint("app.thinking.cycle", "cycle thinking level"),
|
|
795
|
+
rawKeyHint(`${keyText("app.model.cycleForward")}/${keyText("app.model.cycleBackward")}`, "cycle models"),
|
|
796
|
+
hint("app.model.select", "select model"),
|
|
797
|
+
hint("app.tools.expand", "expand tools"),
|
|
798
|
+
hint("app.thinking.toggle", "expand thinking"),
|
|
799
|
+
hint("app.editor.external", "external editor"),
|
|
800
|
+
rawKeyHint("/", "commands"),
|
|
801
|
+
rawKeyHint("!", "run bash"),
|
|
802
|
+
rawKeyHint("!!", "run bash (no context)"),
|
|
803
|
+
hint("app.message.followUp", "queue follow-up"),
|
|
804
|
+
hint("app.message.dequeue", "edit all queued messages"),
|
|
805
|
+
hint("app.clipboard.pasteImage", "paste image (with text fallback)"),
|
|
806
|
+
rawKeyHint("drop files", "attach"),
|
|
828
807
|
].join("\n");
|
|
829
808
|
this.builtInHeader = new ApexSplashHeader(this.version, () => this.session.state.model?.id, () => this.session.sessionManager.getCwd(), this.options.verbose ? expandedInstructions : undefined, {
|
|
830
809
|
topPadding: true,
|
|
@@ -966,6 +945,11 @@ export class InteractiveMode {
|
|
|
966
945
|
if (modelFallbackMessage) {
|
|
967
946
|
this.showWarning(modelFallbackMessage);
|
|
968
947
|
}
|
|
948
|
+
const crash = takeUnnotifiedCrash();
|
|
949
|
+
if (crash) {
|
|
950
|
+
const when = new Date(crash.timestamp).toLocaleString();
|
|
951
|
+
this.showWarning(`${APP_NAME} crashed on ${when} (${crash.message}). Run /bug to report it; the crash details are attached automatically.`);
|
|
952
|
+
}
|
|
969
953
|
void this.maybeWarnAboutAnthropicSubscriptionAuth();
|
|
970
954
|
// Process initial messages
|
|
971
955
|
if (initialMessage) {
|
|
@@ -1553,13 +1537,12 @@ export class InteractiveMode {
|
|
|
1553
1537
|
*/
|
|
1554
1538
|
/** Live keybindings under the autocomplete dropdown. */
|
|
1555
1539
|
buildAutocompleteFooter() {
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
].join(separator);
|
|
1540
|
+
return hintRow([
|
|
1541
|
+
[["tui.select.up", "tui.select.down"], "move"],
|
|
1542
|
+
["tui.select.confirm", "select"],
|
|
1543
|
+
["tui.input.tab", "complete"],
|
|
1544
|
+
["tui.select.cancel", "dismiss"],
|
|
1545
|
+
], { ascii: this.settingsManager.getSymbolPreset() === "ascii" });
|
|
1563
1546
|
}
|
|
1564
1547
|
buildStartupShortcuts() {
|
|
1565
1548
|
const entry = (sigil, label) => `${theme.fg("accent", sigil)} ${theme.fg("muted", label)}`;
|
|
@@ -1727,10 +1710,57 @@ export class InteractiveMode {
|
|
|
1727
1710
|
async handleFatalRuntimeError(prefix, error) {
|
|
1728
1711
|
const message = error instanceof Error ? error.message : String(error);
|
|
1729
1712
|
this.showError(`${prefix}: ${message}`);
|
|
1713
|
+
const extensionHint = this.getCrashExtensionHint(error);
|
|
1714
|
+
if (extensionHint) {
|
|
1715
|
+
this.chatContainer.addChild(new Text(theme.fg("warning", extensionHint), this.outputPad, 0));
|
|
1716
|
+
}
|
|
1717
|
+
if (this.recordCrash("fatal_error", error)) {
|
|
1718
|
+
this.chatContainer.addChild(new Text(theme.fg("muted", this.crashReportInstructions()), this.outputPad, 0));
|
|
1719
|
+
}
|
|
1730
1720
|
stopThemeWatcher();
|
|
1731
1721
|
this.stop("transcript");
|
|
1732
1722
|
process.exit(1);
|
|
1733
1723
|
}
|
|
1724
|
+
getCrashExtensionHint(error) {
|
|
1725
|
+
try {
|
|
1726
|
+
return formatCrashExtensionHint(findExtensionStackMatches(error instanceof Error ? error.stack : undefined, this.session.resourceLoader.getExtensions().extensions));
|
|
1727
|
+
}
|
|
1728
|
+
catch {
|
|
1729
|
+
return undefined;
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
/** Persist a crash so the next start can point the user at `/bug`. Returns false when nothing was written. */
|
|
1733
|
+
recordCrash(kind, error) {
|
|
1734
|
+
try {
|
|
1735
|
+
return (recordCrash({
|
|
1736
|
+
kind,
|
|
1737
|
+
error,
|
|
1738
|
+
sessionFile: this.session.sessionFile,
|
|
1739
|
+
cwd: this.session.sessionManager.getCwd(),
|
|
1740
|
+
}) !== undefined);
|
|
1741
|
+
}
|
|
1742
|
+
catch {
|
|
1743
|
+
return false;
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
crashReportInstructions() {
|
|
1747
|
+
const resume = this.session.sessionFile ? `run \`${APP_NAME} -r\` to resume the session, then` : "start pi and";
|
|
1748
|
+
return `To report this crash: ${resume} run /bug. The crash details are attached automatically.`;
|
|
1749
|
+
}
|
|
1750
|
+
suggestBugReport() {
|
|
1751
|
+
if (this.bugReportHintShown)
|
|
1752
|
+
return;
|
|
1753
|
+
this.bugReportHintShown = true;
|
|
1754
|
+
this.chatContainer.addChild(new Text(theme.fg("muted", `If this looks like a ${APP_NAME} bug, /bug exports a report locally without uploading it.`), this.outputPad, 0));
|
|
1755
|
+
this.ui.requestRender();
|
|
1756
|
+
}
|
|
1757
|
+
maybeSuggestBugReport(message) {
|
|
1758
|
+
if (message.stopReason !== "error" || isRetryableAssistantError(message))
|
|
1759
|
+
return;
|
|
1760
|
+
if (/\b(?:abort(?:ed)?|cancel(?:l?ed)?)\b/i.test(message.errorMessage ?? ""))
|
|
1761
|
+
return;
|
|
1762
|
+
this.suggestBugReport();
|
|
1763
|
+
}
|
|
1734
1764
|
renderCurrentSessionState() {
|
|
1735
1765
|
this.loadedResourcesContainer.clear();
|
|
1736
1766
|
this.chatContainer.clear();
|
|
@@ -1758,8 +1788,12 @@ export class InteractiveMode {
|
|
|
1758
1788
|
/**
|
|
1759
1789
|
* Get a registered tool definition by name (for custom rendering).
|
|
1760
1790
|
*/
|
|
1791
|
+
/**
|
|
1792
|
+
* Extension-registered definition, falling back to the built-in one. The renderer components take
|
|
1793
|
+
* whatever this returns, so they never reach into the tool registry themselves.
|
|
1794
|
+
*/
|
|
1761
1795
|
getRegisteredToolDefinition(toolName) {
|
|
1762
|
-
return this.session.getToolDefinition(toolName);
|
|
1796
|
+
return withBuiltInRenderers(toolName, this.session.getToolDefinition(toolName));
|
|
1763
1797
|
}
|
|
1764
1798
|
getMarkdownTransformers() {
|
|
1765
1799
|
return [this.mermaidMarkdownTransformer, ...this.session.extensionRunner.getMarkdownTransformers()];
|
|
@@ -1829,38 +1863,58 @@ export class InteractiveMode {
|
|
|
1829
1863
|
this.footerDataProvider.setExtensionStatus(key, text);
|
|
1830
1864
|
this.ui.requestRender();
|
|
1831
1865
|
}
|
|
1866
|
+
setEditorWorkingStatusIndicator(indicator) {
|
|
1867
|
+
this.defaultEditor.setWorkingStatusIndicator(undefined);
|
|
1868
|
+
if (!isWorkingStatusEditor(this.editor))
|
|
1869
|
+
return false;
|
|
1870
|
+
this.editor.setWorkingStatusIndicator(indicator);
|
|
1871
|
+
return true;
|
|
1872
|
+
}
|
|
1832
1873
|
showStatusIndicator(indicator) {
|
|
1833
1874
|
this.activeStatusIndicator?.dispose();
|
|
1834
1875
|
this.activeStatusIndicator = indicator;
|
|
1876
|
+
this.activeWorkingIndicatorEmbedded = false;
|
|
1835
1877
|
this.statusContainer.clear();
|
|
1878
|
+
this.setEditorWorkingStatusIndicator(undefined);
|
|
1836
1879
|
if (trayOwnsActivity(indicator.kind, this.customFooter !== undefined)) {
|
|
1837
1880
|
this.footer.setActivity(indicator);
|
|
1838
1881
|
return;
|
|
1839
1882
|
}
|
|
1840
1883
|
this.footer.setActivity(undefined);
|
|
1884
|
+
// With a custom footer there is no tray; an editor that opts in to
|
|
1885
|
+
// `embedWorkingStatus` then carries the working status in its border.
|
|
1886
|
+
if (indicator instanceof WorkingStatusIndicator && this.setEditorWorkingStatusIndicator(indicator)) {
|
|
1887
|
+
this.activeWorkingIndicatorEmbedded = true;
|
|
1888
|
+
return;
|
|
1889
|
+
}
|
|
1841
1890
|
this.statusContainer.addChild(indicator);
|
|
1842
1891
|
}
|
|
1843
1892
|
clearStatusIndicator(kind) {
|
|
1844
1893
|
if (kind && this.activeStatusIndicator?.kind !== kind) {
|
|
1845
1894
|
return;
|
|
1846
1895
|
}
|
|
1847
|
-
const
|
|
1848
|
-
const wasInTray = this.
|
|
1849
|
-
|
|
1850
|
-
|
|
1896
|
+
const clearedIndicator = this.activeStatusIndicator;
|
|
1897
|
+
const wasInTray = clearedIndicator !== undefined && trayOwnsActivity(clearedIndicator.kind, this.customFooter !== undefined);
|
|
1898
|
+
const clearedIndicatorWasEmbedded = clearedIndicator?.kind === "working" && this.activeWorkingIndicatorEmbedded;
|
|
1899
|
+
clearedIndicator?.dispose();
|
|
1851
1900
|
this.activeStatusIndicator = undefined;
|
|
1901
|
+
this.activeWorkingIndicatorEmbedded = false;
|
|
1852
1902
|
this.footer?.setActivity(undefined);
|
|
1853
1903
|
this.statusContainer.clear();
|
|
1854
|
-
|
|
1855
|
-
//
|
|
1856
|
-
|
|
1904
|
+
this.setEditorWorkingStatusIndicator(undefined);
|
|
1905
|
+
// Neither the tray nor the editor border occupied those rows, so they must
|
|
1906
|
+
// not leave the idle filler behind to reserve them.
|
|
1907
|
+
if (wasInTray || clearedIndicatorWasEmbedded)
|
|
1857
1908
|
return;
|
|
1858
|
-
if (
|
|
1909
|
+
if (clearedIndicator && this.options.tuiMode === "regular" && this.ui.getClearOnShrink()) {
|
|
1859
1910
|
this.statusContainer.addChild(this.idleStatus);
|
|
1860
1911
|
}
|
|
1861
1912
|
}
|
|
1862
1913
|
showWorkingStatusIndicator() {
|
|
1863
|
-
|
|
1914
|
+
const colorFn = !trayOwnsActivity("working", this.customFooter !== undefined) && isWorkingStatusEditor(this.editor)
|
|
1915
|
+
? (text) => (this.editor.borderColor ?? theme.getThinkingBorderColor(this.session.thinkingLevel || "off"))(text)
|
|
1916
|
+
: undefined;
|
|
1917
|
+
this.showStatusIndicator(new WorkingStatusIndicator(this.ui, this.workingMessage ?? this.defaultWorkingMessage, this.workingIndicatorOptions, colorFn));
|
|
1864
1918
|
}
|
|
1865
1919
|
setWorkingVisible(visible) {
|
|
1866
1920
|
this.workingVisible = visible;
|
|
@@ -2386,6 +2440,15 @@ export class InteractiveMode {
|
|
|
2386
2440
|
this.editor = this.defaultEditor;
|
|
2387
2441
|
}
|
|
2388
2442
|
this.editorContainer.addChild(this.editor);
|
|
2443
|
+
// A tray-owned working status stays in the footer whichever editor is mounted.
|
|
2444
|
+
if (this.activeStatusIndicator instanceof WorkingStatusIndicator &&
|
|
2445
|
+
!trayOwnsActivity("working", this.customFooter !== undefined)) {
|
|
2446
|
+
this.statusContainer.clear();
|
|
2447
|
+
this.activeWorkingIndicatorEmbedded = this.setEditorWorkingStatusIndicator(this.activeStatusIndicator);
|
|
2448
|
+
if (!this.activeWorkingIndicatorEmbedded) {
|
|
2449
|
+
this.statusContainer.addChild(this.activeStatusIndicator);
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2389
2452
|
this.ui.setFocus(this.editor);
|
|
2390
2453
|
this.ui.requestRender();
|
|
2391
2454
|
}
|
|
@@ -2664,6 +2727,12 @@ export class InteractiveMode {
|
|
|
2664
2727
|
this.editor.setText("");
|
|
2665
2728
|
return;
|
|
2666
2729
|
}
|
|
2730
|
+
if (text === "/bug" || text.startsWith("/bug ")) {
|
|
2731
|
+
const hint = text.slice("/bug".length).trim();
|
|
2732
|
+
this.editor.setText("");
|
|
2733
|
+
await this.handleBugCommand(hint ? hint : undefined);
|
|
2734
|
+
return;
|
|
2735
|
+
}
|
|
2667
2736
|
if (text === "/copy") {
|
|
2668
2737
|
await this.handleCopyCommand();
|
|
2669
2738
|
this.editor.setText("");
|
|
@@ -2870,10 +2939,44 @@ export class InteractiveMode {
|
|
|
2870
2939
|
this.ui.requestRender();
|
|
2871
2940
|
break;
|
|
2872
2941
|
case "entry_appended":
|
|
2942
|
+
if (this.entriesRenderedByBoundaryCompaction.delete(event.entry.id))
|
|
2943
|
+
break;
|
|
2873
2944
|
if (event.entry.type === "custom") {
|
|
2874
2945
|
this.addCustomEntryToChat(event.entry);
|
|
2875
2946
|
this.ui.requestRender();
|
|
2876
2947
|
}
|
|
2948
|
+
else if (event.entry.type === "usage" && event.entry.kind === "cache_warm") {
|
|
2949
|
+
this.addCacheWarmingUsage(event.entry);
|
|
2950
|
+
this.ui.requestRender();
|
|
2951
|
+
}
|
|
2952
|
+
else if (event.entry.type === "custom_message" && event.entry.display) {
|
|
2953
|
+
this.addMessageToChat(createCustomMessage(event.entry.customType, event.entry.content, event.entry.display, event.entry.details, event.entry.timestamp));
|
|
2954
|
+
this.ui.requestRender();
|
|
2955
|
+
}
|
|
2956
|
+
else if (event.entry.type === "compaction") {
|
|
2957
|
+
const entries = this.sessionManager.buildContextEntries();
|
|
2958
|
+
if (entries[0]?.id !== event.entry.id)
|
|
2959
|
+
break;
|
|
2960
|
+
this.chatContainer.clear();
|
|
2961
|
+
const branch = this.sessionManager.getBranch();
|
|
2962
|
+
const compactionIndex = branch.findIndex((entry) => entry.id === event.entry.id);
|
|
2963
|
+
const entriesAfterCompaction = new Set(branch.slice(compactionIndex + 1).map((entry) => entry.id));
|
|
2964
|
+
const retainedEntries = entries.slice(1);
|
|
2965
|
+
this.renderSessionEntries(retainedEntries.filter((entry) => !entriesAfterCompaction.has(entry.id)));
|
|
2966
|
+
this.addMessageToChat(createCompactionSummaryMessage(event.entry.summary, event.entry.tokensBefore, event.entry.timestamp));
|
|
2967
|
+
if (event.entry.usage) {
|
|
2968
|
+
this.addCompactionCostNotice({
|
|
2969
|
+
type: "compaction_cost",
|
|
2970
|
+
kind: "compaction",
|
|
2971
|
+
usage: event.entry.usage,
|
|
2972
|
+
});
|
|
2973
|
+
}
|
|
2974
|
+
this.renderSessionEntries(retainedEntries.filter((entry) => entriesAfterCompaction.has(entry.id)));
|
|
2975
|
+
for (const entryId of entriesAfterCompaction)
|
|
2976
|
+
this.entriesRenderedByBoundaryCompaction.add(entryId);
|
|
2977
|
+
this.footer.invalidate();
|
|
2978
|
+
this.ui.requestRender();
|
|
2979
|
+
}
|
|
2877
2980
|
break;
|
|
2878
2981
|
case "session_info_changed":
|
|
2879
2982
|
this.updateTerminalTitle();
|
|
@@ -2895,7 +2998,9 @@ export class InteractiveMode {
|
|
|
2895
2998
|
this.ui.requestRender();
|
|
2896
2999
|
}
|
|
2897
3000
|
else if (event.message.role === "assistant") {
|
|
3001
|
+
this.retriedAttempt = undefined;
|
|
2898
3002
|
this.streamingComponent = new AssistantMessageComponent(undefined, this.isThinkingHidden(), this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad, this.getMarkdownTransformers());
|
|
3003
|
+
this.adoptChatDetail(this.streamingComponent);
|
|
2899
3004
|
this.streamingMessage = event.message;
|
|
2900
3005
|
this.chatContainer.addChild(this.streamingComponent);
|
|
2901
3006
|
this.streamingComponent.updateContent(this.streamingMessage, true);
|
|
@@ -2949,6 +3054,9 @@ export class InteractiveMode {
|
|
|
2949
3054
|
if (!errorMessage) {
|
|
2950
3055
|
errorMessage = this.streamingMessage.errorMessage || "Error";
|
|
2951
3056
|
}
|
|
3057
|
+
if (this.streamingMessage.stopReason === "error" && this.pendingTools.size === 0) {
|
|
3058
|
+
this.retriedAttempt = { error: errorMessage, component: this.streamingComponent };
|
|
3059
|
+
}
|
|
2952
3060
|
for (const [, component] of this.pendingTools.entries()) {
|
|
2953
3061
|
component.updateResult({
|
|
2954
3062
|
content: [{ type: "text", text: errorMessage }],
|
|
@@ -2956,12 +3064,14 @@ export class InteractiveMode {
|
|
|
2956
3064
|
});
|
|
2957
3065
|
}
|
|
2958
3066
|
this.clearPendingTools();
|
|
3067
|
+
this.maybeSuggestBugReport(this.streamingMessage);
|
|
2959
3068
|
}
|
|
2960
3069
|
else {
|
|
2961
3070
|
// Args are now complete - trigger diff computation for edit tools
|
|
2962
3071
|
for (const [, component] of this.pendingTools.entries()) {
|
|
2963
3072
|
component.setArgsComplete();
|
|
2964
3073
|
}
|
|
3074
|
+
this.maybeShowThinkingDropNotice(this.streamingMessage);
|
|
2965
3075
|
this.maybeShowCacheMissNotice(this.streamingMessage);
|
|
2966
3076
|
}
|
|
2967
3077
|
this.streamingComponent = undefined;
|
|
@@ -3076,7 +3186,7 @@ export class InteractiveMode {
|
|
|
3076
3186
|
}
|
|
3077
3187
|
else {
|
|
3078
3188
|
this.chatContainer.addChild(new Spacer(1));
|
|
3079
|
-
this.chatContainer.addChild(
|
|
3189
|
+
this.chatContainer.addChild(errorText(event.errorMessage, this.toolOutputExpanded, 1));
|
|
3080
3190
|
}
|
|
3081
3191
|
}
|
|
3082
3192
|
void this.flushCompactionQueue({ willRetry: event.willRetry });
|
|
@@ -3084,9 +3194,16 @@ export class InteractiveMode {
|
|
|
3084
3194
|
break;
|
|
3085
3195
|
}
|
|
3086
3196
|
case "auto_retry_start": {
|
|
3197
|
+
// The session drops the failed attempt from agent state; the chat drops it too, and
|
|
3198
|
+
// the retry indicator carries the attempt count instead.
|
|
3199
|
+
if (this.retriedAttempt?.component) {
|
|
3200
|
+
this.chatContainer.removeChild(this.retriedAttempt.component);
|
|
3201
|
+
this.retriedAttempt = { error: this.retriedAttempt.error };
|
|
3202
|
+
}
|
|
3087
3203
|
// Set up escape to abort retry
|
|
3088
3204
|
this.retryEscapeHandler = this.defaultEditor.onEscape;
|
|
3089
3205
|
this.defaultEditor.onEscape = () => {
|
|
3206
|
+
this.retryCancelled = true;
|
|
3090
3207
|
this.session.abortRetry();
|
|
3091
3208
|
};
|
|
3092
3209
|
this.showStatusIndicator(new RetryStatusIndicator(this.ui, event.attempt, event.maxAttempts, event.delayMs));
|
|
@@ -3102,8 +3219,16 @@ export class InteractiveMode {
|
|
|
3102
3219
|
this.clearStatusIndicator("retry");
|
|
3103
3220
|
// Show error only on final failure (success shows normal response)
|
|
3104
3221
|
if (!event.success) {
|
|
3105
|
-
|
|
3222
|
+
const lastAttempt = this.retriedAttempt;
|
|
3223
|
+
if (lastAttempt?.component)
|
|
3224
|
+
this.chatContainer.removeChild(lastAttempt.component);
|
|
3225
|
+
const outcome = this.retryCancelled
|
|
3226
|
+
? "retry cancelled"
|
|
3227
|
+
: `gave up after ${event.attempt} ${event.attempt === 1 ? "retry" : "retries"}`;
|
|
3228
|
+
this.showError(lastAttempt?.error ?? event.finalError ?? "Unknown error", ` (${outcome})`);
|
|
3106
3229
|
}
|
|
3230
|
+
this.retriedAttempt = undefined;
|
|
3231
|
+
this.retryCancelled = false;
|
|
3107
3232
|
this.ui.requestRender();
|
|
3108
3233
|
break;
|
|
3109
3234
|
}
|
|
@@ -3229,6 +3354,8 @@ export class InteractiveMode {
|
|
|
3229
3354
|
this.chatContainer.addChild(component);
|
|
3230
3355
|
break;
|
|
3231
3356
|
}
|
|
3357
|
+
case "system":
|
|
3358
|
+
break;
|
|
3232
3359
|
case "user": {
|
|
3233
3360
|
const textContent = this.getUserMessageText(message);
|
|
3234
3361
|
if (textContent) {
|
|
@@ -3260,6 +3387,7 @@ export class InteractiveMode {
|
|
|
3260
3387
|
}
|
|
3261
3388
|
case "assistant": {
|
|
3262
3389
|
const assistantComponent = new AssistantMessageComponent(message, this.isThinkingHidden(), this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad, this.getMarkdownTransformers());
|
|
3390
|
+
this.adoptChatDetail(assistantComponent);
|
|
3263
3391
|
this.chatContainer.addChild(assistantComponent);
|
|
3264
3392
|
break;
|
|
3265
3393
|
}
|
|
@@ -3275,8 +3403,8 @@ export class InteractiveMode {
|
|
|
3275
3403
|
renderSessionItems(items, options = {}) {
|
|
3276
3404
|
this.clearPendingTools();
|
|
3277
3405
|
const renderedPendingTools = new Map();
|
|
3278
|
-
// Cache
|
|
3279
|
-
//
|
|
3406
|
+
// Cache misses are not persisted, unlike successful cache-warming usage.
|
|
3407
|
+
// Re-derive them and inject them after the assistant messages that paid for them.
|
|
3280
3408
|
const cacheMisses = this.settingsManager.getShowCacheMissNotices()
|
|
3281
3409
|
? collectCacheMisses(this.sessionManager.getEntries(), this.session.modelRuntime)
|
|
3282
3410
|
: new Map();
|
|
@@ -3289,6 +3417,10 @@ export class InteractiveMode {
|
|
|
3289
3417
|
this.addCustomEntryToChat(item);
|
|
3290
3418
|
continue;
|
|
3291
3419
|
}
|
|
3420
|
+
if (isUsageSessionEntry(item)) {
|
|
3421
|
+
this.addCacheWarmingUsage(item);
|
|
3422
|
+
continue;
|
|
3423
|
+
}
|
|
3292
3424
|
if (isCompactionCostNotice(item)) {
|
|
3293
3425
|
this.addCompactionCostNotice(item);
|
|
3294
3426
|
continue;
|
|
@@ -3357,7 +3489,7 @@ export class InteractiveMode {
|
|
|
3357
3489
|
*/
|
|
3358
3490
|
renderSessionEntries(entries, options = {}) {
|
|
3359
3491
|
const items = entries.flatMap((entry) => {
|
|
3360
|
-
if (entry.type === "custom") {
|
|
3492
|
+
if (entry.type === "custom" || (entry.type === "usage" && entry.kind === "cache_warm")) {
|
|
3361
3493
|
return [entry];
|
|
3362
3494
|
}
|
|
3363
3495
|
const messages = sessionEntryToContextMessages(entry);
|
|
@@ -3368,6 +3500,12 @@ export class InteractiveMode {
|
|
|
3368
3500
|
});
|
|
3369
3501
|
this.renderSessionItems(items, options);
|
|
3370
3502
|
}
|
|
3503
|
+
addCacheWarmingUsage(entry) {
|
|
3504
|
+
if (!this.settingsManager.getShowCacheMissNotices())
|
|
3505
|
+
return;
|
|
3506
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3507
|
+
this.chatContainer.addChild(new Text(theme.fg("dim", formatCacheWarmingUsage(entry)), 1, 0));
|
|
3508
|
+
}
|
|
3371
3509
|
/**
|
|
3372
3510
|
* Render billing usage for a compaction or branch summary. The notice is derived
|
|
3373
3511
|
* from persisted summary usage and is not stored as a separate session entry.
|
|
@@ -3382,6 +3520,43 @@ export class InteractiveMode {
|
|
|
3382
3520
|
this.chatContainer.addChild(new Spacer(1));
|
|
3383
3521
|
this.chatContainer.addChild(new Text(theme.fg("warning", `${label}: ${formatTokens(tokens)} tokens billed${cost}`), 1, 0));
|
|
3384
3522
|
}
|
|
3523
|
+
static countDroppedThinkingBlocks(message) {
|
|
3524
|
+
let count = 0;
|
|
3525
|
+
for (const diagnostic of message.diagnostics ?? []) {
|
|
3526
|
+
if (diagnostic.type !== "anthropic_input_transformations")
|
|
3527
|
+
continue;
|
|
3528
|
+
const transformations = diagnostic.details?.transformations;
|
|
3529
|
+
if (!Array.isArray(transformations))
|
|
3530
|
+
continue;
|
|
3531
|
+
count += transformations.filter((transformation) => typeof transformation === "object" &&
|
|
3532
|
+
transformation !== null &&
|
|
3533
|
+
transformation.type === "thinking_dropped").length;
|
|
3534
|
+
}
|
|
3535
|
+
return count;
|
|
3536
|
+
}
|
|
3537
|
+
maybeShowThinkingDropNotice(message) {
|
|
3538
|
+
if (!this.settingsManager.getShowCacheMissNotices())
|
|
3539
|
+
return;
|
|
3540
|
+
const droppedCount = InteractiveMode.countDroppedThinkingBlocks(message);
|
|
3541
|
+
if (droppedCount === 0)
|
|
3542
|
+
return;
|
|
3543
|
+
let previousDroppedCount = 0;
|
|
3544
|
+
// message_end reaches the UI before the current message is persisted,
|
|
3545
|
+
// so the branch's last assistant message is the previous response.
|
|
3546
|
+
const branch = this.sessionManager.getBranch();
|
|
3547
|
+
for (let i = branch.length - 1; i >= 0; i--) {
|
|
3548
|
+
const entry = branch[i];
|
|
3549
|
+
if (entry.type === "message" && entry.message.role === "assistant") {
|
|
3550
|
+
previousDroppedCount = InteractiveMode.countDroppedThinkingBlocks(entry.message);
|
|
3551
|
+
break;
|
|
3552
|
+
}
|
|
3553
|
+
}
|
|
3554
|
+
if (droppedCount <= previousDroppedCount)
|
|
3555
|
+
return;
|
|
3556
|
+
const noun = droppedCount === 1 ? "thinking block" : "thinking blocks";
|
|
3557
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3558
|
+
this.chatContainer.addChild(new Text(theme.fg("warning", `Anthropic dropped ${droppedCount} ${noun} (details in session)`), 1, 0));
|
|
3559
|
+
}
|
|
3385
3560
|
/**
|
|
3386
3561
|
* Show a transcript notice when a completed assistant message paid for a
|
|
3387
3562
|
* significant cache miss. Only states observable facts: the miss itself,
|
|
@@ -3548,6 +3723,12 @@ export class InteractiveMode {
|
|
|
3548
3723
|
catch { }
|
|
3549
3724
|
console.error("apex-code exiting due to uncaughtException:");
|
|
3550
3725
|
console.error(error);
|
|
3726
|
+
const extensionHint = this.getCrashExtensionHint(error);
|
|
3727
|
+
if (extensionHint)
|
|
3728
|
+
console.error(`\n${extensionHint}`);
|
|
3729
|
+
if (this.recordCrash("uncaught_exception", error)) {
|
|
3730
|
+
console.error(`\n${this.crashReportInstructions()}`);
|
|
3731
|
+
}
|
|
3551
3732
|
process.exit(1);
|
|
3552
3733
|
}
|
|
3553
3734
|
/**
|
|
@@ -3683,6 +3864,7 @@ export class InteractiveMode {
|
|
|
3683
3864
|
const level = this.session.thinkingLevel || "off";
|
|
3684
3865
|
this.editor.borderColor = theme.getThinkingBorderColor(level);
|
|
3685
3866
|
}
|
|
3867
|
+
this.activeStatusIndicator?.invalidate();
|
|
3686
3868
|
this.ui.requestRender();
|
|
3687
3869
|
}
|
|
3688
3870
|
cycleThinkingLevel() {
|
|
@@ -3728,27 +3910,40 @@ export class InteractiveMode {
|
|
|
3728
3910
|
if (hasEditDiffsExpansion(component))
|
|
3729
3911
|
component.setEditDiffsExpanded(this.editDiffsExpanded);
|
|
3730
3912
|
}
|
|
3913
|
+
/** The expand key. A level the user picks is saved, so the next session opens at it. */
|
|
3731
3914
|
cycleChatDetail() {
|
|
3732
|
-
this.
|
|
3915
|
+
this.chooseChatDetail(nextChatDetail(this.chatDetail));
|
|
3916
|
+
}
|
|
3917
|
+
chooseChatDetail(detail) {
|
|
3918
|
+
this.settingsManager.setChatDetail(detail);
|
|
3919
|
+
this.setChatDetail(detail);
|
|
3920
|
+
}
|
|
3921
|
+
/** Adopt a saved level before the transcript renders; unset opens collapsed. */
|
|
3922
|
+
loadChatDetail(saved) {
|
|
3923
|
+
this.assignChatDetail(saved ?? INITIAL_CHAT_DETAIL);
|
|
3924
|
+
}
|
|
3925
|
+
assignChatDetail(detail) {
|
|
3926
|
+
const view = chatDetailView(detail);
|
|
3927
|
+
this.chatDetail = detail;
|
|
3928
|
+
this.toolOutputExpanded = view.toolOutputExpanded;
|
|
3929
|
+
this.editDiffsExpanded = view.editDiffsExpanded;
|
|
3733
3930
|
}
|
|
3734
3931
|
/**
|
|
3735
|
-
* Presentation only. This never rewrites messages
|
|
3736
|
-
*
|
|
3737
|
-
*
|
|
3932
|
+
* Presentation only. This never rewrites messages or the session transcript,
|
|
3933
|
+
* and never the hide-thinking preference, which `all` overrides unsaved.
|
|
3934
|
+
* Saving the level itself is `chooseChatDetail`'s job, for user choices only.
|
|
3738
3935
|
*/
|
|
3739
3936
|
setChatDetail(detail) {
|
|
3740
3937
|
if (detail === this.chatDetail)
|
|
3741
3938
|
return;
|
|
3742
|
-
this.
|
|
3743
|
-
const view = chatDetailView(detail);
|
|
3744
|
-
this.toolOutputExpanded = view.toolOutputExpanded;
|
|
3745
|
-
this.editDiffsExpanded = view.editDiffsExpanded;
|
|
3939
|
+
this.assignChatDetail(detail);
|
|
3746
3940
|
this.applyChatDetail();
|
|
3747
3941
|
this.showStatus(`Conversation detail: ${detail}`);
|
|
3748
3942
|
}
|
|
3749
3943
|
/** True when thinking blocks are hidden right now, setting and detail level combined. */
|
|
3750
3944
|
isThinkingHidden() {
|
|
3751
|
-
|
|
3945
|
+
const { thinking } = chatDetailView(this.chatDetail);
|
|
3946
|
+
return thinking === "preference" ? this.hideThinkingBlock : thinking === "collapsed";
|
|
3752
3947
|
}
|
|
3753
3948
|
applyChatDetail() {
|
|
3754
3949
|
const activeHeader = this.customHeader ?? this.builtInHeader;
|
|
@@ -3775,7 +3970,8 @@ export class InteractiveMode {
|
|
|
3775
3970
|
*
|
|
3776
3971
|
* Collapsing maps to `details` rather than `overview`, because an extension
|
|
3777
3972
|
* asking to collapse tool output has not asked to hide edit diffs, and
|
|
3778
|
-
* `details` is the level where only tool output is collapsed.
|
|
3973
|
+
* `details` is the level where only tool output is collapsed. Not saved: an
|
|
3974
|
+
* extension's request should not decide how the next session opens.
|
|
3779
3975
|
*/
|
|
3780
3976
|
setToolsExpanded(expanded) {
|
|
3781
3977
|
this.setChatDetail(expanded ? "all" : "details");
|
|
@@ -3791,8 +3987,16 @@ export class InteractiveMode {
|
|
|
3791
3987
|
}
|
|
3792
3988
|
toggleThinkingBlockVisibility() {
|
|
3793
3989
|
this.offerFirstUseHint("thinking");
|
|
3794
|
-
this.
|
|
3795
|
-
|
|
3990
|
+
if (this.chatDetail === "overview") {
|
|
3991
|
+
// Overview hides thinking regardless of the preference. Treat the toggle as a request to show it.
|
|
3992
|
+
this.hideThinkingBlock = false;
|
|
3993
|
+
this.settingsManager.setHideThinkingBlock(false);
|
|
3994
|
+
this.chooseChatDetail("details");
|
|
3995
|
+
}
|
|
3996
|
+
else {
|
|
3997
|
+
this.hideThinkingBlock = !this.hideThinkingBlock;
|
|
3998
|
+
this.settingsManager.setHideThinkingBlock(this.hideThinkingBlock);
|
|
3999
|
+
}
|
|
3796
4000
|
this.updateThinkingBlockVisibility();
|
|
3797
4001
|
this.showStatus(`Thinking blocks: ${this.hideThinkingBlock ? "hidden" : "visible"}`);
|
|
3798
4002
|
}
|
|
@@ -3821,9 +4025,9 @@ export class InteractiveMode {
|
|
|
3821
4025
|
this.editor.setText("");
|
|
3822
4026
|
this.ui.requestRender();
|
|
3823
4027
|
}
|
|
3824
|
-
showError(errorMessage) {
|
|
4028
|
+
showError(errorMessage, outcome) {
|
|
3825
4029
|
this.chatContainer.addChild(new Spacer(1));
|
|
3826
|
-
this.chatContainer.addChild(
|
|
4030
|
+
this.chatContainer.addChild(errorText(`Error: ${errorMessage}`, this.toolOutputExpanded, this.outputPad, outcome));
|
|
3827
4031
|
this.ui.requestRender();
|
|
3828
4032
|
}
|
|
3829
4033
|
showWarning(warningMessage) {
|
|
@@ -3922,7 +4126,7 @@ export class InteractiveMode {
|
|
|
3922
4126
|
if (allQueued.length === 0) {
|
|
3923
4127
|
this.updatePendingMessagesDisplay();
|
|
3924
4128
|
if (options?.abort) {
|
|
3925
|
-
this.
|
|
4129
|
+
void this.session.abort();
|
|
3926
4130
|
}
|
|
3927
4131
|
return 0;
|
|
3928
4132
|
}
|
|
@@ -3932,7 +4136,7 @@ export class InteractiveMode {
|
|
|
3932
4136
|
this.editor.setText(combinedText);
|
|
3933
4137
|
this.updatePendingMessagesDisplay();
|
|
3934
4138
|
if (options?.abort) {
|
|
3935
|
-
this.
|
|
4139
|
+
void this.session.abort();
|
|
3936
4140
|
}
|
|
3937
4141
|
return allQueued.length;
|
|
3938
4142
|
}
|
|
@@ -4132,6 +4336,7 @@ export class InteractiveMode {
|
|
|
4132
4336
|
followUpMode: this.session.followUpMode,
|
|
4133
4337
|
transport: this.settingsManager.getTransport(),
|
|
4134
4338
|
httpIdleTimeoutMs: this.settingsManager.getHttpIdleTimeoutMs(),
|
|
4339
|
+
cacheWarmingMode: this.settingsManager.getCacheWarmingMode(),
|
|
4135
4340
|
thinkingLevel: this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL,
|
|
4136
4341
|
availableThinkingLevels: [...THINKING_LEVEL_OPTIONS],
|
|
4137
4342
|
modelThinkingLevels: this.settingsManager.getAllModelThinkingLevels(),
|
|
@@ -4150,6 +4355,7 @@ export class InteractiveMode {
|
|
|
4150
4355
|
permissionMode,
|
|
4151
4356
|
editorPaddingX: this.settingsManager.getEditorPaddingX(),
|
|
4152
4357
|
outputPad: this.settingsManager.getOutputPad(),
|
|
4358
|
+
chatDetail: this.chatDetail,
|
|
4153
4359
|
autocompleteMaxVisible: this.settingsManager.getAutocompleteMaxVisible(),
|
|
4154
4360
|
quietStartup: this.settingsManager.getQuietStartup(),
|
|
4155
4361
|
clearOnShrink: this.settingsManager.getClearOnShrink(),
|
|
@@ -4205,6 +4411,10 @@ export class InteractiveMode {
|
|
|
4205
4411
|
configureHttpDispatcher(timeoutMs);
|
|
4206
4412
|
this.showStatus(`HTTP idle timeout: ${formatHttpIdleTimeoutMs(timeoutMs)}`);
|
|
4207
4413
|
},
|
|
4414
|
+
onCacheWarmingModeChange: (mode) => {
|
|
4415
|
+
this.session.setCacheWarmingMode(mode);
|
|
4416
|
+
this.showStatus(`Cache warming: ${mode}`);
|
|
4417
|
+
},
|
|
4208
4418
|
onModelThinkingLevelChange: (provider, modelId, level) => {
|
|
4209
4419
|
this.settingsManager.setModelThinkingLevel(provider, modelId, level);
|
|
4210
4420
|
// If the override is for the current model, apply it to the session too
|
|
@@ -4231,6 +4441,7 @@ export class InteractiveMode {
|
|
|
4231
4441
|
void this.themeController.setThemeSetting(themeSetting);
|
|
4232
4442
|
},
|
|
4233
4443
|
onThemePreview: (themeName) => this.themeController.preview(themeName),
|
|
4444
|
+
onChatDetailChange: (detail) => this.chooseChatDetail(detail),
|
|
4234
4445
|
onHideThinkingBlockChange: (hidden) => {
|
|
4235
4446
|
this.hideThinkingBlock = hidden;
|
|
4236
4447
|
this.settingsManager.setHideThinkingBlock(hidden);
|
|
@@ -4766,6 +4977,11 @@ export class InteractiveMode {
|
|
|
4766
4977
|
this.restoreQueuedMessagesToEditor();
|
|
4767
4978
|
await this.session.abort();
|
|
4768
4979
|
}
|
|
4980
|
+
// Recheck after the dialogs and streaming abort, before replacing another operation's UI.
|
|
4981
|
+
if (this.session.isCompacting) {
|
|
4982
|
+
this.showError("Wait for the current compaction or tree navigation to finish before navigating the session tree.");
|
|
4983
|
+
return;
|
|
4984
|
+
}
|
|
4769
4985
|
// Set up escape handler and status indicator if summarizing
|
|
4770
4986
|
let showingSummaryIndicator = false;
|
|
4771
4987
|
const originalOnEscape = this.defaultEditor.onEscape;
|
|
@@ -4836,9 +5052,9 @@ export class InteractiveMode {
|
|
|
4836
5052
|
}
|
|
4837
5053
|
showSessionSelector() {
|
|
4838
5054
|
this.showSelector((done) => {
|
|
4839
|
-
const selector = new SessionSelectorComponent((onProgress) => SessionManager.list(this.sessionManager.getCwd(), this.sessionManager.getSessionDir(), onProgress), (onProgress) => this.sessionManager.usesDefaultSessionDir()
|
|
4840
|
-
? SessionManager.listAll(onProgress)
|
|
4841
|
-
: SessionManager.listAll(this.sessionManager.getSessionDir(), onProgress), async (sessionPath) => {
|
|
5055
|
+
const selector = new SessionSelectorComponent((onProgress, signal) => SessionManager.list(this.sessionManager.getCwd(), this.sessionManager.getSessionDir(), onProgress, signal), (onProgress, signal) => this.sessionManager.usesDefaultSessionDir()
|
|
5056
|
+
? SessionManager.listAll(onProgress, signal)
|
|
5057
|
+
: SessionManager.listAll(this.sessionManager.getSessionDir(), onProgress, signal), async (sessionPath) => {
|
|
4842
5058
|
done();
|
|
4843
5059
|
await this.handleResumeSession(sessionPath);
|
|
4844
5060
|
}, () => {
|
|
@@ -5122,67 +5338,89 @@ export class InteractiveMode {
|
|
|
5122
5338
|
}
|
|
5123
5339
|
async completeProviderAuthentication(providerId, providerName, authType, previousModel) {
|
|
5124
5340
|
const actionLabel = authType === "oauth" ? `Logged in to ${providerName}` : `Saved API key for ${providerName}`;
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5341
|
+
const session = this.session;
|
|
5342
|
+
// Dynamic catalogs may be empty until the first authenticated network refresh.
|
|
5343
|
+
const deferSelection = isUnknownModel(previousModel) &&
|
|
5344
|
+
hasDefaultModelProvider(providerId) &&
|
|
5345
|
+
!session.modelRuntime
|
|
5346
|
+
.getAvailableSnapshot()
|
|
5347
|
+
.some((model) => model.provider === providerId && model.id === defaultModelPerProvider[providerId]);
|
|
5348
|
+
const finishAuthentication = async () => {
|
|
5349
|
+
let selectedModel;
|
|
5350
|
+
let selectionError;
|
|
5351
|
+
if (isUnknownModel(previousModel)) {
|
|
5352
|
+
const availableModels = this.session.modelRuntime.getAvailableSnapshot();
|
|
5353
|
+
const providerModels = availableModels.filter((model) => model.provider === providerId);
|
|
5354
|
+
// Matches LLAMA_PROVIDER_ID from extensions/llama/provider.ts; kept inline to avoid coupling interactive mode to the built-in extension.
|
|
5355
|
+
if (providerId === "llama.cpp") {
|
|
5356
|
+
selectionError = llamaCppPostLoginGuidance(actionLabel, providerModels.length);
|
|
5357
|
+
}
|
|
5358
|
+
else if (!hasDefaultModelProvider(providerId)) {
|
|
5359
|
+
selectionError = `${actionLabel}, but no default model is configured for provider "${providerId}". Use /model to select a model.`;
|
|
5360
|
+
}
|
|
5361
|
+
else if (providerModels.length === 0) {
|
|
5362
|
+
selectionError = `${actionLabel}, but no models are available for that provider. Use /model to select a model.`;
|
|
5145
5363
|
}
|
|
5146
5364
|
else {
|
|
5147
|
-
|
|
5148
|
-
|
|
5365
|
+
const defaultModelId = defaultModelPerProvider[providerId];
|
|
5366
|
+
// Radius catalogs vary by account; prefer balanced, then use catalog order.
|
|
5367
|
+
selectedModel =
|
|
5368
|
+
providerModels.find((model) => model.id === defaultModelId) ??
|
|
5369
|
+
(providerId === "radius" ? providerModels[0] : undefined);
|
|
5370
|
+
if (!selectedModel) {
|
|
5371
|
+
selectionError = `${actionLabel}, but its default model "${defaultModelId}" is not available. Use /model to select a model.`;
|
|
5149
5372
|
}
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5373
|
+
else {
|
|
5374
|
+
try {
|
|
5375
|
+
await this.session.setModel(selectedModel, { persist: true });
|
|
5376
|
+
}
|
|
5377
|
+
catch (error) {
|
|
5378
|
+
selectedModel = undefined;
|
|
5379
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5380
|
+
selectionError = `${actionLabel}, but selecting its default model failed: ${errorMessage}. Use /model to select a model.`;
|
|
5381
|
+
}
|
|
5154
5382
|
}
|
|
5155
5383
|
}
|
|
5156
5384
|
}
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
this.checkDaxnutsEasterEgg(selectedModel);
|
|
5165
|
-
}
|
|
5166
|
-
else {
|
|
5167
|
-
this.showStatus(`${actionLabel}. Credentials saved to ${getAuthPath()}`);
|
|
5168
|
-
if (selectionError) {
|
|
5169
|
-
this.showError(selectionError);
|
|
5385
|
+
await this.updateAvailableProviderCount();
|
|
5386
|
+
this.footer.invalidate();
|
|
5387
|
+
this.updateEditorBorderColor();
|
|
5388
|
+
if (selectedModel) {
|
|
5389
|
+
this.showStatus(`${actionLabel}. Selected ${selectedModel.id}. Credentials saved to ${getAuthPath()}`);
|
|
5390
|
+
void this.maybeWarnAboutAnthropicSubscriptionAuth(selectedModel);
|
|
5391
|
+
this.checkDaxnutsEasterEgg(selectedModel);
|
|
5170
5392
|
}
|
|
5171
5393
|
else {
|
|
5172
|
-
|
|
5394
|
+
this.showStatus(`${actionLabel}. Credentials saved to ${getAuthPath()}`);
|
|
5395
|
+
if (selectionError) {
|
|
5396
|
+
this.showError(selectionError);
|
|
5397
|
+
}
|
|
5398
|
+
else {
|
|
5399
|
+
void this.maybeWarnAboutAnthropicSubscriptionAuth();
|
|
5400
|
+
}
|
|
5173
5401
|
}
|
|
5402
|
+
};
|
|
5403
|
+
if (deferSelection) {
|
|
5404
|
+
this.showStatus(`${actionLabel}. Credentials saved to ${getAuthPath()}. Refreshing model catalog…`);
|
|
5405
|
+
}
|
|
5406
|
+
else {
|
|
5407
|
+
await finishAuthentication();
|
|
5174
5408
|
}
|
|
5175
5409
|
const controller = new AbortController();
|
|
5176
5410
|
const timeout = setTimeout(() => controller.abort(), 15_000);
|
|
5177
|
-
void
|
|
5411
|
+
void session.modelRuntime
|
|
5178
5412
|
.refresh({ providers: [providerId], signal: controller.signal })
|
|
5179
|
-
.then((result) => {
|
|
5413
|
+
.then(async (result) => {
|
|
5180
5414
|
if (result.aborted) {
|
|
5181
5415
|
this.showWarning(`${actionLabel}, but its model catalog refresh timed out; using cached models.`);
|
|
5182
5416
|
}
|
|
5183
5417
|
else if (result.errors.size > 0) {
|
|
5184
5418
|
this.showWarning(`${actionLabel}, but its model catalog could not be refreshed; using cached models.`);
|
|
5185
5419
|
}
|
|
5420
|
+
// Do not replace a model or session selected while the refresh was running.
|
|
5421
|
+
if (deferSelection && this.session === session && session.model === previousModel) {
|
|
5422
|
+
await finishAuthentication();
|
|
5423
|
+
}
|
|
5186
5424
|
this.updateAvailableProviderCount();
|
|
5187
5425
|
this.footer.invalidate();
|
|
5188
5426
|
this.ui.requestRender();
|
|
@@ -5364,12 +5602,11 @@ export class InteractiveMode {
|
|
|
5364
5602
|
}
|
|
5365
5603
|
this.resetExtensionUI();
|
|
5366
5604
|
const reloadBox = new Container();
|
|
5367
|
-
|
|
5368
|
-
reloadBox.addChild(new DynamicBorder(borderColor));
|
|
5605
|
+
reloadBox.addChild(new DynamicBorder());
|
|
5369
5606
|
reloadBox.addChild(new Spacer(1));
|
|
5370
|
-
reloadBox.addChild(new Text(theme.fg("muted", "Reloading keybindings, extensions, skills, prompts, themes, and context files..."),
|
|
5607
|
+
reloadBox.addChild(new Text(theme.fg("muted", "Reloading keybindings, extensions, skills, prompts, themes, and context files..."), 0, 0));
|
|
5371
5608
|
reloadBox.addChild(new Spacer(1));
|
|
5372
|
-
reloadBox.addChild(new DynamicBorder(
|
|
5609
|
+
reloadBox.addChild(new DynamicBorder());
|
|
5373
5610
|
const previousEditor = this.editor;
|
|
5374
5611
|
this.editorContainer.clear();
|
|
5375
5612
|
this.editorContainer.addChild(reloadBox);
|
|
@@ -5389,6 +5626,7 @@ export class InteractiveMode {
|
|
|
5389
5626
|
return;
|
|
5390
5627
|
}
|
|
5391
5628
|
this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
|
|
5629
|
+
this.loadChatDetail(this.settingsManager.getChatDetail());
|
|
5392
5630
|
this.outputPad = this.settingsManager.getOutputPad();
|
|
5393
5631
|
this.rebuildChatFromMessages();
|
|
5394
5632
|
chatRestoredBeforeSessionStart = true;
|
|
@@ -5586,6 +5824,17 @@ export class InteractiveMode {
|
|
|
5586
5824
|
}
|
|
5587
5825
|
}
|
|
5588
5826
|
}
|
|
5827
|
+
async handleBugCommand(hint) {
|
|
5828
|
+
await reportBug({
|
|
5829
|
+
session: this.session,
|
|
5830
|
+
ui: this.ui,
|
|
5831
|
+
editorContainer: this.editorContainer,
|
|
5832
|
+
editor: this.editor,
|
|
5833
|
+
keybindings: this.keybindings,
|
|
5834
|
+
showStatus: (message) => this.showStatus(message),
|
|
5835
|
+
showError: (message) => this.showError(message),
|
|
5836
|
+
}, hint);
|
|
5837
|
+
}
|
|
5589
5838
|
async handleCopyCommand(options = {}) {
|
|
5590
5839
|
if (options.preferSelection &&
|
|
5591
5840
|
this.ui instanceof TuiAltScreen &&
|
|
@@ -5671,6 +5920,15 @@ export class InteractiveMode {
|
|
|
5671
5920
|
}
|
|
5672
5921
|
info += `${theme.fg("dim", "Output:")} ${stats.tokens.output.toLocaleString()}\n`;
|
|
5673
5922
|
info += `${theme.fg("dim", "Total:")} ${stats.tokens.total.toLocaleString()}\n`;
|
|
5923
|
+
const cacheWarmingStatus = this.session.cacheWarmingStatus;
|
|
5924
|
+
info += `\n${theme.bold("Cache Warming")}\n`;
|
|
5925
|
+
info += `${theme.fg("dim", "Mode:")} ${this.settingsManager.getCacheWarmingMode()}\n`;
|
|
5926
|
+
info += `${theme.fg("dim", "Status:")} ${cacheWarmingStatus ? formatCacheWarmingStatus(cacheWarmingStatus) : "Inactive (cache warming unavailable)"}\n`;
|
|
5927
|
+
const decision = cacheWarmingStatus?.decision;
|
|
5928
|
+
if (decision?.economicsAvailable) {
|
|
5929
|
+
info += `${theme.fg("dim", "Cache miss penalty:")} $${decision.missCost.toFixed(3)}\n`;
|
|
5930
|
+
info += `${theme.fg("dim", "Refresh cost:")} $${decision.warmCost.toFixed(3)}\n`;
|
|
5931
|
+
}
|
|
5674
5932
|
if (stats.cost > 0 || cacheWaste.missedTokens > 0) {
|
|
5675
5933
|
info += `\n${theme.bold("Cost")}\n`;
|
|
5676
5934
|
info += `${theme.fg("dim", "Total:")} $${stats.cost.toFixed(3)}`;
|
|
@@ -5723,7 +5981,7 @@ export class InteractiveMode {
|
|
|
5723
5981
|
: "No changelog entries found.";
|
|
5724
5982
|
this.chatContainer.addChild(new Spacer(1));
|
|
5725
5983
|
this.chatContainer.addChild(new DynamicBorder());
|
|
5726
|
-
this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "What's
|
|
5984
|
+
this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "What's new")), 1, 0));
|
|
5727
5985
|
this.chatContainer.addChild(new Spacer(1));
|
|
5728
5986
|
this.chatContainer.addChild(new Markdown(changelogMarkdown, 1, 1, this.getMarkdownThemeWithSettings()));
|
|
5729
5987
|
this.chatContainer.addChild(new DynamicBorder());
|
|
@@ -5821,7 +6079,7 @@ export class InteractiveMode {
|
|
|
5821
6079
|
| \`${expandTools}\` | Cycle conversation detail (overview / details / all) |
|
|
5822
6080
|
| \`${toggleThinking}\` | Toggle thinking block visibility |
|
|
5823
6081
|
| \`${externalEditor}\` | Edit message in external editor |
|
|
5824
|
-
| \`${copyMessage}\` | Copy last assistant message |
|
|
6082
|
+
| \`${copyMessage}\` | Copy selection or last assistant message |
|
|
5825
6083
|
| \`${followUp}\` | Queue follow-up message |
|
|
5826
6084
|
| \`${dequeue}\` | Restore queued messages |
|
|
5827
6085
|
| \`${pasteImage}\` | Paste image or text from clipboard |
|
|
@@ -5952,12 +6210,19 @@ export class InteractiveMode {
|
|
|
5952
6210
|
async runBashCommand(command, excludeFromContext = false) {
|
|
5953
6211
|
const extensionRunner = this.session.extensionRunner;
|
|
5954
6212
|
// Emit user_bash event to let extensions intercept
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
6213
|
+
let eventResult;
|
|
6214
|
+
try {
|
|
6215
|
+
eventResult = await extensionRunner.emitUserBash({
|
|
6216
|
+
type: "user_bash",
|
|
6217
|
+
command,
|
|
6218
|
+
excludeFromContext,
|
|
6219
|
+
cwd: this.sessionManager.getCwd(),
|
|
6220
|
+
});
|
|
6221
|
+
}
|
|
6222
|
+
catch {
|
|
6223
|
+
// The extension runner already reported the error. Do not fall back to local execution.
|
|
6224
|
+
return;
|
|
6225
|
+
}
|
|
5961
6226
|
// If extension returned a full result, use it directly
|
|
5962
6227
|
if (eventResult?.result) {
|
|
5963
6228
|
const result = eventResult.result;
|