@earendil-works/pi-coding-agent 0.84.2 → 0.84.4

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.
Files changed (304) hide show
  1. package/CHANGELOG.md +127 -0
  2. package/README.md +13 -8
  3. package/dist/bundle/chunks/anthropic-messages-JWX2WP65.js +45 -0
  4. package/dist/bundle/chunks/anthropic.js +75 -0
  5. package/dist/bundle/chunks/azure-openai-responses-5ENVCQ7U.js +2 -0
  6. package/dist/bundle/chunks/bedrock-converse-stream.js +74 -0
  7. package/dist/bundle/chunks/chunk-2KVJKXS2.js +50 -0
  8. package/dist/bundle/chunks/chunk-4L3WN2XY.js +2 -0
  9. package/dist/bundle/chunks/chunk-6CYCI3EJ.js +12 -0
  10. package/dist/bundle/chunks/chunk-6R7UI4IY.js +2 -0
  11. package/dist/bundle/chunks/chunk-AXIIZGTV.js +2 -0
  12. package/dist/bundle/chunks/chunk-DRQDKNXA.js +2 -0
  13. package/dist/bundle/chunks/chunk-GMWCTUPB.js +2 -0
  14. package/dist/bundle/chunks/chunk-JUUC5AZY.js +2 -0
  15. package/dist/bundle/chunks/chunk-L2ZPNLYD.js +2 -0
  16. package/dist/bundle/chunks/chunk-MNAIPA3J.js +2 -0
  17. package/dist/bundle/chunks/chunk-NBBFIJUL.js +11 -0
  18. package/dist/bundle/chunks/chunk-NUHFSC37.js +25 -0
  19. package/dist/bundle/chunks/chunk-OMWWHBTG.js +1548 -0
  20. package/dist/bundle/chunks/chunk-PGXDEOVV.js +2 -0
  21. package/dist/bundle/chunks/chunk-PJACZCIR.js +2 -0
  22. package/dist/bundle/chunks/chunk-PRRNXB7S.js +2 -0
  23. package/dist/bundle/chunks/chunk-TEPMHNKQ.js +2 -0
  24. package/dist/bundle/chunks/chunk-TRDNDS6A.js +4 -0
  25. package/dist/bundle/chunks/chunk-UAQELI3K.js +2 -0
  26. package/dist/bundle/chunks/chunk-XDUWOHPL.js +6 -0
  27. package/dist/bundle/chunks/chunk-XNGRGP62.js +2 -0
  28. package/dist/bundle/chunks/github-copilot.js +2 -0
  29. package/dist/bundle/chunks/google-generative-ai-YMUPJBKR.js +2 -0
  30. package/dist/bundle/chunks/google-vertex-MPWMV4OF.js +2 -0
  31. package/dist/bundle/chunks/https-proxy-agent-2VXB7436.js +13 -0
  32. package/dist/bundle/chunks/image-resize-worker.js +2 -0
  33. package/dist/bundle/chunks/kimi-coding.js +2 -0
  34. package/dist/bundle/chunks/lib-HGQMNYVT.js +9 -0
  35. package/dist/bundle/chunks/mistral-conversations-Q3AWZJAZ.js +5 -0
  36. package/dist/bundle/chunks/multipart-parser-SQ6GDJL7.js +3 -0
  37. package/dist/bundle/chunks/openai-codex-responses-GJVBJXLB.js +8 -0
  38. package/dist/bundle/chunks/openai-codex.js +75 -0
  39. package/dist/bundle/chunks/openai-completions-ERMU2SS7.js +7 -0
  40. package/dist/bundle/chunks/openai-responses-BSOJMSEV.js +2 -0
  41. package/dist/bundle/chunks/openrouter-images-N4XVVFSD.js +2 -0
  42. package/dist/bundle/chunks/openrouter.js +75 -0
  43. package/dist/bundle/chunks/pi-messages-TPFH44NP.js +8 -0
  44. package/dist/bundle/chunks/radius.js +75 -0
  45. package/dist/bundle/chunks/src-UK77SYZE.js +4 -0
  46. package/dist/bundle/chunks/xai.js +2 -0
  47. package/dist/bundle/cli.js +3 -0
  48. package/dist/bundle/client.js +2 -0
  49. package/dist/bundle/index.js +2 -0
  50. package/dist/bundle/rpc-entry.js +3 -0
  51. package/dist/cli/args.d.ts +1 -0
  52. package/dist/cli/args.d.ts.map +1 -1
  53. package/dist/cli/args.js +29 -9
  54. package/dist/cli/args.js.map +1 -1
  55. package/dist/cli/file-processor.d.ts.map +1 -1
  56. package/dist/cli/file-processor.js +2 -1
  57. package/dist/cli/file-processor.js.map +1 -1
  58. package/dist/cli/startup-ui.d.ts.map +1 -1
  59. package/dist/cli/startup-ui.js +2 -1
  60. package/dist/cli/startup-ui.js.map +1 -1
  61. package/dist/config.d.ts +4 -3
  62. package/dist/config.d.ts.map +1 -1
  63. package/dist/config.js +22 -14
  64. package/dist/config.js.map +1 -1
  65. package/dist/core/agent-session.d.ts +38 -8
  66. package/dist/core/agent-session.d.ts.map +1 -1
  67. package/dist/core/agent-session.js +240 -104
  68. package/dist/core/agent-session.js.map +1 -1
  69. package/dist/core/auth-storage.d.ts.map +1 -1
  70. package/dist/core/auth-storage.js +6 -7
  71. package/dist/core/auth-storage.js.map +1 -1
  72. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  73. package/dist/core/compaction/branch-summarization.js +7 -3
  74. package/dist/core/compaction/branch-summarization.js.map +1 -1
  75. package/dist/core/compaction/compaction.d.ts +9 -3
  76. package/dist/core/compaction/compaction.d.ts.map +1 -1
  77. package/dist/core/compaction/compaction.js +59 -36
  78. package/dist/core/compaction/compaction.js.map +1 -1
  79. package/dist/core/defaults.d.ts +1 -0
  80. package/dist/core/defaults.d.ts.map +1 -1
  81. package/dist/core/defaults.js +9 -0
  82. package/dist/core/defaults.js.map +1 -1
  83. package/dist/core/extensions/index.d.ts +2 -2
  84. package/dist/core/extensions/index.d.ts.map +1 -1
  85. package/dist/core/extensions/index.js +1 -1
  86. package/dist/core/extensions/index.js.map +1 -1
  87. package/dist/core/extensions/loader.d.ts.map +1 -1
  88. package/dist/core/extensions/loader.js +115 -48
  89. package/dist/core/extensions/loader.js.map +1 -1
  90. package/dist/core/extensions/runner.d.ts +5 -0
  91. package/dist/core/extensions/runner.d.ts.map +1 -1
  92. package/dist/core/extensions/runner.js +45 -1
  93. package/dist/core/extensions/runner.js.map +1 -1
  94. package/dist/core/extensions/types.d.ts +56 -10
  95. package/dist/core/extensions/types.d.ts.map +1 -1
  96. package/dist/core/extensions/types.js +3 -0
  97. package/dist/core/extensions/types.js.map +1 -1
  98. package/dist/core/footer-data-provider.d.ts +1 -1
  99. package/dist/core/footer-data-provider.d.ts.map +1 -1
  100. package/dist/core/footer-data-provider.js +1 -1
  101. package/dist/core/footer-data-provider.js.map +1 -1
  102. package/dist/core/keybindings.d.ts +20 -19
  103. package/dist/core/keybindings.d.ts.map +1 -1
  104. package/dist/core/keybindings.js +26 -5
  105. package/dist/core/keybindings.js.map +1 -1
  106. package/dist/core/model-config.d.ts +5 -0
  107. package/dist/core/model-config.d.ts.map +1 -1
  108. package/dist/core/model-config.js +3 -1
  109. package/dist/core/model-config.js.map +1 -1
  110. package/dist/core/model-resolver.d.ts +1 -0
  111. package/dist/core/model-resolver.d.ts.map +1 -1
  112. package/dist/core/model-resolver.js +14 -8
  113. package/dist/core/model-resolver.js.map +1 -1
  114. package/dist/core/models-store.d.ts.map +1 -1
  115. package/dist/core/models-store.js +2 -1
  116. package/dist/core/models-store.js.map +1 -1
  117. package/dist/core/package-manager.d.ts.map +1 -1
  118. package/dist/core/package-manager.js +22 -14
  119. package/dist/core/package-manager.js.map +1 -1
  120. package/dist/core/pi-manifest.d.ts.map +1 -1
  121. package/dist/core/pi-manifest.js +2 -1
  122. package/dist/core/pi-manifest.js.map +1 -1
  123. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  124. package/dist/core/remote-catalog-provider.js +2 -1
  125. package/dist/core/remote-catalog-provider.js.map +1 -1
  126. package/dist/core/resource-loader.d.ts.map +1 -1
  127. package/dist/core/resource-loader.js +3 -2
  128. package/dist/core/resource-loader.js.map +1 -1
  129. package/dist/core/sdk.d.ts +2 -2
  130. package/dist/core/sdk.d.ts.map +1 -1
  131. package/dist/core/sdk.js +10 -3
  132. package/dist/core/sdk.js.map +1 -1
  133. package/dist/core/session-export.d.ts +4 -0
  134. package/dist/core/session-export.d.ts.map +1 -0
  135. package/dist/core/session-export.js +32 -0
  136. package/dist/core/session-export.js.map +1 -0
  137. package/dist/core/session-manager.d.ts.map +1 -1
  138. package/dist/core/session-manager.js +6 -3
  139. package/dist/core/session-manager.js.map +1 -1
  140. package/dist/core/settings-diagnostics.d.ts +9 -0
  141. package/dist/core/settings-diagnostics.d.ts.map +1 -0
  142. package/dist/core/settings-diagnostics.js +21 -0
  143. package/dist/core/settings-diagnostics.js.map +1 -0
  144. package/dist/core/settings-manager.d.ts +17 -1
  145. package/dist/core/settings-manager.d.ts.map +1 -1
  146. package/dist/core/settings-manager.js +69 -10
  147. package/dist/core/settings-manager.js.map +1 -1
  148. package/dist/core/skills.d.ts.map +1 -1
  149. package/dist/core/skills.js +51 -33
  150. package/dist/core/skills.js.map +1 -1
  151. package/dist/core/slash-commands.d.ts.map +1 -1
  152. package/dist/core/slash-commands.js +2 -1
  153. package/dist/core/slash-commands.js.map +1 -1
  154. package/dist/core/system-prompt.d.ts.map +1 -1
  155. package/dist/core/system-prompt.js +11 -2
  156. package/dist/core/system-prompt.js.map +1 -1
  157. package/dist/core/tools/bash.d.ts +14 -1
  158. package/dist/core/tools/bash.d.ts.map +1 -1
  159. package/dist/core/tools/bash.js +36 -20
  160. package/dist/core/tools/bash.js.map +1 -1
  161. package/dist/core/tools/edit-diff.d.ts +0 -5
  162. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  163. package/dist/core/tools/edit-diff.js +2 -5
  164. package/dist/core/tools/edit-diff.js.map +1 -1
  165. package/dist/core/tools/edit.d.ts.map +1 -1
  166. package/dist/core/tools/edit.js +20 -4
  167. package/dist/core/tools/edit.js.map +1 -1
  168. package/dist/core/tools/index.d.ts +4 -1
  169. package/dist/core/tools/index.d.ts.map +1 -1
  170. package/dist/core/tools/index.js +18 -1
  171. package/dist/core/tools/index.js.map +1 -1
  172. package/dist/core/tools/powershell.d.ts +16 -0
  173. package/dist/core/tools/powershell.d.ts.map +1 -0
  174. package/dist/core/tools/powershell.js +39 -0
  175. package/dist/core/tools/powershell.js.map +1 -0
  176. package/dist/core/trust-manager.d.ts.map +1 -1
  177. package/dist/core/trust-manager.js +2 -1
  178. package/dist/core/trust-manager.js.map +1 -1
  179. package/dist/extensions/llama/client.d.ts +6 -0
  180. package/dist/extensions/llama/client.d.ts.map +1 -1
  181. package/dist/extensions/llama/client.js +7 -0
  182. package/dist/extensions/llama/client.js.map +1 -1
  183. package/dist/extensions/llama/index.d.ts.map +1 -1
  184. package/dist/extensions/llama/index.js +2 -0
  185. package/dist/extensions/llama/index.js.map +1 -1
  186. package/dist/extensions/llama/provider.d.ts +3 -1
  187. package/dist/extensions/llama/provider.d.ts.map +1 -1
  188. package/dist/extensions/llama/provider.js +29 -4
  189. package/dist/extensions/llama/provider.js.map +1 -1
  190. package/dist/index.d.ts +6 -5
  191. package/dist/index.d.ts.map +1 -1
  192. package/dist/index.js +5 -4
  193. package/dist/index.js.map +1 -1
  194. package/dist/main.d.ts +4 -0
  195. package/dist/main.d.ts.map +1 -1
  196. package/dist/main.js +20 -15
  197. package/dist/main.js.map +1 -1
  198. package/dist/migrations.d.ts.map +1 -1
  199. package/dist/migrations.js +4 -3
  200. package/dist/migrations.js.map +1 -1
  201. package/dist/modes/interactive/components/model-selector.d.ts +9 -3
  202. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  203. package/dist/modes/interactive/components/model-selector.js +56 -12
  204. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  205. package/dist/modes/interactive/components/settings-selector.d.ts +9 -2
  206. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  207. package/dist/modes/interactive/components/settings-selector.js +128 -56
  208. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  209. package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
  210. package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
  211. package/dist/modes/interactive/components/settings-submenu.js +165 -0
  212. package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
  213. package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
  214. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  215. package/dist/modes/interactive/components/thinking-selector.js +74 -17
  216. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  217. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  218. package/dist/modes/interactive/external-editor.js +2 -1
  219. package/dist/modes/interactive/external-editor.js.map +1 -1
  220. package/dist/modes/interactive/interactive-mode.d.ts +15 -0
  221. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  222. package/dist/modes/interactive/interactive-mode.js +223 -129
  223. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  224. package/dist/modes/interactive/session-share.d.ts +16 -0
  225. package/dist/modes/interactive/session-share.d.ts.map +1 -0
  226. package/dist/modes/interactive/session-share.js +188 -0
  227. package/dist/modes/interactive/session-share.js.map +1 -0
  228. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  229. package/dist/modes/interactive/theme/theme.js +4 -3
  230. package/dist/modes/interactive/theme/theme.js.map +1 -1
  231. package/dist/modes/json-event.d.ts +16 -13
  232. package/dist/modes/json-event.d.ts.map +1 -1
  233. package/dist/modes/json-event.js +20 -6
  234. package/dist/modes/json-event.js.map +1 -1
  235. package/dist/modes/rpc/rpc-client.d.ts +7 -0
  236. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  237. package/dist/modes/rpc/rpc-client.js +7 -0
  238. package/dist/modes/rpc/rpc-client.js.map +1 -1
  239. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  240. package/dist/modes/rpc/rpc-mode.js +3 -0
  241. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  242. package/dist/modes/rpc/rpc-types.d.ts +12 -0
  243. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  244. package/dist/modes/rpc/rpc-types.js.map +1 -1
  245. package/dist/package-manager-cli.d.ts +1 -0
  246. package/dist/package-manager-cli.d.ts.map +1 -1
  247. package/dist/package-manager-cli.js +186 -2
  248. package/dist/package-manager-cli.js.map +1 -1
  249. package/dist/utils/frontmatter.d.ts.map +1 -1
  250. package/dist/utils/frontmatter.js +2 -1
  251. package/dist/utils/frontmatter.js.map +1 -1
  252. package/dist/utils/management-http.d.ts +5 -3
  253. package/dist/utils/management-http.d.ts.map +1 -1
  254. package/dist/utils/management-http.js +14 -10
  255. package/dist/utils/management-http.js.map +1 -1
  256. package/dist/utils/shell.d.ts +3 -0
  257. package/dist/utils/shell.d.ts.map +1 -1
  258. package/dist/utils/shell.js +23 -9
  259. package/dist/utils/shell.js.map +1 -1
  260. package/dist/utils/syntax-highlight.d.ts +1 -0
  261. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  262. package/dist/utils/syntax-highlight.js +60 -1
  263. package/dist/utils/syntax-highlight.js.map +1 -1
  264. package/dist/utils/text.d.ts +8 -0
  265. package/dist/utils/text.d.ts.map +1 -0
  266. package/dist/utils/text.js +9 -0
  267. package/dist/utils/text.js.map +1 -0
  268. package/docs/compaction.md +17 -0
  269. package/docs/custom-provider.md +12 -9
  270. package/docs/environment-variables.md +11 -8
  271. package/docs/extensions.md +35 -7
  272. package/docs/json.md +7 -2
  273. package/docs/keybindings.md +9 -9
  274. package/docs/llama-cpp.md +2 -0
  275. package/docs/models.md +8 -2
  276. package/docs/packages.md +2 -2
  277. package/docs/quickstart.md +1 -1
  278. package/docs/rpc.md +32 -3
  279. package/docs/sdk.md +7 -2
  280. package/docs/settings.md +23 -7
  281. package/docs/skills.md +4 -3
  282. package/docs/terminal-setup.md +23 -9
  283. package/docs/usage.md +10 -5
  284. package/docs/windows.md +24 -2
  285. package/examples/extensions/border-status-editor.ts +1 -1
  286. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  287. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  288. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  289. package/examples/extensions/git-checkpoint.ts +2 -2
  290. package/examples/extensions/gondolin/package-lock.json +2 -2
  291. package/examples/extensions/gondolin/package.json +1 -1
  292. package/examples/extensions/notify.ts +3 -1
  293. package/examples/extensions/sandbox/package-lock.json +2 -2
  294. package/examples/extensions/sandbox/package.json +1 -1
  295. package/examples/extensions/subagent/README.md +1 -1
  296. package/examples/extensions/subagent/index.ts +6 -1
  297. package/examples/extensions/titlebar-spinner.ts +1 -1
  298. package/examples/extensions/with-deps/package-lock.json +2 -2
  299. package/examples/extensions/with-deps/package.json +1 -1
  300. package/examples/rpc-extension-ui.ts +1 -1
  301. package/examples/sdk/06-extensions.ts +1 -1
  302. package/examples/sdk/README.md +1 -1
  303. package/npm-shrinkwrap.json +24 -77
  304. package/package.json +12 -14
@@ -7,13 +7,14 @@ import * as fs from "node:fs";
7
7
  import * as os from "node:os";
8
8
  import * as path from "node:path";
9
9
  import * as TuiLayouts from "@earendil-works/pi-tui";
10
- import { CombinedAutocompleteProvider, Container, fuzzyFilter, getCapabilities, hyperlink, Markdown, matchesKey, ProcessTerminal, Spacer, setKeybindings, Text, TruncatedText, TuiAltScreen, TuiMainScreen, visibleWidth, } from "@earendil-works/pi-tui";
10
+ import { CombinedAutocompleteProvider, Container, fuzzyFilter, getCapabilities, hyperlink, Markdown, matchesKey, ProcessTerminal, Spacer, setCapabilityOverrides, setKeybindings, Text, TruncatedText, TuiAltScreen, TuiMainScreen, visibleWidth, } from "@earendil-works/pi-tui";
11
11
  import chalk from "chalk";
12
- import { spawn, spawnSync } from "child_process";
13
- import { APP_NAME, APP_TITLE, CONFIG_DIR_NAME, getAgentDir, getAuthPath, getDebugLogPath, getDocsPath, getShareViewerUrl, VERSION, } from "../../config.js";
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,6 +64,7 @@ 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";
@@ -71,6 +73,7 @@ import { UserMessageSelectorComponent } from "./components/user-message-selector
71
73
  import { editInExternalEditor } from "./external-editor.js";
72
74
  import { refreshModelCatalogs } from "./model-catalog-refresh.js";
73
75
  import { getModelSearchText } from "./model-search.js";
76
+ import { shareSession } from "./session-share.js";
74
77
  import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, onThemeChange, setRegisteredThemes, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
75
78
  import { InteractiveThemeController } from "./theme/theme-controller.js";
76
79
  function isExpandable(obj) {
@@ -91,6 +94,9 @@ class ExpandableText extends Text {
91
94
  function isCustomSessionEntry(item) {
92
95
  return "type" in item && item.type === "custom";
93
96
  }
97
+ function isCompactionCostNotice(item) {
98
+ return "type" in item && item.type === "compaction_cost";
99
+ }
94
100
  const DEAD_TERMINAL_ERROR_CODES = new Set(["EIO", "EPIPE", "ENOTCONN"]);
95
101
  function isDeadTerminalError(error) {
96
102
  if (!error || typeof error !== "object" || !("code" in error)) {
@@ -130,6 +136,11 @@ export function formatResumeCommand(sessionManager) {
130
136
  function hasDefaultModelProvider(providerId) {
131
137
  return providerId in defaultModelPerProvider;
132
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
+ }
133
144
  const AUTH_TYPE_ORDER = { oauth: 0, api_key: 1 };
134
145
  function createFuzzyAutocompleteItems(items, prefix, getSearchText, toAutocompleteItem) {
135
146
  const filtered = fuzzyFilter(items, prefix, getSearchText);
@@ -176,6 +187,7 @@ export function createInteractiveTui(options) {
176
187
  searchCurrentMatchStyle: (text) => theme.bold(theme.inverse(styleSearchMatch(text))),
177
188
  openUrl: openBrowser,
178
189
  onRightClickPaste: options.onRightClickPaste,
190
+ copyOnSelect: options.fullscreenCopyOnSelect,
179
191
  copySelection: async (text) => {
180
192
  try {
181
193
  await copyToClipboard(text);
@@ -339,6 +351,7 @@ export class InteractiveMode {
339
351
  }
340
352
  constructor(runtimeHost, options = {}) {
341
353
  this.runtimeHost = runtimeHost;
354
+ setCapabilityOverrides(this.settingsManager.getTerminalCapabilityOverrides());
342
355
  const tuiMode = options.tuiMode ?? this.settingsManager.getTuiMode();
343
356
  this.options = { ...options, tuiMode };
344
357
  this.autoTrustOnReloadCwd = options.autoTrustOnReloadCwd;
@@ -355,6 +368,7 @@ export class InteractiveMode {
355
368
  showHardwareCursor: this.settingsManager.getShowHardwareCursor(),
356
369
  logDirectory: getAgentDir(),
357
370
  onRightClickPaste: this.onRightClickPaste,
371
+ fullscreenCopyOnSelect: this.settingsManager.getFullscreenCopyOnSelect(),
358
372
  });
359
373
  this.ui = createInteractiveTuiReference(() => this.renderer);
360
374
  this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink());
@@ -465,6 +479,15 @@ export class InteractiveMode {
465
479
  }));
466
480
  };
467
481
  }
482
+ const thinkingCommand = slashCommands.find((command) => command.name === "thinking");
483
+ if (thinkingCommand) {
484
+ thinkingCommand.getArgumentCompletions = (prefix) => {
485
+ return createFuzzyAutocompleteItems(this.session.getAvailableThinkingLevels(), prefix, (level) => level, (level) => ({
486
+ value: level,
487
+ label: level,
488
+ }));
489
+ };
490
+ }
468
491
  const loginCommand = slashCommands.find((command) => command.name === "login");
469
492
  if (loginCommand) {
470
493
  loginCommand.getArgumentCompletions = (prefix) => {
@@ -593,6 +616,7 @@ export class InteractiveMode {
593
616
  logDirectory: getAgentDir(),
594
617
  terminal,
595
618
  onRightClickPaste: this.onRightClickPaste,
619
+ fullscreenCopyOnSelect: this.settingsManager.getFullscreenCopyOnSelect(),
596
620
  });
597
621
  nextUi.setClearOnShrink(clearOnShrink);
598
622
  nextUi.onDebug = onDebug;
@@ -749,6 +773,14 @@ export class InteractiveMode {
749
773
  });
750
774
  // Initialize available provider count for footer display
751
775
  await this.updateAvailableProviderCount();
776
+ // Flush the completed startup state before loading the remaining syntax grammars.
777
+ this.ui.renderNow();
778
+ void loadAllHighlightLanguages().then(() => {
779
+ if (!this.isInitialized)
780
+ return;
781
+ this.ui.invalidate();
782
+ this.ui.requestRender();
783
+ });
752
784
  }
753
785
  /**
754
786
  * Update terminal title with session name and cwd.
@@ -804,7 +836,18 @@ export class InteractiveMode {
804
836
  }
805
837
  });
806
838
  // Show startup warnings
807
- const { migratedProviders, modelFallbackMessage, initialMessage, initialImages, initialMessages } = this.options;
839
+ const { migratedProviders, startupDiagnostics, modelFallbackMessage, initialMessage, initialImages, initialMessages, } = this.options;
840
+ for (const diagnostic of startupDiagnostics ?? []) {
841
+ if (diagnostic.type === "error") {
842
+ this.showError(diagnostic.message);
843
+ }
844
+ else if (diagnostic.type === "warning") {
845
+ this.showWarning(diagnostic.message);
846
+ }
847
+ else {
848
+ this.showStatus(diagnostic.message);
849
+ }
850
+ }
808
851
  if (migratedProviders && migratedProviders.length > 0) {
809
852
  this.showWarning(`Migrated credentials to auth.json: ${migratedProviders.join(", ")}`);
810
853
  }
@@ -1482,8 +1525,12 @@ export class InteractiveMode {
1482
1525
  this.transcriptScrollView?.setScrollbar(this.settingsManager.getFullscreenScrollbar());
1483
1526
  }
1484
1527
  applyRuntimeSettings() {
1528
+ setCapabilityOverrides(this.settingsManager.getTerminalCapabilityOverrides());
1485
1529
  configureHttpDispatcher(this.settingsManager.getHttpIdleTimeoutMs());
1486
1530
  this.applyFullscreenScrollbarSetting();
1531
+ if (this.renderer instanceof TuiAltScreen) {
1532
+ this.renderer.setCopyOnSelect(this.settingsManager.getFullscreenCopyOnSelect());
1533
+ }
1487
1534
  this.footer.setSession(this.session);
1488
1535
  this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
1489
1536
  this.footerDataProvider.setCwd(this.sessionManager.getCwd());
@@ -1633,6 +1680,9 @@ export class InteractiveMode {
1633
1680
  this.statusContainer.addChild(this.idleStatus);
1634
1681
  }
1635
1682
  }
1683
+ showWorkingStatusIndicator() {
1684
+ this.showStatusIndicator(new WorkingStatusIndicator(this.ui, this.workingMessage ?? this.defaultWorkingMessage, this.workingIndicatorOptions));
1685
+ }
1636
1686
  setWorkingVisible(visible) {
1637
1687
  this.workingVisible = visible;
1638
1688
  if (!visible) {
@@ -1641,7 +1691,7 @@ export class InteractiveMode {
1641
1691
  return;
1642
1692
  }
1643
1693
  if (this.session.isStreaming && this.activeStatusIndicator?.kind !== "working") {
1644
- this.showStatusIndicator(new WorkingStatusIndicator(this.ui, this.workingMessage ?? this.defaultWorkingMessage, this.workingIndicatorOptions));
1694
+ this.showWorkingStatusIndicator();
1645
1695
  }
1646
1696
  this.ui.requestRender();
1647
1697
  }
@@ -2261,7 +2311,7 @@ export class InteractiveMode {
2261
2311
  this.defaultEditor.onAction("app.tools.expand", () => this.toggleToolOutputExpansion());
2262
2312
  this.defaultEditor.onAction("app.thinking.toggle", () => this.toggleThinkingBlockVisibility());
2263
2313
  this.defaultEditor.onAction("app.editor.external", () => void this.handleOpenExternalEditor());
2264
- this.defaultEditor.onAction("app.message.copy", () => void this.handleCopyCommand({ flashConfirmation: true }));
2314
+ this.defaultEditor.onAction("app.message.copy", () => void this.handleCopyCommand({ flashConfirmation: true, preferSelection: true }));
2265
2315
  this.defaultEditor.onAction("app.message.followUp", () => this.handleFollowUp());
2266
2316
  this.defaultEditor.onAction("app.message.dequeue", () => this.handleDequeue());
2267
2317
  this.defaultEditor.onAction("app.session.new", () => this.handleClearCommand());
@@ -2346,6 +2396,12 @@ export class InteractiveMode {
2346
2396
  await this.handleModelCommand(searchTerm);
2347
2397
  return;
2348
2398
  }
2399
+ if (text === "/thinking" || text.startsWith("/thinking ")) {
2400
+ const searchTerm = text.startsWith("/thinking ") ? text.slice(10).trim() : undefined;
2401
+ this.editor.setText("");
2402
+ this.handleThinkingCommand(searchTerm);
2403
+ return;
2404
+ }
2349
2405
  if (text === "/export" || text.startsWith("/export ")) {
2350
2406
  await this.handleExportCommand(text);
2351
2407
  this.editor.setText("");
@@ -2522,17 +2578,21 @@ export class InteractiveMode {
2522
2578
  switch (event.type) {
2523
2579
  case "agent_start":
2524
2580
  this.pendingTools.clear();
2525
- if (this.settingsManager.getShowTerminalProgress()) {
2526
- this.ui.terminal.setProgress(true);
2527
- }
2528
2581
  // Restore main escape handler if retry handler is still active
2529
2582
  // (retry success event fires later, but we need main handler now)
2530
2583
  if (this.retryEscapeHandler) {
2531
2584
  this.defaultEditor.onEscape = this.retryEscapeHandler;
2532
2585
  this.retryEscapeHandler = undefined;
2533
2586
  }
2587
+ break;
2588
+ case "turn_start":
2589
+ if (this.settingsManager.getShowTerminalProgress()) {
2590
+ this.ui.terminal.setProgress(true);
2591
+ }
2534
2592
  if (this.workingVisible) {
2535
- this.showStatusIndicator(new WorkingStatusIndicator(this.ui, this.workingMessage ?? this.defaultWorkingMessage, this.workingIndicatorOptions));
2593
+ if (this.activeStatusIndicator?.kind !== "working") {
2594
+ this.showWorkingStatusIndicator();
2595
+ }
2536
2596
  }
2537
2597
  else {
2538
2598
  this.clearStatusIndicator();
@@ -2724,9 +2784,21 @@ export class InteractiveMode {
2724
2784
  }
2725
2785
  }
2726
2786
  else if (event.result) {
2787
+ const entries = this.sessionManager.buildContextEntries();
2788
+ if (entries[0]?.type !== "compaction") {
2789
+ throw new Error("Completed compaction is missing from the session context");
2790
+ }
2727
2791
  this.chatContainer.clear();
2728
- this.rebuildChatFromMessages();
2792
+ // The latest compaction is prepended for model context; append it below at its chronological position.
2793
+ this.renderSessionEntries(entries.slice(1));
2729
2794
  this.addMessageToChat(createCompactionSummaryMessage(event.result.summary, event.result.tokensBefore, new Date().toISOString()));
2795
+ if (event.result.usage) {
2796
+ this.addCompactionCostNotice({
2797
+ type: "compaction_cost",
2798
+ kind: "compaction",
2799
+ usage: event.result.usage,
2800
+ });
2801
+ }
2730
2802
  this.footer.invalidate();
2731
2803
  }
2732
2804
  else if (event.errorMessage) {
@@ -2948,6 +3020,10 @@ export class InteractiveMode {
2948
3020
  this.addCustomEntryToChat(item);
2949
3021
  continue;
2950
3022
  }
3023
+ if (isCompactionCostNotice(item)) {
3024
+ this.addCompactionCostNotice(item);
3025
+ continue;
3026
+ }
2951
3027
  const message = item;
2952
3028
  // Assistant messages need special handling for tool calls
2953
3029
  if (message.role === "assistant") {
@@ -3015,10 +3091,28 @@ export class InteractiveMode {
3015
3091
  if (entry.type === "custom") {
3016
3092
  return [entry];
3017
3093
  }
3018
- return sessionEntryToContextMessages(entry);
3094
+ const messages = sessionEntryToContextMessages(entry);
3095
+ if ((entry.type === "compaction" || entry.type === "branch_summary") && entry.usage && messages.length > 0) {
3096
+ return [...messages, { type: "compaction_cost", kind: entry.type, usage: entry.usage }];
3097
+ }
3098
+ return messages;
3019
3099
  });
3020
3100
  this.renderSessionItems(items, options);
3021
3101
  }
3102
+ /**
3103
+ * Render billing usage for a compaction or branch summary. The notice is derived
3104
+ * from persisted summary usage and is not stored as a separate session entry.
3105
+ */
3106
+ addCompactionCostNotice(notice) {
3107
+ if (!this.settingsManager.getShowCacheMissNotices())
3108
+ return;
3109
+ const { usage } = notice;
3110
+ const tokens = usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
3111
+ const cost = usage.cost.total >= 0.01 ? ` (~$${usage.cost.total.toFixed(2)})` : "";
3112
+ const label = notice.kind === "compaction" ? "Compaction" : "Branch summary";
3113
+ this.chatContainer.addChild(new Spacer(1));
3114
+ this.chatContainer.addChild(new Text(theme.fg("warning", `${label}: ${formatTokens(tokens)} tokens billed${cost}`), 1, 0));
3115
+ }
3022
3116
  /**
3023
3117
  * Show a transcript notice when a completed assistant message paid for a
3024
3118
  * significant cache miss. Only states observable facts: the miss itself,
@@ -3368,18 +3462,19 @@ export class InteractiveMode {
3368
3462
  }
3369
3463
  this.showStatus(`Tool output: ${expanded ? "expanded" : "collapsed"}`);
3370
3464
  }
3465
+ /** Update rendered assistant messages without rebuilding live tool components. */
3466
+ updateThinkingBlockVisibility() {
3467
+ for (const child of this.chatContainer.children) {
3468
+ if (child instanceof AssistantMessageComponent) {
3469
+ child.setHideThinkingBlock(this.hideThinkingBlock);
3470
+ }
3471
+ }
3472
+ this.ui.requestRender();
3473
+ }
3371
3474
  toggleThinkingBlockVisibility() {
3372
3475
  this.hideThinkingBlock = !this.hideThinkingBlock;
3373
3476
  this.settingsManager.setHideThinkingBlock(this.hideThinkingBlock);
3374
- // Rebuild chat from session messages
3375
- this.chatContainer.clear();
3376
- this.rebuildChatFromMessages();
3377
- // If streaming, re-add the streaming component with updated visibility and re-render
3378
- if (this.streamingComponent && this.streamingMessage) {
3379
- this.streamingComponent.setHideThinkingBlock(this.hideThinkingBlock);
3380
- this.streamingComponent.updateContent(this.streamingMessage);
3381
- this.chatContainer.addChild(this.streamingComponent);
3382
- }
3477
+ this.updateThinkingBlockVisibility();
3383
3478
  this.showStatus(`Thinking blocks: ${this.hideThinkingBlock ? "hidden" : "visible"}`);
3384
3479
  }
3385
3480
  async handleOpenExternalEditor() {
@@ -3655,8 +3750,14 @@ export class InteractiveMode {
3655
3750
  showSettingsSelector() {
3656
3751
  this.showSelector((done) => {
3657
3752
  let selector;
3753
+ const defaultProvider = this.settingsManager.getDefaultProvider();
3754
+ const defaultModelId = this.settingsManager.getDefaultModel();
3755
+ const defaultModel = defaultProvider && defaultModelId ? `${defaultProvider}/${defaultModelId}` : "not set";
3658
3756
  selector = new SettingsSelectorComponent({
3659
3757
  autoCompact: this.session.autoCompactionEnabled,
3758
+ defaultModel,
3759
+ currentModel: this.session.model,
3760
+ availableDefaultModels: this.session.modelRuntime.getAvailableSnapshot(),
3660
3761
  showImages: this.settingsManager.getShowImages(),
3661
3762
  imageWidthCells: this.settingsManager.getImageWidthCells(),
3662
3763
  autoResizeImages: this.settingsManager.getImageAutoResize(),
@@ -3666,8 +3767,9 @@ export class InteractiveMode {
3666
3767
  followUpMode: this.session.followUpMode,
3667
3768
  transport: this.settingsManager.getTransport(),
3668
3769
  httpIdleTimeoutMs: this.settingsManager.getHttpIdleTimeoutMs(),
3669
- thinkingLevel: this.session.thinkingLevel,
3670
- availableThinkingLevels: this.session.getAvailableThinkingLevels(),
3770
+ thinkingLevel: this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL,
3771
+ availableThinkingLevels: [...THINKING_LEVEL_OPTIONS],
3772
+ modelThinkingLevels: this.settingsManager.getAllModelThinkingLevels(),
3671
3773
  currentTheme: this.themeController.getThemeSelection() || "dark",
3672
3774
  terminalTheme: this.themeController.getTerminalTheme(),
3673
3775
  availableThemes: getAvailableThemes(),
@@ -3689,6 +3791,7 @@ export class InteractiveMode {
3689
3791
  tuiMode: this.ui.mode,
3690
3792
  fullscreenExitOutput: this.settingsManager.getFullscreenExitOutput(),
3691
3793
  fullscreenScrollbar: this.settingsManager.getFullscreenScrollbar(),
3794
+ fullscreenCopyOnSelect: this.settingsManager.getFullscreenCopyOnSelect(),
3692
3795
  warnings: this.settingsManager.getWarnings(),
3693
3796
  }, {
3694
3797
  onAutoCompactChange: (enabled) => {
@@ -3736,10 +3839,26 @@ export class InteractiveMode {
3736
3839
  configureHttpDispatcher(timeoutMs);
3737
3840
  this.showStatus(`HTTP idle timeout: ${formatHttpIdleTimeoutMs(timeoutMs)}`);
3738
3841
  },
3739
- onThinkingLevelChange: (level) => {
3740
- this.session.setThinkingLevel(level);
3741
- this.footer.invalidate();
3742
- this.updateEditorBorderColor();
3842
+ onModelThinkingLevelChange: (provider, modelId, level) => {
3843
+ this.settingsManager.setModelThinkingLevel(provider, modelId, level);
3844
+ // If the override is for the current model, apply it to the session too
3845
+ const current = this.session.model;
3846
+ if (current && current.provider === provider && current.id === modelId) {
3847
+ this.session.setThinkingLevel(level);
3848
+ this.footer.invalidate();
3849
+ this.updateEditorBorderColor();
3850
+ }
3851
+ },
3852
+ onModelThinkingLevelRemove: (provider, modelId) => {
3853
+ this.settingsManager.removeModelThinkingLevel(provider, modelId);
3854
+ // If the override was for the current model, revert to global default
3855
+ const current = this.session.model;
3856
+ if (current && current.provider === provider && current.id === modelId) {
3857
+ const globalDefault = this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
3858
+ this.session.setThinkingLevel(globalDefault);
3859
+ this.footer.invalidate();
3860
+ this.updateEditorBorderColor();
3861
+ }
3743
3862
  },
3744
3863
  onThemeChange: (themeSetting) => {
3745
3864
  this.settingsManager.setTheme(themeSetting);
@@ -3749,13 +3868,7 @@ export class InteractiveMode {
3749
3868
  onHideThinkingBlockChange: (hidden) => {
3750
3869
  this.hideThinkingBlock = hidden;
3751
3870
  this.settingsManager.setHideThinkingBlock(hidden);
3752
- for (const child of this.chatContainer.children) {
3753
- if (child instanceof AssistantMessageComponent) {
3754
- child.setHideThinkingBlock(hidden);
3755
- }
3756
- }
3757
- this.chatContainer.clear();
3758
- this.rebuildChatFromMessages();
3871
+ this.updateThinkingBlockVisibility();
3759
3872
  },
3760
3873
  onMermaidRenderingModeChange: (mode) => {
3761
3874
  this.settingsManager.setMermaidRenderingMode(mode);
@@ -3849,6 +3962,11 @@ export class InteractiveMode {
3849
3962
  this.settingsManager.setFullscreenScrollbar(mode);
3850
3963
  this.applyFullscreenScrollbarSetting();
3851
3964
  },
3965
+ onFullscreenCopyOnSelectChange: (enabled) => {
3966
+ this.settingsManager.setFullscreenCopyOnSelect(enabled);
3967
+ if (this.renderer instanceof TuiAltScreen)
3968
+ this.renderer.setCopyOnSelect(enabled);
3969
+ },
3852
3970
  onWarningsChange: (warnings) => {
3853
3971
  this.settingsManager.setWarnings(warnings);
3854
3972
  },
@@ -3860,6 +3978,44 @@ export class InteractiveMode {
3860
3978
  return { component: selector, focus: selector.getSettingsList() };
3861
3979
  });
3862
3980
  }
3981
+ handleThinkingCommand(searchTerm) {
3982
+ const availableLevels = this.session.getAvailableThinkingLevels();
3983
+ if (!searchTerm) {
3984
+ this.showThinkingSelector();
3985
+ return;
3986
+ }
3987
+ const normalized = searchTerm.trim().toLowerCase();
3988
+ const level = availableLevels.find((candidate) => candidate.toLowerCase() === normalized);
3989
+ if (!level) {
3990
+ this.showError(`Unknown thinking level "${searchTerm}". Available levels: ${availableLevels.join(", ")}.`);
3991
+ return;
3992
+ }
3993
+ this.selectThinkingLevel(level, false);
3994
+ }
3995
+ selectThinkingLevel(level, persist) {
3996
+ try {
3997
+ this.session.setThinkingLevel(level, { persist });
3998
+ this.footer.invalidate();
3999
+ this.updateEditorBorderColor();
4000
+ this.showStatus(persist ? `Default thinking level: ${level}` : `Thinking level: ${level}`);
4001
+ }
4002
+ catch (error) {
4003
+ this.showError(error instanceof Error ? error.message : String(error));
4004
+ }
4005
+ }
4006
+ showThinkingSelector() {
4007
+ this.showSelector((done) => {
4008
+ const selectLevel = (level, persist) => {
4009
+ this.selectThinkingLevel(level, persist);
4010
+ done();
4011
+ };
4012
+ const selector = new ThinkingSelectorComponent(this.session.thinkingLevel ?? DEFAULT_THINKING_LEVEL, this.session.getAvailableThinkingLevels(), (level) => selectLevel(level, false), () => {
4013
+ done();
4014
+ this.ui.requestRender();
4015
+ }, (level) => selectLevel(level, true), this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);
4016
+ return { component: selector, focus: selector };
4017
+ });
4018
+ }
3863
4019
  async handleModelCommand(searchTerm) {
3864
4020
  if (!searchTerm) {
3865
4021
  this.showModelSelector();
@@ -3868,7 +4024,7 @@ export class InteractiveMode {
3868
4024
  const model = await this.findExactModelMatch(searchTerm);
3869
4025
  if (model) {
3870
4026
  try {
3871
- await this.session.setModel(model);
4027
+ await this.session.setModel(model, { persist: false });
3872
4028
  this.footer.invalidate();
3873
4029
  this.updateEditorBorderColor();
3874
4030
  this.showStatus(`Model: ${model.id}`);
@@ -3997,13 +4153,14 @@ export class InteractiveMode {
3997
4153
  }
3998
4154
  showModelSelector(initialSearchInput) {
3999
4155
  this.showSelector((done) => {
4000
- const selector = new ModelSelectorComponent(this.ui, this.session.model, this.settingsManager, this.session.modelRuntime, this.session.scopedModels, async (model) => {
4156
+ const selectModel = async (model, persist) => {
4001
4157
  try {
4002
- await this.session.setModel(model);
4158
+ await this.session.setModel(model, { persist });
4159
+ this.updateAvailableProviderCount();
4003
4160
  this.footer.invalidate();
4004
4161
  this.updateEditorBorderColor();
4005
4162
  done();
4006
- this.showStatus(`Model: ${model.id}`);
4163
+ this.showStatus(persist ? `Default model: ${model.provider}/${model.id}` : `Model: ${model.id}`);
4007
4164
  void this.maybeWarnAboutAnthropicSubscriptionAuth(model);
4008
4165
  this.checkDaxnutsEasterEgg(model);
4009
4166
  }
@@ -4011,10 +4168,13 @@ export class InteractiveMode {
4011
4168
  done();
4012
4169
  this.showError(error instanceof Error ? error.message : String(error));
4013
4170
  }
4014
- }, () => {
4171
+ };
4172
+ const defaultProvider = this.settingsManager.getDefaultProvider();
4173
+ const defaultModel = this.settingsManager.getDefaultModel();
4174
+ const selector = new ModelSelectorComponent(this.ui, this.session.model, this.session.modelRuntime, this.session.scopedModels, (model) => selectModel(model, false), () => {
4015
4175
  done();
4016
4176
  this.ui.requestRender();
4017
- }, initialSearchInput);
4177
+ }, initialSearchInput, (model) => selectModel(model, true), defaultProvider && defaultModel ? { provider: defaultProvider, id: defaultModel } : undefined);
4018
4178
  return { component: selector, focus: selector, dispose: () => selector.dispose() };
4019
4179
  });
4020
4180
  }
@@ -4570,7 +4730,11 @@ export class InteractiveMode {
4570
4730
  if (isUnknownModel(previousModel)) {
4571
4731
  const availableModels = this.session.modelRuntime.getAvailableSnapshot();
4572
4732
  const providerModels = availableModels.filter((model) => model.provider === providerId);
4573
- if (!hasDefaultModelProvider(providerId)) {
4733
+ // Matches LLAMA_PROVIDER_ID from extensions/llama/provider.ts; kept inline to avoid coupling interactive mode to the built-in extension.
4734
+ if (providerId === "llama.cpp") {
4735
+ selectionError = llamaCppPostLoginGuidance(actionLabel, providerModels.length);
4736
+ }
4737
+ else if (!hasDefaultModelProvider(providerId)) {
4574
4738
  selectionError = `${actionLabel}, but no default model is configured for provider "${providerId}". Use /model to select a model.`;
4575
4739
  }
4576
4740
  else if (providerModels.length === 0) {
@@ -4584,7 +4748,7 @@ export class InteractiveMode {
4584
4748
  }
4585
4749
  else {
4586
4750
  try {
4587
- await this.session.setModel(selectedModel);
4751
+ await this.session.setModel(selectedModel, { persist: true });
4588
4752
  }
4589
4753
  catch (error) {
4590
4754
  selectedModel = undefined;
@@ -4841,8 +5005,8 @@ export class InteractiveMode {
4841
5005
  activeHeader.setExpanded(this.toolOutputExpanded);
4842
5006
  }
4843
5007
  setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
4844
- await this.themeController.applyFromSettings();
4845
5008
  this.applyRuntimeSettings();
5009
+ await this.themeController.applyFromSettings();
4846
5010
  this.setupAutocompleteProvider();
4847
5011
  const runner = this.session.extensionRunner;
4848
5012
  this.setupExtensionShortcuts(runner);
@@ -4954,93 +5118,23 @@ export class InteractiveMode {
4954
5118
  }
4955
5119
  }
4956
5120
  async handleShareCommand() {
4957
- // Check if gh is available and logged in
4958
- try {
4959
- const authResult = spawnSync("gh", ["auth", "status"], { encoding: "utf-8" });
4960
- if (authResult.status !== 0) {
4961
- this.showError("GitHub CLI is not logged in. Run 'gh auth login' first.");
4962
- return;
4963
- }
4964
- }
4965
- catch {
4966
- this.showError("GitHub CLI (gh) is not installed. Install it from https://cli.github.com/");
4967
- return;
4968
- }
4969
- // Export to a temp file
4970
- const tmpFile = path.join(os.tmpdir(), "session.html");
4971
- try {
4972
- await this.session.exportToHtml(tmpFile, { themeName: theme.name });
4973
- }
4974
- catch (error) {
4975
- this.showError(`Failed to export session: ${error instanceof Error ? error.message : "Unknown error"}`);
4976
- return;
4977
- }
4978
- // Show cancellable loader, replacing the editor
4979
- const loader = new BorderedLoader(this.ui, theme, "Creating gist...");
4980
- this.editorContainer.clear();
4981
- this.editorContainer.addChild(loader);
4982
- this.ui.setFocus(loader);
4983
- this.ui.requestRender();
4984
- const restoreEditor = () => {
4985
- loader.dispose();
4986
- this.editorContainer.clear();
4987
- this.editorContainer.addChild(this.editor);
4988
- this.ui.setFocus(this.editor);
4989
- try {
4990
- fs.unlinkSync(tmpFile);
4991
- }
4992
- catch {
4993
- // Ignore cleanup errors
4994
- }
4995
- };
4996
- // Create a secret gist asynchronously
4997
- let proc = null;
4998
- loader.onAbort = () => {
4999
- proc?.kill();
5000
- restoreEditor();
5001
- this.showStatus("Share cancelled");
5002
- };
5003
- try {
5004
- const result = await new Promise((resolve) => {
5005
- proc = spawn("gh", ["gist", "create", "--public=false", tmpFile]);
5006
- let stdout = "";
5007
- let stderr = "";
5008
- proc.stdout?.on("data", (data) => {
5009
- stdout += data.toString();
5010
- });
5011
- proc.stderr?.on("data", (data) => {
5012
- stderr += data.toString();
5013
- });
5014
- proc.on("close", (code) => resolve({ stdout, stderr, code }));
5015
- });
5016
- if (loader.signal.aborted)
5017
- return;
5018
- restoreEditor();
5019
- if (result.code !== 0) {
5020
- const errorMsg = result.stderr?.trim() || "Unknown error";
5021
- this.showError(`Failed to create gist: ${errorMsg}`);
5022
- return;
5023
- }
5024
- // Extract gist ID from the URL returned by gh
5025
- // gh returns something like: https://gist.github.com/username/GIST_ID
5026
- const gistUrl = result.stdout?.trim();
5027
- const gistId = gistUrl?.split("/").pop();
5028
- if (!gistId) {
5029
- this.showError("Failed to parse gist ID from gh output");
5030
- return;
5031
- }
5032
- // Create the preview URL
5033
- const previewUrl = getShareViewerUrl(gistId);
5034
- this.showStatus(`Share URL: ${previewUrl}\nGist: ${gistUrl}`);
5035
- }
5036
- catch (error) {
5037
- if (!loader.signal.aborted) {
5038
- restoreEditor();
5039
- this.showError(`Failed to create gist: ${error instanceof Error ? error.message : "Unknown error"}`);
5040
- }
5041
- }
5121
+ await shareSession({
5122
+ session: this.session,
5123
+ ui: this.ui,
5124
+ editorContainer: this.editorContainer,
5125
+ editor: this.editor,
5126
+ showStatus: (message) => this.showStatus(message),
5127
+ showError: (message) => this.showError(message),
5128
+ });
5042
5129
  }
5043
5130
  async handleCopyCommand(options = {}) {
5131
+ if (options.preferSelection &&
5132
+ this.ui instanceof TuiAltScreen &&
5133
+ !this.ui.getCopyOnSelect() &&
5134
+ this.ui.hasActiveSelection()) {
5135
+ await this.ui.copyActiveSelectionToClipboard();
5136
+ return;
5137
+ }
5044
5138
  const text = this.session.getLastAssistantText();
5045
5139
  if (!text) {
5046
5140
  this.showError("No agent messages to copy yet.");