apex-code 0.5.0 → 0.5.2
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 +263 -5
- 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 +55 -7
- 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 +7 -8
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +20 -115
- package/dist/core/tools/bash.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 -125
- 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 -146
- 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/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 -17
- package/dist/modes/interactive/components/assistant-message.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/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 +6 -1
- 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 +6 -0
- 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 +4 -0
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +4 -1
- 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/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +3 -3
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +13 -15
- 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 +74 -48
- 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 +35 -10
- package/dist/modes/interactive/components/settings-selector.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 +6 -6
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +27 -5
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +64 -42
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +9 -0
- 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 +2 -2
- package/dist/modes/interactive/components/trust-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +43 -29
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +431 -166
- 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/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/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +31 -72
- package/dist/package-manager-cli.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 +7 -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 +94 -87
- 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/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,6 +47,8 @@ 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";
|
|
47
54
|
import { describeWithSource } from "./components/autocomplete-source.js";
|
|
@@ -56,6 +63,7 @@ import { CustomMessageComponent } from "./components/custom-message.js";
|
|
|
56
63
|
import { DaxnutsComponent } from "./components/daxnuts.js";
|
|
57
64
|
import { DynamicBorder } from "./components/dynamic-border.js";
|
|
58
65
|
import { EarendilAnnouncementComponent } from "./components/earendil-announcement.js";
|
|
66
|
+
import { collapsedErrorLine, summarizeError } from "./components/error-summary.js";
|
|
59
67
|
import { ExtensionEditorComponent } from "./components/extension-editor.js";
|
|
60
68
|
import { ExtensionInputComponent } from "./components/extension-input.js";
|
|
61
69
|
import { ExtensionSelectorComponent } from "./components/extension-selector.js";
|
|
@@ -84,19 +92,28 @@ import { refreshModelCatalogs } from "./model-catalog-refresh.js";
|
|
|
84
92
|
import { getModelSearchText } from "./model-search.js";
|
|
85
93
|
import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, onThemeChange, setRegisteredThemes, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
|
|
86
94
|
import { InteractiveThemeController } from "./theme/theme-controller.js";
|
|
87
|
-
|
|
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";
|
|
88
99
|
/** The next rung, wrapping from `all` back to `overview`. */
|
|
89
100
|
export function nextChatDetail(detail) {
|
|
90
|
-
return
|
|
101
|
+
return CHAT_DETAILS[(CHAT_DETAILS.indexOf(detail) + 1) % CHAT_DETAILS.length] ?? INITIAL_CHAT_DETAIL;
|
|
91
102
|
}
|
|
92
103
|
/** The single place the three rungs turn into rendering flags. */
|
|
93
104
|
export function chatDetailView(detail) {
|
|
94
105
|
return {
|
|
95
106
|
toolOutputExpanded: detail === "all",
|
|
96
107
|
editDiffsExpanded: detail !== "overview",
|
|
97
|
-
|
|
108
|
+
thinking: detail === "overview" ? "collapsed" : detail === "details" ? "preference" : "revealed",
|
|
98
109
|
};
|
|
99
110
|
}
|
|
111
|
+
function isWorkingStatusEditor(editor) {
|
|
112
|
+
return ("embedWorkingStatus" in editor &&
|
|
113
|
+
editor.embedWorkingStatus === true &&
|
|
114
|
+
"setWorkingStatusIndicator" in editor &&
|
|
115
|
+
typeof editor.setWorkingStatusIndicator === "function");
|
|
116
|
+
}
|
|
100
117
|
function isExpandable(obj) {
|
|
101
118
|
return typeof obj === "object" && obj !== null && "setExpanded" in obj && typeof obj.setExpanded === "function";
|
|
102
119
|
}
|
|
@@ -118,12 +135,27 @@ class ExpandableText extends Text {
|
|
|
118
135
|
this.setText(expanded ? this.getExpandedText() : this.getCollapsedText());
|
|
119
136
|
}
|
|
120
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
|
+
}
|
|
121
150
|
function isCustomSessionEntry(item) {
|
|
122
151
|
return "type" in item && item.type === "custom";
|
|
123
152
|
}
|
|
124
153
|
function isCompactionCostNotice(item) {
|
|
125
154
|
return "type" in item && item.type === "compaction_cost";
|
|
126
155
|
}
|
|
156
|
+
function isUsageSessionEntry(item) {
|
|
157
|
+
return "type" in item && item.type === "usage";
|
|
158
|
+
}
|
|
127
159
|
const DEAD_TERMINAL_ERROR_CODES = new Set(["EIO", "EPIPE", "ENOTCONN"]);
|
|
128
160
|
function isDeadTerminalError(error) {
|
|
129
161
|
if (!error || typeof error !== "object" || !("code" in error)) {
|
|
@@ -132,6 +164,22 @@ function isDeadTerminalError(error) {
|
|
|
132
164
|
const code = error.code;
|
|
133
165
|
return code !== undefined && DEAD_TERMINAL_ERROR_CODES.has(code);
|
|
134
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
|
+
}
|
|
135
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.";
|
|
136
184
|
function isAnthropicSubscriptionAuthKey(apiKey) {
|
|
137
185
|
return typeof apiKey === "string" && apiKey.startsWith("sk-ant-oat");
|
|
@@ -204,61 +252,6 @@ function formatLoginProviderCompletionDescription(provider) {
|
|
|
204
252
|
const authTypes = provider.authTypes.map(formatAuthSelectorProviderType).join("/");
|
|
205
253
|
return provider.name === provider.id ? authTypes : `${provider.name} · ${authTypes}`;
|
|
206
254
|
}
|
|
207
|
-
/** Composition root for selecting the interactive terminal renderer. */
|
|
208
|
-
export function createInteractiveTui(options) {
|
|
209
|
-
const terminal = options.terminal ?? new ProcessTerminal();
|
|
210
|
-
if (options.tuiMode === "fullscreen") {
|
|
211
|
-
const styleSearchMatch = (text) => theme.bg("searchMatchBg", theme.fg("searchMatchText", text));
|
|
212
|
-
return new TuiAltScreen(terminal, options.showHardwareCursor, options.logDirectory, {
|
|
213
|
-
searchMatchStyle: (text) => theme.underline(styleSearchMatch(text)),
|
|
214
|
-
searchCurrentMatchStyle: (text) => theme.bold(theme.inverse(styleSearchMatch(text))),
|
|
215
|
-
openUrl: openBrowser,
|
|
216
|
-
onRightClickPaste: options.onRightClickPaste,
|
|
217
|
-
copyOnSelect: options.fullscreenCopyOnSelect,
|
|
218
|
-
copySelection: async (text) => {
|
|
219
|
-
try {
|
|
220
|
-
await copyToClipboard(text);
|
|
221
|
-
return true;
|
|
222
|
-
}
|
|
223
|
-
catch {
|
|
224
|
-
return false;
|
|
225
|
-
}
|
|
226
|
-
},
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
return new TuiMainScreen(terminal, options.showHardwareCursor, options.logDirectory);
|
|
230
|
-
}
|
|
231
|
-
/** Stable reference for components while InteractiveMode replaces the active renderer. */
|
|
232
|
-
export function createInteractiveTuiReference(getTui) {
|
|
233
|
-
return new Proxy({}, {
|
|
234
|
-
get: (_target, property) => {
|
|
235
|
-
const tui = getTui();
|
|
236
|
-
const value = Reflect.get(tui, property, tui);
|
|
237
|
-
if (typeof value !== "function")
|
|
238
|
-
return value;
|
|
239
|
-
let methodTui = tui;
|
|
240
|
-
let method = value;
|
|
241
|
-
return (...args) => {
|
|
242
|
-
const currentTui = getTui();
|
|
243
|
-
if (currentTui !== methodTui) {
|
|
244
|
-
const currentMethod = Reflect.get(currentTui, property, currentTui);
|
|
245
|
-
if (typeof currentMethod !== "function") {
|
|
246
|
-
throw new TypeError(`TUI property ${String(property)} is not callable`);
|
|
247
|
-
}
|
|
248
|
-
methodTui = currentTui;
|
|
249
|
-
method = currentMethod;
|
|
250
|
-
}
|
|
251
|
-
return Reflect.apply(method, methodTui, args);
|
|
252
|
-
};
|
|
253
|
-
},
|
|
254
|
-
set: (_target, property, value) => {
|
|
255
|
-
const tui = getTui();
|
|
256
|
-
return Reflect.set(tui, property, value, tui);
|
|
257
|
-
},
|
|
258
|
-
has: (_target, property) => Reflect.has(getTui(), property),
|
|
259
|
-
getPrototypeOf: () => Reflect.getPrototypeOf(getTui()),
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
255
|
/**
|
|
263
256
|
* Working status rides in the footer tray; everything else keeps its own rows.
|
|
264
257
|
*
|
|
@@ -317,11 +310,12 @@ export class InteractiveMode {
|
|
|
317
310
|
onInputCallback;
|
|
318
311
|
pendingUserInputs = [];
|
|
319
312
|
activeStatusIndicator = undefined;
|
|
313
|
+
activeWorkingIndicatorEmbedded = false;
|
|
320
314
|
idleStatus = new IdleStatus();
|
|
321
315
|
workingMessage = undefined;
|
|
322
316
|
workingVisible = true;
|
|
323
317
|
workingIndicatorOptions = undefined;
|
|
324
|
-
defaultWorkingMessage = "Working
|
|
318
|
+
defaultWorkingMessage = "Working";
|
|
325
319
|
defaultHiddenThinkingLabel = "Thinking...";
|
|
326
320
|
hiddenThinkingLabel = this.defaultHiddenThinkingLabel;
|
|
327
321
|
lastSigintTime = 0;
|
|
@@ -335,13 +329,14 @@ export class InteractiveMode {
|
|
|
335
329
|
managedToolStatusStarted = false;
|
|
336
330
|
// Streaming message tracking
|
|
337
331
|
streamingComponent = undefined;
|
|
332
|
+
entriesRenderedByBoundaryCompaction = new Set();
|
|
338
333
|
streamingMessage = undefined;
|
|
339
334
|
// Tool execution tracking: toolCallId -> component
|
|
340
335
|
pendingTools = new Map();
|
|
341
336
|
// Tool output expansion state
|
|
342
|
-
chatDetail =
|
|
343
|
-
toolOutputExpanded =
|
|
344
|
-
editDiffsExpanded =
|
|
337
|
+
chatDetail = INITIAL_CHAT_DETAIL;
|
|
338
|
+
toolOutputExpanded = chatDetailView(INITIAL_CHAT_DETAIL).toolOutputExpanded;
|
|
339
|
+
editDiffsExpanded = chatDetailView(INITIAL_CHAT_DETAIL).editDiffsExpanded;
|
|
345
340
|
// Thinking block visibility state
|
|
346
341
|
hideThinkingBlock = false;
|
|
347
342
|
outputPad = 1;
|
|
@@ -381,6 +376,8 @@ export class InteractiveMode {
|
|
|
381
376
|
compactionQueuedMessages = [];
|
|
382
377
|
// Shutdown state
|
|
383
378
|
shutdownRequested = false;
|
|
379
|
+
/** The `/bug` hint is shown at most once per session so error output stays readable. */
|
|
380
|
+
bugReportHintShown = false;
|
|
384
381
|
// Extension UI state
|
|
385
382
|
extensionSelector = undefined;
|
|
386
383
|
extensionInput = undefined;
|
|
@@ -444,6 +441,7 @@ export class InteractiveMode {
|
|
|
444
441
|
tuiMode,
|
|
445
442
|
showHardwareCursor: this.settingsManager.getShowHardwareCursor(),
|
|
446
443
|
logDirectory: getAgentDir(),
|
|
444
|
+
terminal: options.terminal,
|
|
447
445
|
onRightClickPaste: this.onRightClickPaste,
|
|
448
446
|
fullscreenCopyOnSelect: this.settingsManager.getFullscreenCopyOnSelect(),
|
|
449
447
|
});
|
|
@@ -479,6 +477,9 @@ export class InteractiveMode {
|
|
|
479
477
|
surfaceColor: (text) => theme.bg("userMessageBg", text),
|
|
480
478
|
autocompleteRule: (width) => theme.fg("borderMuted", (this.settingsManager.getSymbolPreset() === "ascii" ? "-" : "┄").repeat(Math.max(1, width))),
|
|
481
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.
|
|
482
483
|
});
|
|
483
484
|
this.editor = this.defaultEditor;
|
|
484
485
|
this.editorContainer = new ComposerDock(() => this.editor);
|
|
@@ -488,8 +489,8 @@ export class InteractiveMode {
|
|
|
488
489
|
this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
|
|
489
490
|
this.footerContainer = new Container();
|
|
490
491
|
this.footerContainer.addChild(this.footer);
|
|
491
|
-
// Load hide thinking block setting
|
|
492
492
|
this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
|
|
493
|
+
this.loadChatDetail(this.settingsManager.getChatDetail());
|
|
493
494
|
this.outputPad = this.settingsManager.getOutputPad();
|
|
494
495
|
// Register themes from resource loader and initialize
|
|
495
496
|
setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
|
|
@@ -743,25 +744,20 @@ export class InteractiveMode {
|
|
|
743
744
|
}
|
|
744
745
|
// Keep one component tree and remount it when changing renderers.
|
|
745
746
|
this.renderWidgets(); // Initialize with default spacer
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
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,
|
|
750
755
|
scrollbar: this.settingsManager.getFullscreenScrollbar(),
|
|
751
|
-
|
|
756
|
+
scrollbarTrackStyle: (text) => theme.fg("scrollbarTrack", text),
|
|
757
|
+
scrollbarThumbStyle: (text) => theme.fg("scrollbarThumb", text),
|
|
752
758
|
});
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
{ component: this.statusContainer, shrink: 1, minSize: 0 },
|
|
756
|
-
{ component: this.widgetContainerAbove, shrink: 1, minSize: 0 },
|
|
757
|
-
{ component: this.editorContainer, shrink: 1, minSize: 3 },
|
|
758
|
-
{ component: this.widgetContainerBelow, shrink: 1, minSize: 0 },
|
|
759
|
-
{ component: this.footerContainer, shrink: 1, minSize: 1 },
|
|
760
|
-
]);
|
|
761
|
-
this.fullscreenLayoutRoot = new TuiLayouts.VStack([
|
|
762
|
-
{ component: this.transcriptScrollView, basis: 0, grow: 1, shrink: 1, minSize: 1 },
|
|
763
|
-
{ component: dock, basis: "auto", grow: 0, shrink: 1, minSize: 1 },
|
|
764
|
-
]);
|
|
759
|
+
this.transcriptScrollView = viewport.transcript;
|
|
760
|
+
this.fullscreenLayoutRoot = viewport.root;
|
|
765
761
|
this.mountInteractiveTui(this.renderer, [
|
|
766
762
|
this.documentContainer,
|
|
767
763
|
this.pendingMessagesContainer,
|
|
@@ -949,6 +945,11 @@ export class InteractiveMode {
|
|
|
949
945
|
if (modelFallbackMessage) {
|
|
950
946
|
this.showWarning(modelFallbackMessage);
|
|
951
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
|
+
}
|
|
952
953
|
void this.maybeWarnAboutAnthropicSubscriptionAuth();
|
|
953
954
|
// Process initial messages
|
|
954
955
|
if (initialMessage) {
|
|
@@ -1709,10 +1710,57 @@ export class InteractiveMode {
|
|
|
1709
1710
|
async handleFatalRuntimeError(prefix, error) {
|
|
1710
1711
|
const message = error instanceof Error ? error.message : String(error);
|
|
1711
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
|
+
}
|
|
1712
1720
|
stopThemeWatcher();
|
|
1713
1721
|
this.stop("transcript");
|
|
1714
1722
|
process.exit(1);
|
|
1715
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
|
+
}
|
|
1716
1764
|
renderCurrentSessionState() {
|
|
1717
1765
|
this.loadedResourcesContainer.clear();
|
|
1718
1766
|
this.chatContainer.clear();
|
|
@@ -1740,8 +1788,12 @@ export class InteractiveMode {
|
|
|
1740
1788
|
/**
|
|
1741
1789
|
* Get a registered tool definition by name (for custom rendering).
|
|
1742
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
|
+
*/
|
|
1743
1795
|
getRegisteredToolDefinition(toolName) {
|
|
1744
|
-
return this.session.getToolDefinition(toolName);
|
|
1796
|
+
return withBuiltInRenderers(toolName, this.session.getToolDefinition(toolName));
|
|
1745
1797
|
}
|
|
1746
1798
|
getMarkdownTransformers() {
|
|
1747
1799
|
return [this.mermaidMarkdownTransformer, ...this.session.extensionRunner.getMarkdownTransformers()];
|
|
@@ -1811,38 +1863,58 @@ export class InteractiveMode {
|
|
|
1811
1863
|
this.footerDataProvider.setExtensionStatus(key, text);
|
|
1812
1864
|
this.ui.requestRender();
|
|
1813
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
|
+
}
|
|
1814
1873
|
showStatusIndicator(indicator) {
|
|
1815
1874
|
this.activeStatusIndicator?.dispose();
|
|
1816
1875
|
this.activeStatusIndicator = indicator;
|
|
1876
|
+
this.activeWorkingIndicatorEmbedded = false;
|
|
1817
1877
|
this.statusContainer.clear();
|
|
1878
|
+
this.setEditorWorkingStatusIndicator(undefined);
|
|
1818
1879
|
if (trayOwnsActivity(indicator.kind, this.customFooter !== undefined)) {
|
|
1819
1880
|
this.footer.setActivity(indicator);
|
|
1820
1881
|
return;
|
|
1821
1882
|
}
|
|
1822
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
|
+
}
|
|
1823
1890
|
this.statusContainer.addChild(indicator);
|
|
1824
1891
|
}
|
|
1825
1892
|
clearStatusIndicator(kind) {
|
|
1826
1893
|
if (kind && this.activeStatusIndicator?.kind !== kind) {
|
|
1827
1894
|
return;
|
|
1828
1895
|
}
|
|
1829
|
-
const
|
|
1830
|
-
const wasInTray = this.
|
|
1831
|
-
|
|
1832
|
-
|
|
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();
|
|
1833
1900
|
this.activeStatusIndicator = undefined;
|
|
1901
|
+
this.activeWorkingIndicatorEmbedded = false;
|
|
1834
1902
|
this.footer?.setActivity(undefined);
|
|
1835
1903
|
this.statusContainer.clear();
|
|
1836
|
-
|
|
1837
|
-
//
|
|
1838
|
-
|
|
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)
|
|
1839
1908
|
return;
|
|
1840
|
-
if (
|
|
1909
|
+
if (clearedIndicator && this.options.tuiMode === "regular" && this.ui.getClearOnShrink()) {
|
|
1841
1910
|
this.statusContainer.addChild(this.idleStatus);
|
|
1842
1911
|
}
|
|
1843
1912
|
}
|
|
1844
1913
|
showWorkingStatusIndicator() {
|
|
1845
|
-
|
|
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));
|
|
1846
1918
|
}
|
|
1847
1919
|
setWorkingVisible(visible) {
|
|
1848
1920
|
this.workingVisible = visible;
|
|
@@ -2368,6 +2440,15 @@ export class InteractiveMode {
|
|
|
2368
2440
|
this.editor = this.defaultEditor;
|
|
2369
2441
|
}
|
|
2370
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
|
+
}
|
|
2371
2452
|
this.ui.setFocus(this.editor);
|
|
2372
2453
|
this.ui.requestRender();
|
|
2373
2454
|
}
|
|
@@ -2646,6 +2727,12 @@ export class InteractiveMode {
|
|
|
2646
2727
|
this.editor.setText("");
|
|
2647
2728
|
return;
|
|
2648
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
|
+
}
|
|
2649
2736
|
if (text === "/copy") {
|
|
2650
2737
|
await this.handleCopyCommand();
|
|
2651
2738
|
this.editor.setText("");
|
|
@@ -2852,10 +2939,44 @@ export class InteractiveMode {
|
|
|
2852
2939
|
this.ui.requestRender();
|
|
2853
2940
|
break;
|
|
2854
2941
|
case "entry_appended":
|
|
2942
|
+
if (this.entriesRenderedByBoundaryCompaction.delete(event.entry.id))
|
|
2943
|
+
break;
|
|
2855
2944
|
if (event.entry.type === "custom") {
|
|
2856
2945
|
this.addCustomEntryToChat(event.entry);
|
|
2857
2946
|
this.ui.requestRender();
|
|
2858
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
|
+
}
|
|
2859
2980
|
break;
|
|
2860
2981
|
case "session_info_changed":
|
|
2861
2982
|
this.updateTerminalTitle();
|
|
@@ -2879,6 +3000,7 @@ export class InteractiveMode {
|
|
|
2879
3000
|
else if (event.message.role === "assistant") {
|
|
2880
3001
|
this.retriedAttempt = undefined;
|
|
2881
3002
|
this.streamingComponent = new AssistantMessageComponent(undefined, this.isThinkingHidden(), this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad, this.getMarkdownTransformers());
|
|
3003
|
+
this.adoptChatDetail(this.streamingComponent);
|
|
2882
3004
|
this.streamingMessage = event.message;
|
|
2883
3005
|
this.chatContainer.addChild(this.streamingComponent);
|
|
2884
3006
|
this.streamingComponent.updateContent(this.streamingMessage, true);
|
|
@@ -2942,12 +3064,14 @@ export class InteractiveMode {
|
|
|
2942
3064
|
});
|
|
2943
3065
|
}
|
|
2944
3066
|
this.clearPendingTools();
|
|
3067
|
+
this.maybeSuggestBugReport(this.streamingMessage);
|
|
2945
3068
|
}
|
|
2946
3069
|
else {
|
|
2947
3070
|
// Args are now complete - trigger diff computation for edit tools
|
|
2948
3071
|
for (const [, component] of this.pendingTools.entries()) {
|
|
2949
3072
|
component.setArgsComplete();
|
|
2950
3073
|
}
|
|
3074
|
+
this.maybeShowThinkingDropNotice(this.streamingMessage);
|
|
2951
3075
|
this.maybeShowCacheMissNotice(this.streamingMessage);
|
|
2952
3076
|
}
|
|
2953
3077
|
this.streamingComponent = undefined;
|
|
@@ -3062,7 +3186,7 @@ export class InteractiveMode {
|
|
|
3062
3186
|
}
|
|
3063
3187
|
else {
|
|
3064
3188
|
this.chatContainer.addChild(new Spacer(1));
|
|
3065
|
-
this.chatContainer.addChild(
|
|
3189
|
+
this.chatContainer.addChild(errorText(event.errorMessage, this.toolOutputExpanded, 1));
|
|
3066
3190
|
}
|
|
3067
3191
|
}
|
|
3068
3192
|
void this.flushCompactionQueue({ willRetry: event.willRetry });
|
|
@@ -3101,7 +3225,7 @@ export class InteractiveMode {
|
|
|
3101
3225
|
const outcome = this.retryCancelled
|
|
3102
3226
|
? "retry cancelled"
|
|
3103
3227
|
: `gave up after ${event.attempt} ${event.attempt === 1 ? "retry" : "retries"}`;
|
|
3104
|
-
this.showError(
|
|
3228
|
+
this.showError(lastAttempt?.error ?? event.finalError ?? "Unknown error", ` (${outcome})`);
|
|
3105
3229
|
}
|
|
3106
3230
|
this.retriedAttempt = undefined;
|
|
3107
3231
|
this.retryCancelled = false;
|
|
@@ -3230,6 +3354,8 @@ export class InteractiveMode {
|
|
|
3230
3354
|
this.chatContainer.addChild(component);
|
|
3231
3355
|
break;
|
|
3232
3356
|
}
|
|
3357
|
+
case "system":
|
|
3358
|
+
break;
|
|
3233
3359
|
case "user": {
|
|
3234
3360
|
const textContent = this.getUserMessageText(message);
|
|
3235
3361
|
if (textContent) {
|
|
@@ -3261,6 +3387,7 @@ export class InteractiveMode {
|
|
|
3261
3387
|
}
|
|
3262
3388
|
case "assistant": {
|
|
3263
3389
|
const assistantComponent = new AssistantMessageComponent(message, this.isThinkingHidden(), this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad, this.getMarkdownTransformers());
|
|
3390
|
+
this.adoptChatDetail(assistantComponent);
|
|
3264
3391
|
this.chatContainer.addChild(assistantComponent);
|
|
3265
3392
|
break;
|
|
3266
3393
|
}
|
|
@@ -3276,8 +3403,8 @@ export class InteractiveMode {
|
|
|
3276
3403
|
renderSessionItems(items, options = {}) {
|
|
3277
3404
|
this.clearPendingTools();
|
|
3278
3405
|
const renderedPendingTools = new Map();
|
|
3279
|
-
// Cache
|
|
3280
|
-
//
|
|
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.
|
|
3281
3408
|
const cacheMisses = this.settingsManager.getShowCacheMissNotices()
|
|
3282
3409
|
? collectCacheMisses(this.sessionManager.getEntries(), this.session.modelRuntime)
|
|
3283
3410
|
: new Map();
|
|
@@ -3290,6 +3417,10 @@ export class InteractiveMode {
|
|
|
3290
3417
|
this.addCustomEntryToChat(item);
|
|
3291
3418
|
continue;
|
|
3292
3419
|
}
|
|
3420
|
+
if (isUsageSessionEntry(item)) {
|
|
3421
|
+
this.addCacheWarmingUsage(item);
|
|
3422
|
+
continue;
|
|
3423
|
+
}
|
|
3293
3424
|
if (isCompactionCostNotice(item)) {
|
|
3294
3425
|
this.addCompactionCostNotice(item);
|
|
3295
3426
|
continue;
|
|
@@ -3358,7 +3489,7 @@ export class InteractiveMode {
|
|
|
3358
3489
|
*/
|
|
3359
3490
|
renderSessionEntries(entries, options = {}) {
|
|
3360
3491
|
const items = entries.flatMap((entry) => {
|
|
3361
|
-
if (entry.type === "custom") {
|
|
3492
|
+
if (entry.type === "custom" || (entry.type === "usage" && entry.kind === "cache_warm")) {
|
|
3362
3493
|
return [entry];
|
|
3363
3494
|
}
|
|
3364
3495
|
const messages = sessionEntryToContextMessages(entry);
|
|
@@ -3369,6 +3500,12 @@ export class InteractiveMode {
|
|
|
3369
3500
|
});
|
|
3370
3501
|
this.renderSessionItems(items, options);
|
|
3371
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
|
+
}
|
|
3372
3509
|
/**
|
|
3373
3510
|
* Render billing usage for a compaction or branch summary. The notice is derived
|
|
3374
3511
|
* from persisted summary usage and is not stored as a separate session entry.
|
|
@@ -3383,6 +3520,43 @@ export class InteractiveMode {
|
|
|
3383
3520
|
this.chatContainer.addChild(new Spacer(1));
|
|
3384
3521
|
this.chatContainer.addChild(new Text(theme.fg("warning", `${label}: ${formatTokens(tokens)} tokens billed${cost}`), 1, 0));
|
|
3385
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
|
+
}
|
|
3386
3560
|
/**
|
|
3387
3561
|
* Show a transcript notice when a completed assistant message paid for a
|
|
3388
3562
|
* significant cache miss. Only states observable facts: the miss itself,
|
|
@@ -3549,6 +3723,12 @@ export class InteractiveMode {
|
|
|
3549
3723
|
catch { }
|
|
3550
3724
|
console.error("apex-code exiting due to uncaughtException:");
|
|
3551
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
|
+
}
|
|
3552
3732
|
process.exit(1);
|
|
3553
3733
|
}
|
|
3554
3734
|
/**
|
|
@@ -3684,6 +3864,7 @@ export class InteractiveMode {
|
|
|
3684
3864
|
const level = this.session.thinkingLevel || "off";
|
|
3685
3865
|
this.editor.borderColor = theme.getThinkingBorderColor(level);
|
|
3686
3866
|
}
|
|
3867
|
+
this.activeStatusIndicator?.invalidate();
|
|
3687
3868
|
this.ui.requestRender();
|
|
3688
3869
|
}
|
|
3689
3870
|
cycleThinkingLevel() {
|
|
@@ -3729,27 +3910,40 @@ export class InteractiveMode {
|
|
|
3729
3910
|
if (hasEditDiffsExpansion(component))
|
|
3730
3911
|
component.setEditDiffsExpanded(this.editDiffsExpanded);
|
|
3731
3912
|
}
|
|
3913
|
+
/** The expand key. A level the user picks is saved, so the next session opens at it. */
|
|
3732
3914
|
cycleChatDetail() {
|
|
3733
|
-
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;
|
|
3734
3930
|
}
|
|
3735
3931
|
/**
|
|
3736
|
-
* Presentation only. This never rewrites messages
|
|
3737
|
-
*
|
|
3738
|
-
*
|
|
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.
|
|
3739
3935
|
*/
|
|
3740
3936
|
setChatDetail(detail) {
|
|
3741
3937
|
if (detail === this.chatDetail)
|
|
3742
3938
|
return;
|
|
3743
|
-
this.
|
|
3744
|
-
const view = chatDetailView(detail);
|
|
3745
|
-
this.toolOutputExpanded = view.toolOutputExpanded;
|
|
3746
|
-
this.editDiffsExpanded = view.editDiffsExpanded;
|
|
3939
|
+
this.assignChatDetail(detail);
|
|
3747
3940
|
this.applyChatDetail();
|
|
3748
3941
|
this.showStatus(`Conversation detail: ${detail}`);
|
|
3749
3942
|
}
|
|
3750
3943
|
/** True when thinking blocks are hidden right now, setting and detail level combined. */
|
|
3751
3944
|
isThinkingHidden() {
|
|
3752
|
-
|
|
3945
|
+
const { thinking } = chatDetailView(this.chatDetail);
|
|
3946
|
+
return thinking === "preference" ? this.hideThinkingBlock : thinking === "collapsed";
|
|
3753
3947
|
}
|
|
3754
3948
|
applyChatDetail() {
|
|
3755
3949
|
const activeHeader = this.customHeader ?? this.builtInHeader;
|
|
@@ -3776,7 +3970,8 @@ export class InteractiveMode {
|
|
|
3776
3970
|
*
|
|
3777
3971
|
* Collapsing maps to `details` rather than `overview`, because an extension
|
|
3778
3972
|
* asking to collapse tool output has not asked to hide edit diffs, and
|
|
3779
|
-
* `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.
|
|
3780
3975
|
*/
|
|
3781
3976
|
setToolsExpanded(expanded) {
|
|
3782
3977
|
this.setChatDetail(expanded ? "all" : "details");
|
|
@@ -3792,8 +3987,16 @@ export class InteractiveMode {
|
|
|
3792
3987
|
}
|
|
3793
3988
|
toggleThinkingBlockVisibility() {
|
|
3794
3989
|
this.offerFirstUseHint("thinking");
|
|
3795
|
-
this.
|
|
3796
|
-
|
|
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
|
+
}
|
|
3797
4000
|
this.updateThinkingBlockVisibility();
|
|
3798
4001
|
this.showStatus(`Thinking blocks: ${this.hideThinkingBlock ? "hidden" : "visible"}`);
|
|
3799
4002
|
}
|
|
@@ -3822,9 +4025,9 @@ export class InteractiveMode {
|
|
|
3822
4025
|
this.editor.setText("");
|
|
3823
4026
|
this.ui.requestRender();
|
|
3824
4027
|
}
|
|
3825
|
-
showError(errorMessage) {
|
|
4028
|
+
showError(errorMessage, outcome) {
|
|
3826
4029
|
this.chatContainer.addChild(new Spacer(1));
|
|
3827
|
-
this.chatContainer.addChild(
|
|
4030
|
+
this.chatContainer.addChild(errorText(`Error: ${errorMessage}`, this.toolOutputExpanded, this.outputPad, outcome));
|
|
3828
4031
|
this.ui.requestRender();
|
|
3829
4032
|
}
|
|
3830
4033
|
showWarning(warningMessage) {
|
|
@@ -3923,7 +4126,7 @@ export class InteractiveMode {
|
|
|
3923
4126
|
if (allQueued.length === 0) {
|
|
3924
4127
|
this.updatePendingMessagesDisplay();
|
|
3925
4128
|
if (options?.abort) {
|
|
3926
|
-
this.
|
|
4129
|
+
void this.session.abort();
|
|
3927
4130
|
}
|
|
3928
4131
|
return 0;
|
|
3929
4132
|
}
|
|
@@ -3933,7 +4136,7 @@ export class InteractiveMode {
|
|
|
3933
4136
|
this.editor.setText(combinedText);
|
|
3934
4137
|
this.updatePendingMessagesDisplay();
|
|
3935
4138
|
if (options?.abort) {
|
|
3936
|
-
this.
|
|
4139
|
+
void this.session.abort();
|
|
3937
4140
|
}
|
|
3938
4141
|
return allQueued.length;
|
|
3939
4142
|
}
|
|
@@ -4133,6 +4336,7 @@ export class InteractiveMode {
|
|
|
4133
4336
|
followUpMode: this.session.followUpMode,
|
|
4134
4337
|
transport: this.settingsManager.getTransport(),
|
|
4135
4338
|
httpIdleTimeoutMs: this.settingsManager.getHttpIdleTimeoutMs(),
|
|
4339
|
+
cacheWarmingMode: this.settingsManager.getCacheWarmingMode(),
|
|
4136
4340
|
thinkingLevel: this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL,
|
|
4137
4341
|
availableThinkingLevels: [...THINKING_LEVEL_OPTIONS],
|
|
4138
4342
|
modelThinkingLevels: this.settingsManager.getAllModelThinkingLevels(),
|
|
@@ -4151,6 +4355,7 @@ export class InteractiveMode {
|
|
|
4151
4355
|
permissionMode,
|
|
4152
4356
|
editorPaddingX: this.settingsManager.getEditorPaddingX(),
|
|
4153
4357
|
outputPad: this.settingsManager.getOutputPad(),
|
|
4358
|
+
chatDetail: this.chatDetail,
|
|
4154
4359
|
autocompleteMaxVisible: this.settingsManager.getAutocompleteMaxVisible(),
|
|
4155
4360
|
quietStartup: this.settingsManager.getQuietStartup(),
|
|
4156
4361
|
clearOnShrink: this.settingsManager.getClearOnShrink(),
|
|
@@ -4206,6 +4411,10 @@ export class InteractiveMode {
|
|
|
4206
4411
|
configureHttpDispatcher(timeoutMs);
|
|
4207
4412
|
this.showStatus(`HTTP idle timeout: ${formatHttpIdleTimeoutMs(timeoutMs)}`);
|
|
4208
4413
|
},
|
|
4414
|
+
onCacheWarmingModeChange: (mode) => {
|
|
4415
|
+
this.session.setCacheWarmingMode(mode);
|
|
4416
|
+
this.showStatus(`Cache warming: ${mode}`);
|
|
4417
|
+
},
|
|
4209
4418
|
onModelThinkingLevelChange: (provider, modelId, level) => {
|
|
4210
4419
|
this.settingsManager.setModelThinkingLevel(provider, modelId, level);
|
|
4211
4420
|
// If the override is for the current model, apply it to the session too
|
|
@@ -4232,6 +4441,7 @@ export class InteractiveMode {
|
|
|
4232
4441
|
void this.themeController.setThemeSetting(themeSetting);
|
|
4233
4442
|
},
|
|
4234
4443
|
onThemePreview: (themeName) => this.themeController.preview(themeName),
|
|
4444
|
+
onChatDetailChange: (detail) => this.chooseChatDetail(detail),
|
|
4235
4445
|
onHideThinkingBlockChange: (hidden) => {
|
|
4236
4446
|
this.hideThinkingBlock = hidden;
|
|
4237
4447
|
this.settingsManager.setHideThinkingBlock(hidden);
|
|
@@ -4767,6 +4977,11 @@ export class InteractiveMode {
|
|
|
4767
4977
|
this.restoreQueuedMessagesToEditor();
|
|
4768
4978
|
await this.session.abort();
|
|
4769
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
|
+
}
|
|
4770
4985
|
// Set up escape handler and status indicator if summarizing
|
|
4771
4986
|
let showingSummaryIndicator = false;
|
|
4772
4987
|
const originalOnEscape = this.defaultEditor.onEscape;
|
|
@@ -4837,9 +5052,9 @@ export class InteractiveMode {
|
|
|
4837
5052
|
}
|
|
4838
5053
|
showSessionSelector() {
|
|
4839
5054
|
this.showSelector((done) => {
|
|
4840
|
-
const selector = new SessionSelectorComponent((onProgress) => SessionManager.list(this.sessionManager.getCwd(), this.sessionManager.getSessionDir(), onProgress), (onProgress) => this.sessionManager.usesDefaultSessionDir()
|
|
4841
|
-
? SessionManager.listAll(onProgress)
|
|
4842
|
-
: 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) => {
|
|
4843
5058
|
done();
|
|
4844
5059
|
await this.handleResumeSession(sessionPath);
|
|
4845
5060
|
}, () => {
|
|
@@ -5123,67 +5338,89 @@ export class InteractiveMode {
|
|
|
5123
5338
|
}
|
|
5124
5339
|
async completeProviderAuthentication(providerId, providerName, authType, previousModel) {
|
|
5125
5340
|
const actionLabel = authType === "oauth" ? `Logged in to ${providerName}` : `Saved API key for ${providerName}`;
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
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.`;
|
|
5146
5363
|
}
|
|
5147
5364
|
else {
|
|
5148
|
-
|
|
5149
|
-
|
|
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.`;
|
|
5150
5372
|
}
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
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
|
+
}
|
|
5155
5382
|
}
|
|
5156
5383
|
}
|
|
5157
5384
|
}
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
this.checkDaxnutsEasterEgg(selectedModel);
|
|
5166
|
-
}
|
|
5167
|
-
else {
|
|
5168
|
-
this.showStatus(`${actionLabel}. Credentials saved to ${getAuthPath()}`);
|
|
5169
|
-
if (selectionError) {
|
|
5170
|
-
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);
|
|
5171
5392
|
}
|
|
5172
5393
|
else {
|
|
5173
|
-
|
|
5394
|
+
this.showStatus(`${actionLabel}. Credentials saved to ${getAuthPath()}`);
|
|
5395
|
+
if (selectionError) {
|
|
5396
|
+
this.showError(selectionError);
|
|
5397
|
+
}
|
|
5398
|
+
else {
|
|
5399
|
+
void this.maybeWarnAboutAnthropicSubscriptionAuth();
|
|
5400
|
+
}
|
|
5174
5401
|
}
|
|
5402
|
+
};
|
|
5403
|
+
if (deferSelection) {
|
|
5404
|
+
this.showStatus(`${actionLabel}. Credentials saved to ${getAuthPath()}. Refreshing model catalog…`);
|
|
5405
|
+
}
|
|
5406
|
+
else {
|
|
5407
|
+
await finishAuthentication();
|
|
5175
5408
|
}
|
|
5176
5409
|
const controller = new AbortController();
|
|
5177
5410
|
const timeout = setTimeout(() => controller.abort(), 15_000);
|
|
5178
|
-
void
|
|
5411
|
+
void session.modelRuntime
|
|
5179
5412
|
.refresh({ providers: [providerId], signal: controller.signal })
|
|
5180
|
-
.then((result) => {
|
|
5413
|
+
.then(async (result) => {
|
|
5181
5414
|
if (result.aborted) {
|
|
5182
5415
|
this.showWarning(`${actionLabel}, but its model catalog refresh timed out; using cached models.`);
|
|
5183
5416
|
}
|
|
5184
5417
|
else if (result.errors.size > 0) {
|
|
5185
5418
|
this.showWarning(`${actionLabel}, but its model catalog could not be refreshed; using cached models.`);
|
|
5186
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
|
+
}
|
|
5187
5424
|
this.updateAvailableProviderCount();
|
|
5188
5425
|
this.footer.invalidate();
|
|
5189
5426
|
this.ui.requestRender();
|
|
@@ -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)}`;
|
|
@@ -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;
|