@earendil-works/pi-coding-agent 0.84.1 → 0.84.3
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 +139 -0
- package/README.md +9 -4
- package/dist/bundle/chunks/anthropic-messages-JWX2WP65.js +45 -0
- package/dist/bundle/chunks/anthropic.js +75 -0
- package/dist/bundle/chunks/azure-openai-responses-5ENVCQ7U.js +2 -0
- package/dist/bundle/chunks/bedrock-converse-stream.js +74 -0
- package/dist/bundle/chunks/chunk-4L3WN2XY.js +2 -0
- package/dist/bundle/chunks/chunk-6CYCI3EJ.js +12 -0
- package/dist/bundle/chunks/chunk-6R7UI4IY.js +2 -0
- package/dist/bundle/chunks/chunk-AXIIZGTV.js +2 -0
- package/dist/bundle/chunks/chunk-DRQDKNXA.js +2 -0
- package/dist/bundle/chunks/chunk-E5KXRMZK.js +1547 -0
- package/dist/bundle/chunks/chunk-GMWCTUPB.js +2 -0
- package/dist/bundle/chunks/chunk-GPPBJGBU.js +50 -0
- package/dist/bundle/chunks/chunk-JUUC5AZY.js +2 -0
- package/dist/bundle/chunks/chunk-L2ZPNLYD.js +2 -0
- package/dist/bundle/chunks/chunk-MNAIPA3J.js +2 -0
- package/dist/bundle/chunks/chunk-NBBFIJUL.js +11 -0
- package/dist/bundle/chunks/chunk-NUHFSC37.js +25 -0
- package/dist/bundle/chunks/chunk-PGXDEOVV.js +2 -0
- package/dist/bundle/chunks/chunk-PJACZCIR.js +2 -0
- package/dist/bundle/chunks/chunk-PRRNXB7S.js +2 -0
- package/dist/bundle/chunks/chunk-TEPMHNKQ.js +2 -0
- package/dist/bundle/chunks/chunk-TRDNDS6A.js +4 -0
- package/dist/bundle/chunks/chunk-UAQELI3K.js +2 -0
- package/dist/bundle/chunks/chunk-XDUWOHPL.js +6 -0
- package/dist/bundle/chunks/chunk-XNGRGP62.js +2 -0
- package/dist/bundle/chunks/dist-RDWOYWHR.js +13 -0
- package/dist/bundle/chunks/github-copilot.js +2 -0
- package/dist/bundle/chunks/google-generative-ai-XDKMGBCJ.js +2 -0
- package/dist/bundle/chunks/google-vertex-D5FGEO3Y.js +2 -0
- package/dist/bundle/chunks/image-resize-worker.js +2 -0
- package/dist/bundle/chunks/kimi-coding.js +2 -0
- package/dist/bundle/chunks/lib-HGQMNYVT.js +9 -0
- package/dist/bundle/chunks/mistral-conversations-YK73UAOZ.js +5 -0
- package/dist/bundle/chunks/multipart-parser-SQ6GDJL7.js +3 -0
- package/dist/bundle/chunks/openai-codex-responses-GJVBJXLB.js +8 -0
- package/dist/bundle/chunks/openai-codex.js +75 -0
- package/dist/bundle/chunks/openai-completions-JD4WAC3R.js +7 -0
- package/dist/bundle/chunks/openai-responses-BSOJMSEV.js +2 -0
- package/dist/bundle/chunks/openrouter-images-N4XVVFSD.js +2 -0
- package/dist/bundle/chunks/openrouter.js +75 -0
- package/dist/bundle/chunks/pi-messages-TPFH44NP.js +8 -0
- package/dist/bundle/chunks/radius.js +75 -0
- package/dist/bundle/chunks/src-UK77SYZE.js +4 -0
- package/dist/bundle/chunks/xai.js +2 -0
- package/dist/bundle/cli.js +3 -0
- package/dist/bundle/client.js +2 -0
- package/dist/bundle/index.js +2 -0
- package/dist/bundle/rpc-entry.js +3 -0
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +40 -9
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/auth-command.d.ts.map +1 -1
- package/dist/cli/auth-command.js +5 -4
- package/dist/cli/auth-command.js.map +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +2 -1
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/config.d.ts +4 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +22 -14
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +33 -9
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +169 -101
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +6 -7
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +3 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +4 -3
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +41 -32
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/defaults.d.ts +1 -0
- package/dist/core/defaults.d.ts.map +1 -1
- package/dist/core/defaults.js +9 -0
- package/dist/core/defaults.js.map +1 -1
- package/dist/core/experimental.d.ts +4 -0
- package/dist/core/experimental.d.ts.map +1 -1
- package/dist/core/experimental.js +4 -0
- package/dist/core/experimental.js.map +1 -1
- package/dist/core/extensions/index.d.ts +2 -2
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +115 -48
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/types.d.ts +42 -9
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +3 -0
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +1 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +1 -1
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/keybindings.d.ts +38 -13
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +26 -5
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-config.d.ts +10 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +4 -1
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-resolver.d.ts +1 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +14 -8
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/models-store.d.ts.map +1 -1
- package/dist/core/models-store.js +2 -1
- package/dist/core/models-store.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +22 -14
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/pi-manifest.d.ts.map +1 -1
- package/dist/core/pi-manifest.js +2 -1
- package/dist/core/pi-manifest.js.map +1 -1
- package/dist/core/project-trust.d.ts.map +1 -1
- package/dist/core/project-trust.js +2 -2
- package/dist/core/project-trust.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +2 -1
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +3 -2
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +7 -5
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +12 -4
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-export.d.ts +4 -0
- package/dist/core/session-export.d.ts.map +1 -0
- package/dist/core/session-export.js +32 -0
- package/dist/core/session-export.js.map +1 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +4 -3
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-diagnostics.d.ts +9 -0
- package/dist/core/settings-diagnostics.d.ts.map +1 -0
- package/dist/core/settings-diagnostics.js +21 -0
- package/dist/core/settings-diagnostics.js.map +1 -0
- package/dist/core/settings-manager.d.ts +15 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +64 -10
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +51 -33
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +2 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +12 -3
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +14 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +38 -20
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts +0 -5
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js +2 -5
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +22 -4
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +1 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +1 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +4 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +18 -1
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/powershell.d.ts +16 -0
- package/dist/core/tools/powershell.d.ts.map +1 -0
- package/dist/core/tools/powershell.js +39 -0
- package/dist/core/tools/powershell.js.map +1 -0
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +2 -0
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +2 -0
- package/dist/core/tools/write.js.map +1 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +2 -1
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/extensions/llama/index.d.ts.map +1 -1
- package/dist/extensions/llama/index.js +2 -0
- package/dist/extensions/llama/index.js.map +1 -1
- package/dist/extensions/llama/provider.d.ts.map +1 -1
- package/dist/extensions/llama/provider.js +6 -2
- package/dist/extensions/llama/provider.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +4 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +23 -16
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +4 -3
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +9 -3
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +58 -13
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +10 -3
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +128 -56
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
- package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
- package/dist/modes/interactive/components/settings-submenu.js +165 -0
- package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
- package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +74 -17
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +10 -1
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts.map +1 -1
- package/dist/modes/interactive/external-editor.js +2 -1
- package/dist/modes/interactive/external-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +19 -2
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +250 -129
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/model-catalog-refresh.d.ts +7 -0
- package/dist/modes/interactive/model-catalog-refresh.d.ts.map +1 -0
- package/dist/modes/interactive/model-catalog-refresh.js +34 -0
- package/dist/modes/interactive/model-catalog-refresh.js.map +1 -0
- package/dist/modes/interactive/session-share.d.ts +16 -0
- package/dist/modes/interactive/session-share.d.ts.map +1 -0
- package/dist/modes/interactive/session-share.js +188 -0
- package/dist/modes/interactive/session-share.js.map +1 -0
- package/dist/modes/interactive/theme/dark.json +2 -0
- package/dist/modes/interactive/theme/light.json +2 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts +10 -2
- package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-controller.js +25 -11
- package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
- package/dist/modes/interactive/theme/theme-schema.json +9 -1
- package/dist/modes/interactive/theme/theme.d.ts +5 -3
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +16 -5
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/json-event.d.ts +18 -12
- package/dist/modes/json-event.d.ts.map +1 -1
- package/dist/modes/json-event.js +22 -5
- package/dist/modes/json-event.js.map +1 -1
- package/dist/package-manager-cli.d.ts +1 -0
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +187 -3
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/server/create-harness.d.ts.map +1 -1
- package/dist/server/create-harness.js +2 -0
- package/dist/server/create-harness.js.map +1 -1
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js +2 -1
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/management-http.d.ts +5 -3
- package/dist/utils/management-http.d.ts.map +1 -1
- package/dist/utils/management-http.js +14 -10
- package/dist/utils/management-http.js.map +1 -1
- package/dist/utils/shell.d.ts +3 -0
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +17 -5
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts +1 -0
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js +60 -1
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/dist/utils/text.d.ts +8 -0
- package/dist/utils/text.d.ts.map +1 -0
- package/dist/utils/text.js +9 -0
- package/dist/utils/text.js.map +1 -0
- package/dist/utils/tools-manager.d.ts +10 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +14 -19
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/compaction.md +21 -6
- package/docs/custom-provider.md +6 -3
- package/docs/environment-variables.md +16 -10
- package/docs/extensions.md +21 -7
- package/docs/json.md +12 -5
- package/docs/keybindings.md +15 -9
- package/docs/llama-cpp.md +2 -0
- package/docs/models.md +8 -2
- package/docs/packages.md +2 -2
- package/docs/rpc.md +24 -7
- package/docs/sdk.md +16 -2
- package/docs/settings.md +41 -2
- package/docs/skills.md +4 -3
- package/docs/terminal-setup.md +34 -11
- package/docs/themes.md +24 -3
- package/docs/tui.md +2 -2
- package/docs/usage.md +9 -4
- package/docs/windows.md +24 -2
- package/examples/extensions/border-status-editor.ts +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/git-checkpoint.ts +2 -2
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/notify.ts +3 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/subagent/README.md +3 -1
- package/examples/extensions/subagent/agents.ts +40 -9
- package/examples/extensions/subagent/index.ts +27 -4
- package/examples/extensions/titlebar-spinner.ts +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/rpc-extension-ui.ts +1 -1
- package/examples/sdk/06-extensions.ts +1 -1
- package/examples/sdk/README.md +1 -1
- package/npm-shrinkwrap.json +28 -132
- package/package.json +12 -14
|
@@ -9,11 +9,12 @@ import * as path from "node:path";
|
|
|
9
9
|
import * as TuiLayouts from "@earendil-works/pi-tui";
|
|
10
10
|
import { CombinedAutocompleteProvider, Container, fuzzyFilter, getCapabilities, hyperlink, Markdown, matchesKey, ProcessTerminal, Spacer, setKeybindings, Text, TruncatedText, TuiAltScreen, TuiMainScreen, visibleWidth, } from "@earendil-works/pi-tui";
|
|
11
11
|
import chalk from "chalk";
|
|
12
|
-
import { spawn
|
|
13
|
-
import { APP_NAME, APP_TITLE, CONFIG_DIR_NAME, getAgentDir, getAuthPath, getDebugLogPath, getDocsPath,
|
|
12
|
+
import { spawn } from "child_process";
|
|
13
|
+
import { APP_NAME, APP_TITLE, CONFIG_DIR_NAME, getAgentDir, getAuthPath, getDebugLogPath, getDocsPath, VERSION, } from "../../config.js";
|
|
14
14
|
import { parseSkillBlock } from "../../core/agent-session.js";
|
|
15
15
|
import { SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js";
|
|
16
16
|
import { CACHE_TTL_MS, collectCacheMisses, computeCacheWaste, detectCacheMiss, } from "../../core/cache-stats.js";
|
|
17
|
+
import { DEFAULT_THINKING_LEVEL, THINKING_LEVEL_OPTIONS } from "../../core/defaults.js";
|
|
17
18
|
import { FooterDataProvider } from "../../core/footer-data-provider.js";
|
|
18
19
|
import { configureHttpDispatcher, formatHttpIdleTimeoutMs } from "../../core/http-dispatcher.js";
|
|
19
20
|
import { KeybindingsManager } from "../../core/keybindings.js";
|
|
@@ -35,12 +36,12 @@ import { openBrowser } from "../../utils/open-browser.js";
|
|
|
35
36
|
import { getCwdRelativePath } from "../../utils/paths.js";
|
|
36
37
|
import { getPiUserAgent } from "../../utils/pi-user-agent.js";
|
|
37
38
|
import { killTrackedDetachedChildren } from "../../utils/shell.js";
|
|
39
|
+
import { loadAllHighlightLanguages } from "../../utils/syntax-highlight.js";
|
|
38
40
|
import { ensureTool } from "../../utils/tools-manager.js";
|
|
39
41
|
import { checkForNewPiVersion } from "../../utils/version-check.js";
|
|
40
42
|
import { ArminComponent } from "./components/armin.js";
|
|
41
43
|
import { AssistantMessageComponent } from "./components/assistant-message.js";
|
|
42
44
|
import { BashExecutionComponent } from "./components/bash-execution.js";
|
|
43
|
-
import { BorderedLoader } from "./components/bordered-loader.js";
|
|
44
45
|
import { BranchSummaryMessageComponent } from "./components/branch-summary-message.js";
|
|
45
46
|
import { CompactionSummaryMessageComponent } from "./components/compaction-summary-message.js";
|
|
46
47
|
import { CustomEditor } from "./components/custom-editor.js";
|
|
@@ -63,13 +64,16 @@ import { SessionSelectorComponent } from "./components/session-selector.js";
|
|
|
63
64
|
import { SettingsSelectorComponent } from "./components/settings-selector.js";
|
|
64
65
|
import { SkillInvocationMessageComponent } from "./components/skill-invocation-message.js";
|
|
65
66
|
import { BranchSummaryStatusIndicator, CompactionStatusIndicator, IdleStatus, RetryStatusIndicator, WorkingStatusIndicator, } from "./components/status-indicator.js";
|
|
67
|
+
import { ThinkingSelectorComponent } from "./components/thinking-selector.js";
|
|
66
68
|
import { ToolExecutionComponent } from "./components/tool-execution.js";
|
|
67
69
|
import { TreeSelectorComponent } from "./components/tree-selector.js";
|
|
68
70
|
import { TrustSelectorComponent } from "./components/trust-selector.js";
|
|
69
71
|
import { UserMessageComponent } from "./components/user-message.js";
|
|
70
72
|
import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
|
|
71
73
|
import { editInExternalEditor } from "./external-editor.js";
|
|
74
|
+
import { refreshModelCatalogs } from "./model-catalog-refresh.js";
|
|
72
75
|
import { getModelSearchText } from "./model-search.js";
|
|
76
|
+
import { shareSession } from "./session-share.js";
|
|
73
77
|
import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, onThemeChange, setRegisteredThemes, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
|
|
74
78
|
import { InteractiveThemeController } from "./theme/theme-controller.js";
|
|
75
79
|
function isExpandable(obj) {
|
|
@@ -90,6 +94,9 @@ class ExpandableText extends Text {
|
|
|
90
94
|
function isCustomSessionEntry(item) {
|
|
91
95
|
return "type" in item && item.type === "custom";
|
|
92
96
|
}
|
|
97
|
+
function isCompactionCostNotice(item) {
|
|
98
|
+
return "type" in item && item.type === "compaction_cost";
|
|
99
|
+
}
|
|
93
100
|
const DEAD_TERMINAL_ERROR_CODES = new Set(["EIO", "EPIPE", "ENOTCONN"]);
|
|
94
101
|
function isDeadTerminalError(error) {
|
|
95
102
|
if (!error || typeof error !== "object" || !("code" in error)) {
|
|
@@ -129,6 +136,11 @@ export function formatResumeCommand(sessionManager) {
|
|
|
129
136
|
function hasDefaultModelProvider(providerId) {
|
|
130
137
|
return providerId in defaultModelPerProvider;
|
|
131
138
|
}
|
|
139
|
+
function llamaCppPostLoginGuidance(actionLabel, loadedModelCount) {
|
|
140
|
+
return loadedModelCount === 0
|
|
141
|
+
? `${actionLabel}. No llama.cpp models are loaded. Use /llama to load a model, then /model to select it.`
|
|
142
|
+
: `${actionLabel}. Use /model to select a loaded llama.cpp model, or /llama to manage models.`;
|
|
143
|
+
}
|
|
132
144
|
const AUTH_TYPE_ORDER = { oauth: 0, api_key: 1 };
|
|
133
145
|
function createFuzzyAutocompleteItems(items, prefix, getSearchText, toAutocompleteItem) {
|
|
134
146
|
const filtered = fuzzyFilter(items, prefix, getSearchText);
|
|
@@ -169,9 +181,21 @@ function formatLoginProviderCompletionDescription(provider) {
|
|
|
169
181
|
export function createInteractiveTui(options) {
|
|
170
182
|
const terminal = options.terminal ?? new ProcessTerminal();
|
|
171
183
|
if (options.tuiMode === "fullscreen") {
|
|
184
|
+
const styleSearchMatch = (text) => theme.bg("searchMatchBg", theme.fg("searchMatchText", text));
|
|
172
185
|
return new TuiAltScreen(terminal, options.showHardwareCursor, options.logDirectory, {
|
|
186
|
+
searchMatchStyle: (text) => theme.underline(styleSearchMatch(text)),
|
|
187
|
+
searchCurrentMatchStyle: (text) => theme.bold(theme.inverse(styleSearchMatch(text))),
|
|
173
188
|
openUrl: openBrowser,
|
|
174
189
|
onRightClickPaste: options.onRightClickPaste,
|
|
190
|
+
copySelection: async (text) => {
|
|
191
|
+
try {
|
|
192
|
+
await copyToClipboard(text);
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
},
|
|
175
199
|
});
|
|
176
200
|
}
|
|
177
201
|
return new TuiMainScreen(terminal, options.showHardwareCursor, options.logDirectory);
|
|
@@ -253,6 +277,7 @@ export class InteractiveMode {
|
|
|
253
277
|
// Status line tracking (for mutating immediately-sequential status updates)
|
|
254
278
|
lastStatusSpacer = undefined;
|
|
255
279
|
lastStatusText = undefined;
|
|
280
|
+
managedToolStatusStarted = false;
|
|
256
281
|
// Streaming message tracking
|
|
257
282
|
streamingComponent = undefined;
|
|
258
283
|
streamingMessage = undefined;
|
|
@@ -333,6 +358,7 @@ export class InteractiveMode {
|
|
|
333
358
|
});
|
|
334
359
|
this.runtimeHost.setRebindSession(async () => {
|
|
335
360
|
await this.rebindCurrentSession({ renderBeforeBind: true });
|
|
361
|
+
await this.themeController.applyFromSettings();
|
|
336
362
|
});
|
|
337
363
|
this.version = VERSION;
|
|
338
364
|
this.renderer = createInteractiveTui({
|
|
@@ -375,7 +401,12 @@ export class InteractiveMode {
|
|
|
375
401
|
this.outputPad = this.settingsManager.getOutputPad();
|
|
376
402
|
// Register themes from resource loader and initialize
|
|
377
403
|
setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
|
|
378
|
-
this.themeController = new InteractiveThemeController(this.ui,
|
|
404
|
+
this.themeController = new InteractiveThemeController(this.ui, {
|
|
405
|
+
getSettingsManager: () => this.settingsManager,
|
|
406
|
+
showError: (message) => this.showError(message),
|
|
407
|
+
onChanged: () => this.updateEditorBorderColor(),
|
|
408
|
+
initialThemeSetting: options.initialThemeSetting,
|
|
409
|
+
});
|
|
379
410
|
}
|
|
380
411
|
getAutocompleteSourceTag(sourceInfo) {
|
|
381
412
|
if (!sourceInfo) {
|
|
@@ -445,6 +476,15 @@ export class InteractiveMode {
|
|
|
445
476
|
}));
|
|
446
477
|
};
|
|
447
478
|
}
|
|
479
|
+
const thinkingCommand = slashCommands.find((command) => command.name === "thinking");
|
|
480
|
+
if (thinkingCommand) {
|
|
481
|
+
thinkingCommand.getArgumentCompletions = (prefix) => {
|
|
482
|
+
return createFuzzyAutocompleteItems(this.session.getAvailableThinkingLevels(), prefix, (level) => level, (level) => ({
|
|
483
|
+
value: level,
|
|
484
|
+
label: level,
|
|
485
|
+
}));
|
|
486
|
+
};
|
|
487
|
+
}
|
|
448
488
|
const loginCommand = slashCommands.find((command) => command.name === "login");
|
|
449
489
|
if (loginCommand) {
|
|
450
490
|
loginCommand.getArgumentCompletions = (prefix) => {
|
|
@@ -538,14 +578,14 @@ export class InteractiveMode {
|
|
|
538
578
|
tui.setLayoutRoot(this.fullscreenLayoutRoot);
|
|
539
579
|
}
|
|
540
580
|
}
|
|
541
|
-
stopInteractiveTui() {
|
|
542
|
-
if (this.renderer.mode === "fullscreen") {
|
|
581
|
+
stopInteractiveTui(fullscreenExitOutput) {
|
|
582
|
+
if (this.renderer.mode === "fullscreen" && fullscreenExitOutput === "transcript") {
|
|
543
583
|
while (this.renderer.hasOverlayEntries)
|
|
544
584
|
this.renderer.hideOverlay();
|
|
545
585
|
this.switchTuiMode("regular", false, false);
|
|
546
586
|
this.renderer.renderNow();
|
|
547
587
|
}
|
|
548
|
-
this.ui.stop();
|
|
588
|
+
this.ui.stop({ preserveScreen: this.renderer.mode === "fullscreen" });
|
|
549
589
|
}
|
|
550
590
|
switchTuiMode(mode, restoreProgress = true, startRenderer = true) {
|
|
551
591
|
const previousUi = this.renderer;
|
|
@@ -602,10 +642,6 @@ export class InteractiveMode {
|
|
|
602
642
|
this.registerSignalHandlers();
|
|
603
643
|
// Load changelog (only show new entries, skip for resumed sessions)
|
|
604
644
|
this.changelogMarkdown = this.getChangelogForDisplay();
|
|
605
|
-
// Ensure fd and rg are available (downloads if missing, adds to PATH via getBinDir)
|
|
606
|
-
// Both are needed: fd for autocomplete, rg for grep tool and bash commands
|
|
607
|
-
const [fdPath] = await Promise.all([ensureTool("fd"), ensureTool("rg")]);
|
|
608
|
-
this.fdPath = fdPath;
|
|
609
645
|
if (this.session.scopedModels.length > 0 && (this.options.verbose || !this.settingsManager.getQuietStartup())) {
|
|
610
646
|
const modelList = this.session.scopedModels
|
|
611
647
|
.map((sm) => {
|
|
@@ -649,9 +685,11 @@ export class InteractiveMode {
|
|
|
649
685
|
this.widgetContainerBelow,
|
|
650
686
|
this.footerContainer,
|
|
651
687
|
]);
|
|
688
|
+
// Accept text while startup completes, but only enable interrupt, exit, and submission feedback.
|
|
689
|
+
this.defaultEditor.onAction("app.clear", () => this.handleCtrlC());
|
|
690
|
+
this.defaultEditor.onCtrlD = () => this.handleCtrlD();
|
|
691
|
+
this.defaultEditor.onSubmit = (text) => this.handleStartupSubmit(text);
|
|
652
692
|
this.ui.setFocus(this.editor);
|
|
653
|
-
this.setupKeyHandlers();
|
|
654
|
-
this.setupEditorSubmitHandler();
|
|
655
693
|
// Start the UI before initializing extensions so session_start handlers can use interactive dialogs
|
|
656
694
|
this.ui.start();
|
|
657
695
|
this.isInitialized = true;
|
|
@@ -703,6 +741,18 @@ export class InteractiveMode {
|
|
|
703
741
|
this.headerContainer.addChild(this.builtInHeader);
|
|
704
742
|
}
|
|
705
743
|
this.ui.requestRender();
|
|
744
|
+
// Ensure fd and rg are available after mounting the TUI (downloads if missing, adds to PATH via getBinDir)
|
|
745
|
+
// so slow downloads do not make startup appear frozen.
|
|
746
|
+
// Both are needed: fd for autocomplete, rg for grep tool and bash commands.
|
|
747
|
+
const [fdPath] = await Promise.all([
|
|
748
|
+
ensureTool("fd", (status) => this.showManagedToolStatus(status)),
|
|
749
|
+
ensureTool("rg", (status) => this.showManagedToolStatus(status)),
|
|
750
|
+
]);
|
|
751
|
+
this.fdPath = fdPath;
|
|
752
|
+
// Enable the remaining input handlers only after managed-tool setup completes.
|
|
753
|
+
this.setupKeyHandlers();
|
|
754
|
+
this.setupEditorSubmitHandler();
|
|
755
|
+
this.ui.requestRender();
|
|
706
756
|
// Initialize extensions first so resources are shown before messages
|
|
707
757
|
await this.rebindCurrentSession();
|
|
708
758
|
// Render initial messages AFTER showing loaded resources
|
|
@@ -719,6 +769,14 @@ export class InteractiveMode {
|
|
|
719
769
|
});
|
|
720
770
|
// Initialize available provider count for footer display
|
|
721
771
|
await this.updateAvailableProviderCount();
|
|
772
|
+
// Flush the completed startup state before loading the remaining syntax grammars.
|
|
773
|
+
this.ui.renderNow();
|
|
774
|
+
void loadAllHighlightLanguages().then(() => {
|
|
775
|
+
if (!this.isInitialized)
|
|
776
|
+
return;
|
|
777
|
+
this.ui.invalidate();
|
|
778
|
+
this.ui.requestRender();
|
|
779
|
+
});
|
|
722
780
|
}
|
|
723
781
|
/**
|
|
724
782
|
* Update terminal title with session name and cwd.
|
|
@@ -742,8 +800,7 @@ export class InteractiveMode {
|
|
|
742
800
|
if (!process.env.PI_OFFLINE) {
|
|
743
801
|
const controller = new AbortController();
|
|
744
802
|
const timeout = setTimeout(() => controller.abort(), 15_000);
|
|
745
|
-
void this.session.modelRuntime
|
|
746
|
-
.refresh({ signal: controller.signal })
|
|
803
|
+
void refreshModelCatalogs(this.session.modelRuntime, controller.signal)
|
|
747
804
|
.then(() => this.updateAvailableProviderCount())
|
|
748
805
|
.catch(() => { })
|
|
749
806
|
.finally(() => clearTimeout(timeout));
|
|
@@ -775,7 +832,18 @@ export class InteractiveMode {
|
|
|
775
832
|
}
|
|
776
833
|
});
|
|
777
834
|
// Show startup warnings
|
|
778
|
-
const { migratedProviders, modelFallbackMessage, initialMessage, initialImages, initialMessages } = this.options;
|
|
835
|
+
const { migratedProviders, startupDiagnostics, modelFallbackMessage, initialMessage, initialImages, initialMessages, } = this.options;
|
|
836
|
+
for (const diagnostic of startupDiagnostics ?? []) {
|
|
837
|
+
if (diagnostic.type === "error") {
|
|
838
|
+
this.showError(diagnostic.message);
|
|
839
|
+
}
|
|
840
|
+
else if (diagnostic.type === "warning") {
|
|
841
|
+
this.showWarning(diagnostic.message);
|
|
842
|
+
}
|
|
843
|
+
else {
|
|
844
|
+
this.showStatus(diagnostic.message);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
779
847
|
if (migratedProviders && migratedProviders.length > 0) {
|
|
780
848
|
this.showWarning(`Migrated credentials to auth.json: ${migratedProviders.join(", ")}`);
|
|
781
849
|
}
|
|
@@ -1499,7 +1567,7 @@ export class InteractiveMode {
|
|
|
1499
1567
|
const message = error instanceof Error ? error.message : String(error);
|
|
1500
1568
|
this.showError(`${prefix}: ${message}`);
|
|
1501
1569
|
stopThemeWatcher();
|
|
1502
|
-
this.stop();
|
|
1570
|
+
this.stop("transcript");
|
|
1503
1571
|
process.exit(1);
|
|
1504
1572
|
}
|
|
1505
1573
|
renderCurrentSessionState() {
|
|
@@ -2291,6 +2359,10 @@ export class InteractiveMode {
|
|
|
2291
2359
|
// Silently ignore clipboard errors (may not have permission, etc.)
|
|
2292
2360
|
}
|
|
2293
2361
|
}
|
|
2362
|
+
handleStartupSubmit(text) {
|
|
2363
|
+
this.editor.setText(text);
|
|
2364
|
+
this.showStatus("Startup is still in progress");
|
|
2365
|
+
}
|
|
2294
2366
|
setupEditorSubmitHandler() {
|
|
2295
2367
|
this.defaultEditor.onSubmit = async (text) => {
|
|
2296
2368
|
text = text.trim();
|
|
@@ -2313,6 +2385,12 @@ export class InteractiveMode {
|
|
|
2313
2385
|
await this.handleModelCommand(searchTerm);
|
|
2314
2386
|
return;
|
|
2315
2387
|
}
|
|
2388
|
+
if (text === "/thinking" || text.startsWith("/thinking ")) {
|
|
2389
|
+
const searchTerm = text.startsWith("/thinking ") ? text.slice(10).trim() : undefined;
|
|
2390
|
+
this.editor.setText("");
|
|
2391
|
+
this.handleThinkingCommand(searchTerm);
|
|
2392
|
+
return;
|
|
2393
|
+
}
|
|
2316
2394
|
if (text === "/export" || text.startsWith("/export ")) {
|
|
2317
2395
|
await this.handleExportCommand(text);
|
|
2318
2396
|
this.editor.setText("");
|
|
@@ -2691,9 +2769,21 @@ export class InteractiveMode {
|
|
|
2691
2769
|
}
|
|
2692
2770
|
}
|
|
2693
2771
|
else if (event.result) {
|
|
2772
|
+
const entries = this.sessionManager.buildContextEntries();
|
|
2773
|
+
if (entries[0]?.type !== "compaction") {
|
|
2774
|
+
throw new Error("Completed compaction is missing from the session context");
|
|
2775
|
+
}
|
|
2694
2776
|
this.chatContainer.clear();
|
|
2695
|
-
|
|
2777
|
+
// The latest compaction is prepended for model context; append it below at its chronological position.
|
|
2778
|
+
this.renderSessionEntries(entries.slice(1));
|
|
2696
2779
|
this.addMessageToChat(createCompactionSummaryMessage(event.result.summary, event.result.tokensBefore, new Date().toISOString()));
|
|
2780
|
+
if (event.result.usage) {
|
|
2781
|
+
this.addCompactionCostNotice({
|
|
2782
|
+
type: "compaction_cost",
|
|
2783
|
+
kind: "compaction",
|
|
2784
|
+
usage: event.result.usage,
|
|
2785
|
+
});
|
|
2786
|
+
}
|
|
2697
2787
|
this.footer.invalidate();
|
|
2698
2788
|
}
|
|
2699
2789
|
else if (event.errorMessage) {
|
|
@@ -2766,6 +2856,19 @@ export class InteractiveMode {
|
|
|
2766
2856
|
: message.content.filter((c) => c.type === "text");
|
|
2767
2857
|
return textBlocks.map((c) => c.text).join("");
|
|
2768
2858
|
}
|
|
2859
|
+
/** Show a managed-tool status update in the chat. */
|
|
2860
|
+
showManagedToolStatus(status) {
|
|
2861
|
+
if (!this.managedToolStatusStarted) {
|
|
2862
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2863
|
+
this.managedToolStatusStarted = true;
|
|
2864
|
+
}
|
|
2865
|
+
const message = status.type === "warning" ? `Warning: ${status.message}` : status.message;
|
|
2866
|
+
const color = status.type === "warning" ? "warning" : "dim";
|
|
2867
|
+
this.chatContainer.addChild(new Text(theme.fg(color, message), 1, 0));
|
|
2868
|
+
this.lastStatusSpacer = undefined;
|
|
2869
|
+
this.lastStatusText = undefined;
|
|
2870
|
+
this.ui.requestRender();
|
|
2871
|
+
}
|
|
2769
2872
|
/**
|
|
2770
2873
|
* Show a status message in the chat.
|
|
2771
2874
|
*
|
|
@@ -2902,6 +3005,10 @@ export class InteractiveMode {
|
|
|
2902
3005
|
this.addCustomEntryToChat(item);
|
|
2903
3006
|
continue;
|
|
2904
3007
|
}
|
|
3008
|
+
if (isCompactionCostNotice(item)) {
|
|
3009
|
+
this.addCompactionCostNotice(item);
|
|
3010
|
+
continue;
|
|
3011
|
+
}
|
|
2905
3012
|
const message = item;
|
|
2906
3013
|
// Assistant messages need special handling for tool calls
|
|
2907
3014
|
if (message.role === "assistant") {
|
|
@@ -2969,10 +3076,28 @@ export class InteractiveMode {
|
|
|
2969
3076
|
if (entry.type === "custom") {
|
|
2970
3077
|
return [entry];
|
|
2971
3078
|
}
|
|
2972
|
-
|
|
3079
|
+
const messages = sessionEntryToContextMessages(entry);
|
|
3080
|
+
if ((entry.type === "compaction" || entry.type === "branch_summary") && entry.usage && messages.length > 0) {
|
|
3081
|
+
return [...messages, { type: "compaction_cost", kind: entry.type, usage: entry.usage }];
|
|
3082
|
+
}
|
|
3083
|
+
return messages;
|
|
2973
3084
|
});
|
|
2974
3085
|
this.renderSessionItems(items, options);
|
|
2975
3086
|
}
|
|
3087
|
+
/**
|
|
3088
|
+
* Render billing usage for a compaction or branch summary. The notice is derived
|
|
3089
|
+
* from persisted summary usage and is not stored as a separate session entry.
|
|
3090
|
+
*/
|
|
3091
|
+
addCompactionCostNotice(notice) {
|
|
3092
|
+
if (!this.settingsManager.getShowCacheMissNotices())
|
|
3093
|
+
return;
|
|
3094
|
+
const { usage } = notice;
|
|
3095
|
+
const tokens = usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
|
|
3096
|
+
const cost = usage.cost.total >= 0.01 ? ` (~$${usage.cost.total.toFixed(2)})` : "";
|
|
3097
|
+
const label = notice.kind === "compaction" ? "Compaction" : "Branch summary";
|
|
3098
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3099
|
+
this.chatContainer.addChild(new Text(theme.fg("warning", `${label}: ${formatTokens(tokens)} tokens billed${cost}`), 1, 0));
|
|
3100
|
+
}
|
|
2976
3101
|
/**
|
|
2977
3102
|
* Show a transcript notice when a completed assistant message paid for a
|
|
2978
3103
|
* significant cache miss. Only states observable facts: the miss itself,
|
|
@@ -3137,7 +3262,7 @@ export class InteractiveMode {
|
|
|
3137
3262
|
this.ui.stop();
|
|
3138
3263
|
}
|
|
3139
3264
|
catch { }
|
|
3140
|
-
console.error(
|
|
3265
|
+
console.error(`${APP_NAME} exiting due to uncaughtException:`);
|
|
3141
3266
|
console.error(error);
|
|
3142
3267
|
process.exit(1);
|
|
3143
3268
|
}
|
|
@@ -3609,8 +3734,14 @@ export class InteractiveMode {
|
|
|
3609
3734
|
showSettingsSelector() {
|
|
3610
3735
|
this.showSelector((done) => {
|
|
3611
3736
|
let selector;
|
|
3737
|
+
const defaultProvider = this.settingsManager.getDefaultProvider();
|
|
3738
|
+
const defaultModelId = this.settingsManager.getDefaultModel();
|
|
3739
|
+
const defaultModel = defaultProvider && defaultModelId ? `${defaultProvider}/${defaultModelId}` : "not set";
|
|
3612
3740
|
selector = new SettingsSelectorComponent({
|
|
3613
3741
|
autoCompact: this.session.autoCompactionEnabled,
|
|
3742
|
+
defaultModel,
|
|
3743
|
+
currentModel: this.session.model,
|
|
3744
|
+
availableDefaultModels: this.session.modelRuntime.getAvailableSnapshot(),
|
|
3614
3745
|
showImages: this.settingsManager.getShowImages(),
|
|
3615
3746
|
imageWidthCells: this.settingsManager.getImageWidthCells(),
|
|
3616
3747
|
autoResizeImages: this.settingsManager.getImageAutoResize(),
|
|
@@ -3620,9 +3751,10 @@ export class InteractiveMode {
|
|
|
3620
3751
|
followUpMode: this.session.followUpMode,
|
|
3621
3752
|
transport: this.settingsManager.getTransport(),
|
|
3622
3753
|
httpIdleTimeoutMs: this.settingsManager.getHttpIdleTimeoutMs(),
|
|
3623
|
-
thinkingLevel: this.
|
|
3624
|
-
availableThinkingLevels:
|
|
3625
|
-
|
|
3754
|
+
thinkingLevel: this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL,
|
|
3755
|
+
availableThinkingLevels: [...THINKING_LEVEL_OPTIONS],
|
|
3756
|
+
modelThinkingLevels: this.settingsManager.getAllModelThinkingLevels(),
|
|
3757
|
+
currentTheme: this.themeController.getThemeSelection() || "dark",
|
|
3626
3758
|
terminalTheme: this.themeController.getTerminalTheme(),
|
|
3627
3759
|
availableThemes: getAvailableThemes(),
|
|
3628
3760
|
hideThinkingBlock: this.hideThinkingBlock,
|
|
@@ -3641,6 +3773,7 @@ export class InteractiveMode {
|
|
|
3641
3773
|
clearOnShrink: this.settingsManager.getClearOnShrink(),
|
|
3642
3774
|
showTerminalProgress: this.settingsManager.getShowTerminalProgress(),
|
|
3643
3775
|
tuiMode: this.ui.mode,
|
|
3776
|
+
fullscreenExitOutput: this.settingsManager.getFullscreenExitOutput(),
|
|
3644
3777
|
fullscreenScrollbar: this.settingsManager.getFullscreenScrollbar(),
|
|
3645
3778
|
warnings: this.settingsManager.getWarnings(),
|
|
3646
3779
|
}, {
|
|
@@ -3689,14 +3822,30 @@ export class InteractiveMode {
|
|
|
3689
3822
|
configureHttpDispatcher(timeoutMs);
|
|
3690
3823
|
this.showStatus(`HTTP idle timeout: ${formatHttpIdleTimeoutMs(timeoutMs)}`);
|
|
3691
3824
|
},
|
|
3692
|
-
|
|
3693
|
-
this.
|
|
3694
|
-
|
|
3695
|
-
this.
|
|
3825
|
+
onModelThinkingLevelChange: (provider, modelId, level) => {
|
|
3826
|
+
this.settingsManager.setModelThinkingLevel(provider, modelId, level);
|
|
3827
|
+
// If the override is for the current model, apply it to the session too
|
|
3828
|
+
const current = this.session.model;
|
|
3829
|
+
if (current && current.provider === provider && current.id === modelId) {
|
|
3830
|
+
this.session.setThinkingLevel(level);
|
|
3831
|
+
this.footer.invalidate();
|
|
3832
|
+
this.updateEditorBorderColor();
|
|
3833
|
+
}
|
|
3834
|
+
},
|
|
3835
|
+
onModelThinkingLevelRemove: (provider, modelId) => {
|
|
3836
|
+
this.settingsManager.removeModelThinkingLevel(provider, modelId);
|
|
3837
|
+
// If the override was for the current model, revert to global default
|
|
3838
|
+
const current = this.session.model;
|
|
3839
|
+
if (current && current.provider === provider && current.id === modelId) {
|
|
3840
|
+
const globalDefault = this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
|
|
3841
|
+
this.session.setThinkingLevel(globalDefault);
|
|
3842
|
+
this.footer.invalidate();
|
|
3843
|
+
this.updateEditorBorderColor();
|
|
3844
|
+
}
|
|
3696
3845
|
},
|
|
3697
3846
|
onThemeChange: (themeSetting) => {
|
|
3698
3847
|
this.settingsManager.setTheme(themeSetting);
|
|
3699
|
-
void this.themeController.
|
|
3848
|
+
void this.themeController.setThemeSetting(themeSetting);
|
|
3700
3849
|
},
|
|
3701
3850
|
onThemePreview: (themeName) => this.themeController.preview(themeName),
|
|
3702
3851
|
onHideThinkingBlockChange: (hidden) => {
|
|
@@ -3795,6 +3944,9 @@ export class InteractiveMode {
|
|
|
3795
3944
|
this.statusContainer.clear();
|
|
3796
3945
|
this.showStatus(`TUI mode: ${mode}`);
|
|
3797
3946
|
},
|
|
3947
|
+
onFullscreenExitOutputChange: (output) => {
|
|
3948
|
+
this.settingsManager.setFullscreenExitOutput(output);
|
|
3949
|
+
},
|
|
3798
3950
|
onFullscreenScrollbarChange: (mode) => {
|
|
3799
3951
|
this.settingsManager.setFullscreenScrollbar(mode);
|
|
3800
3952
|
this.applyFullscreenScrollbarSetting();
|
|
@@ -3810,6 +3962,44 @@ export class InteractiveMode {
|
|
|
3810
3962
|
return { component: selector, focus: selector.getSettingsList() };
|
|
3811
3963
|
});
|
|
3812
3964
|
}
|
|
3965
|
+
handleThinkingCommand(searchTerm) {
|
|
3966
|
+
const availableLevels = this.session.getAvailableThinkingLevels();
|
|
3967
|
+
if (!searchTerm) {
|
|
3968
|
+
this.showThinkingSelector();
|
|
3969
|
+
return;
|
|
3970
|
+
}
|
|
3971
|
+
const normalized = searchTerm.trim().toLowerCase();
|
|
3972
|
+
const level = availableLevels.find((candidate) => candidate.toLowerCase() === normalized);
|
|
3973
|
+
if (!level) {
|
|
3974
|
+
this.showError(`Unknown thinking level "${searchTerm}". Available levels: ${availableLevels.join(", ")}.`);
|
|
3975
|
+
return;
|
|
3976
|
+
}
|
|
3977
|
+
this.selectThinkingLevel(level, false);
|
|
3978
|
+
}
|
|
3979
|
+
selectThinkingLevel(level, persist) {
|
|
3980
|
+
try {
|
|
3981
|
+
this.session.setThinkingLevel(level, { persist });
|
|
3982
|
+
this.footer.invalidate();
|
|
3983
|
+
this.updateEditorBorderColor();
|
|
3984
|
+
this.showStatus(persist ? `Default thinking level: ${level}` : `Thinking level: ${level}`);
|
|
3985
|
+
}
|
|
3986
|
+
catch (error) {
|
|
3987
|
+
this.showError(error instanceof Error ? error.message : String(error));
|
|
3988
|
+
}
|
|
3989
|
+
}
|
|
3990
|
+
showThinkingSelector() {
|
|
3991
|
+
this.showSelector((done) => {
|
|
3992
|
+
const selectLevel = (level, persist) => {
|
|
3993
|
+
this.selectThinkingLevel(level, persist);
|
|
3994
|
+
done();
|
|
3995
|
+
};
|
|
3996
|
+
const selector = new ThinkingSelectorComponent(this.session.thinkingLevel ?? DEFAULT_THINKING_LEVEL, this.session.getAvailableThinkingLevels(), (level) => selectLevel(level, false), () => {
|
|
3997
|
+
done();
|
|
3998
|
+
this.ui.requestRender();
|
|
3999
|
+
}, (level) => selectLevel(level, true), this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);
|
|
4000
|
+
return { component: selector, focus: selector };
|
|
4001
|
+
});
|
|
4002
|
+
}
|
|
3813
4003
|
async handleModelCommand(searchTerm) {
|
|
3814
4004
|
if (!searchTerm) {
|
|
3815
4005
|
this.showModelSelector();
|
|
@@ -3818,7 +4008,7 @@ export class InteractiveMode {
|
|
|
3818
4008
|
const model = await this.findExactModelMatch(searchTerm);
|
|
3819
4009
|
if (model) {
|
|
3820
4010
|
try {
|
|
3821
|
-
await this.session.setModel(model);
|
|
4011
|
+
await this.session.setModel(model, { persist: false });
|
|
3822
4012
|
this.footer.invalidate();
|
|
3823
4013
|
this.updateEditorBorderColor();
|
|
3824
4014
|
this.showStatus(`Model: ${model.id}`);
|
|
@@ -3847,7 +4037,7 @@ export class InteractiveMode {
|
|
|
3847
4037
|
controller.abort();
|
|
3848
4038
|
}, 15_000);
|
|
3849
4039
|
try {
|
|
3850
|
-
const result = await this.session.modelRuntime
|
|
4040
|
+
const result = await refreshModelCatalogs(this.session.modelRuntime, controller.signal);
|
|
3851
4041
|
if (result.aborted && timedOut) {
|
|
3852
4042
|
this.showWarning("Model refresh timed out; searching cached models.");
|
|
3853
4043
|
}
|
|
@@ -3935,7 +4125,7 @@ export class InteractiveMode {
|
|
|
3935
4125
|
onSelect: (selection) => {
|
|
3936
4126
|
trustStore.setMany(selection.updates);
|
|
3937
4127
|
done();
|
|
3938
|
-
this.showStatus(`Saved trust decision: ${selection.trusted ? "trusted" : "untrusted"}. Restart
|
|
4128
|
+
this.showStatus(`Saved trust decision: ${selection.trusted ? "trusted" : "untrusted"}. Restart ${APP_NAME} for this to take effect.`);
|
|
3939
4129
|
},
|
|
3940
4130
|
onCancel: () => {
|
|
3941
4131
|
done();
|
|
@@ -3947,13 +4137,13 @@ export class InteractiveMode {
|
|
|
3947
4137
|
}
|
|
3948
4138
|
showModelSelector(initialSearchInput) {
|
|
3949
4139
|
this.showSelector((done) => {
|
|
3950
|
-
const
|
|
4140
|
+
const selectModel = async (model, persist) => {
|
|
3951
4141
|
try {
|
|
3952
|
-
await this.session.setModel(model);
|
|
4142
|
+
await this.session.setModel(model, { persist });
|
|
3953
4143
|
this.footer.invalidate();
|
|
3954
4144
|
this.updateEditorBorderColor();
|
|
3955
4145
|
done();
|
|
3956
|
-
this.showStatus(`Model: ${model.id}`);
|
|
4146
|
+
this.showStatus(persist ? `Default model: ${model.provider}/${model.id}` : `Model: ${model.id}`);
|
|
3957
4147
|
void this.maybeWarnAboutAnthropicSubscriptionAuth(model);
|
|
3958
4148
|
this.checkDaxnutsEasterEgg(model);
|
|
3959
4149
|
}
|
|
@@ -3961,10 +4151,13 @@ export class InteractiveMode {
|
|
|
3961
4151
|
done();
|
|
3962
4152
|
this.showError(error instanceof Error ? error.message : String(error));
|
|
3963
4153
|
}
|
|
3964
|
-
}
|
|
4154
|
+
};
|
|
4155
|
+
const defaultProvider = this.settingsManager.getDefaultProvider();
|
|
4156
|
+
const defaultModel = this.settingsManager.getDefaultModel();
|
|
4157
|
+
const selector = new ModelSelectorComponent(this.ui, this.session.model, this.session.modelRuntime, this.session.scopedModels, (model) => selectModel(model, false), () => {
|
|
3965
4158
|
done();
|
|
3966
4159
|
this.ui.requestRender();
|
|
3967
|
-
}, initialSearchInput);
|
|
4160
|
+
}, initialSearchInput, (model) => selectModel(model, true), defaultProvider && defaultModel ? { provider: defaultProvider, id: defaultModel } : undefined);
|
|
3968
4161
|
return { component: selector, focus: selector, dispose: () => selector.dispose() };
|
|
3969
4162
|
});
|
|
3970
4163
|
}
|
|
@@ -4035,8 +4228,7 @@ export class InteractiveMode {
|
|
|
4035
4228
|
this.ui.requestRender();
|
|
4036
4229
|
},
|
|
4037
4230
|
});
|
|
4038
|
-
void this.session.modelRuntime
|
|
4039
|
-
.refresh({ signal: controller.signal })
|
|
4231
|
+
void refreshModelCatalogs(this.session.modelRuntime, controller.signal)
|
|
4040
4232
|
.then((result) => {
|
|
4041
4233
|
if (disposed)
|
|
4042
4234
|
return;
|
|
@@ -4521,7 +4713,11 @@ export class InteractiveMode {
|
|
|
4521
4713
|
if (isUnknownModel(previousModel)) {
|
|
4522
4714
|
const availableModels = this.session.modelRuntime.getAvailableSnapshot();
|
|
4523
4715
|
const providerModels = availableModels.filter((model) => model.provider === providerId);
|
|
4524
|
-
|
|
4716
|
+
// Matches LLAMA_PROVIDER_ID from extensions/llama/provider.ts; kept inline to avoid coupling interactive mode to the built-in extension.
|
|
4717
|
+
if (providerId === "llama.cpp") {
|
|
4718
|
+
selectionError = llamaCppPostLoginGuidance(actionLabel, providerModels.length);
|
|
4719
|
+
}
|
|
4720
|
+
else if (!hasDefaultModelProvider(providerId)) {
|
|
4525
4721
|
selectionError = `${actionLabel}, but no default model is configured for provider "${providerId}". Use /model to select a model.`;
|
|
4526
4722
|
}
|
|
4527
4723
|
else if (providerModels.length === 0) {
|
|
@@ -4535,7 +4731,7 @@ export class InteractiveMode {
|
|
|
4535
4731
|
}
|
|
4536
4732
|
else {
|
|
4537
4733
|
try {
|
|
4538
|
-
await this.session.setModel(selectedModel);
|
|
4734
|
+
await this.session.setModel(selectedModel, { persist: true });
|
|
4539
4735
|
}
|
|
4540
4736
|
catch (error) {
|
|
4541
4737
|
selectedModel = undefined;
|
|
@@ -4590,7 +4786,7 @@ export class InteractiveMode {
|
|
|
4590
4786
|
this.ui.requestRender();
|
|
4591
4787
|
};
|
|
4592
4788
|
const dialog = new LoginDialogComponent(this.ui, providerOption.id, () => restoreEditor(), providerOption.name, `${providerOption.name} setup`);
|
|
4593
|
-
dialog.showInfo(`${providerOption.method?.name ?? "Authentication"} is configured outside
|
|
4789
|
+
dialog.showInfo(`${providerOption.method?.name ?? "Authentication"} is configured outside ${APP_NAME}.`, [], true);
|
|
4594
4790
|
this.editorContainer.clear();
|
|
4595
4791
|
this.editorContainer.addChild(dialog);
|
|
4596
4792
|
this.ui.setFocus(dialog);
|
|
@@ -4827,7 +5023,9 @@ export class InteractiveMode {
|
|
|
4827
5023
|
this.showStatus(`Session exported to: ${filePath}`);
|
|
4828
5024
|
}
|
|
4829
5025
|
else {
|
|
4830
|
-
const filePath = await this.session.exportToHtml(outputPath
|
|
5026
|
+
const filePath = await this.session.exportToHtml(outputPath, {
|
|
5027
|
+
themeName: theme.name,
|
|
5028
|
+
});
|
|
4831
5029
|
this.showStatus(`Session exported to: ${filePath}`);
|
|
4832
5030
|
}
|
|
4833
5031
|
}
|
|
@@ -4903,91 +5101,14 @@ export class InteractiveMode {
|
|
|
4903
5101
|
}
|
|
4904
5102
|
}
|
|
4905
5103
|
async handleShareCommand() {
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
}
|
|
4914
|
-
catch {
|
|
4915
|
-
this.showError("GitHub CLI (gh) is not installed. Install it from https://cli.github.com/");
|
|
4916
|
-
return;
|
|
4917
|
-
}
|
|
4918
|
-
// Export to a temp file
|
|
4919
|
-
const tmpFile = path.join(os.tmpdir(), "session.html");
|
|
4920
|
-
try {
|
|
4921
|
-
await this.session.exportToHtml(tmpFile);
|
|
4922
|
-
}
|
|
4923
|
-
catch (error) {
|
|
4924
|
-
this.showError(`Failed to export session: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
4925
|
-
return;
|
|
4926
|
-
}
|
|
4927
|
-
// Show cancellable loader, replacing the editor
|
|
4928
|
-
const loader = new BorderedLoader(this.ui, theme, "Creating gist...");
|
|
4929
|
-
this.editorContainer.clear();
|
|
4930
|
-
this.editorContainer.addChild(loader);
|
|
4931
|
-
this.ui.setFocus(loader);
|
|
4932
|
-
this.ui.requestRender();
|
|
4933
|
-
const restoreEditor = () => {
|
|
4934
|
-
loader.dispose();
|
|
4935
|
-
this.editorContainer.clear();
|
|
4936
|
-
this.editorContainer.addChild(this.editor);
|
|
4937
|
-
this.ui.setFocus(this.editor);
|
|
4938
|
-
try {
|
|
4939
|
-
fs.unlinkSync(tmpFile);
|
|
4940
|
-
}
|
|
4941
|
-
catch {
|
|
4942
|
-
// Ignore cleanup errors
|
|
4943
|
-
}
|
|
4944
|
-
};
|
|
4945
|
-
// Create a secret gist asynchronously
|
|
4946
|
-
let proc = null;
|
|
4947
|
-
loader.onAbort = () => {
|
|
4948
|
-
proc?.kill();
|
|
4949
|
-
restoreEditor();
|
|
4950
|
-
this.showStatus("Share cancelled");
|
|
4951
|
-
};
|
|
4952
|
-
try {
|
|
4953
|
-
const result = await new Promise((resolve) => {
|
|
4954
|
-
proc = spawn("gh", ["gist", "create", "--public=false", tmpFile]);
|
|
4955
|
-
let stdout = "";
|
|
4956
|
-
let stderr = "";
|
|
4957
|
-
proc.stdout?.on("data", (data) => {
|
|
4958
|
-
stdout += data.toString();
|
|
4959
|
-
});
|
|
4960
|
-
proc.stderr?.on("data", (data) => {
|
|
4961
|
-
stderr += data.toString();
|
|
4962
|
-
});
|
|
4963
|
-
proc.on("close", (code) => resolve({ stdout, stderr, code }));
|
|
4964
|
-
});
|
|
4965
|
-
if (loader.signal.aborted)
|
|
4966
|
-
return;
|
|
4967
|
-
restoreEditor();
|
|
4968
|
-
if (result.code !== 0) {
|
|
4969
|
-
const errorMsg = result.stderr?.trim() || "Unknown error";
|
|
4970
|
-
this.showError(`Failed to create gist: ${errorMsg}`);
|
|
4971
|
-
return;
|
|
4972
|
-
}
|
|
4973
|
-
// Extract gist ID from the URL returned by gh
|
|
4974
|
-
// gh returns something like: https://gist.github.com/username/GIST_ID
|
|
4975
|
-
const gistUrl = result.stdout?.trim();
|
|
4976
|
-
const gistId = gistUrl?.split("/").pop();
|
|
4977
|
-
if (!gistId) {
|
|
4978
|
-
this.showError("Failed to parse gist ID from gh output");
|
|
4979
|
-
return;
|
|
4980
|
-
}
|
|
4981
|
-
// Create the preview URL
|
|
4982
|
-
const previewUrl = getShareViewerUrl(gistId);
|
|
4983
|
-
this.showStatus(`Share URL: ${previewUrl}\nGist: ${gistUrl}`);
|
|
4984
|
-
}
|
|
4985
|
-
catch (error) {
|
|
4986
|
-
if (!loader.signal.aborted) {
|
|
4987
|
-
restoreEditor();
|
|
4988
|
-
this.showError(`Failed to create gist: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
4989
|
-
}
|
|
4990
|
-
}
|
|
5104
|
+
await shareSession({
|
|
5105
|
+
session: this.session,
|
|
5106
|
+
ui: this.ui,
|
|
5107
|
+
editorContainer: this.editorContainer,
|
|
5108
|
+
editor: this.editor,
|
|
5109
|
+
showStatus: (message) => this.showStatus(message),
|
|
5110
|
+
showError: (message) => this.showError(message),
|
|
5111
|
+
});
|
|
4991
5112
|
}
|
|
4992
5113
|
async handleCopyCommand(options = {}) {
|
|
4993
5114
|
const text = this.session.getLastAssistantText();
|
|
@@ -5364,7 +5485,7 @@ export class InteractiveMode {
|
|
|
5364
5485
|
// Ignore, will be emitted as an event
|
|
5365
5486
|
}
|
|
5366
5487
|
}
|
|
5367
|
-
stop() {
|
|
5488
|
+
stop(fullscreenExitOutput = this.settingsManager.getFullscreenExitOutput()) {
|
|
5368
5489
|
this.disposeActiveSelector();
|
|
5369
5490
|
if (this.settingsManager.getShowTerminalProgress()) {
|
|
5370
5491
|
this.ui.terminal.setProgress(false);
|
|
@@ -5378,7 +5499,7 @@ export class InteractiveMode {
|
|
|
5378
5499
|
this.unsubscribe();
|
|
5379
5500
|
}
|
|
5380
5501
|
if (this.isInitialized) {
|
|
5381
|
-
this.stopInteractiveTui();
|
|
5502
|
+
this.stopInteractiveTui(fullscreenExitOutput);
|
|
5382
5503
|
this.isInitialized = false;
|
|
5383
5504
|
}
|
|
5384
5505
|
this.unregisterSignalHandlers();
|