@averyyy/pi-coding-agent 0.84.2-piclient.3 → 0.84.4-piclient.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) 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-B2RCCN5N.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-AP7VCR6I.js +1633 -0
  12. package/dist/bundle/chunks/chunk-AXIIZGTV.js +2 -0
  13. package/dist/bundle/chunks/chunk-DRQDKNXA.js +2 -0
  14. package/dist/bundle/chunks/chunk-GMWCTUPB.js +2 -0
  15. package/dist/bundle/chunks/chunk-JUUC5AZY.js +2 -0
  16. package/dist/bundle/chunks/chunk-L2ZPNLYD.js +2 -0
  17. package/dist/bundle/chunks/chunk-LRBSW3NQ.js +11 -0
  18. package/dist/bundle/chunks/chunk-MNAIPA3J.js +2 -0
  19. package/dist/bundle/chunks/chunk-NUHFSC37.js +25 -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-MJHX7NXI.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-CP4VKB27.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 +27 -9
  66. package/dist/core/agent-session.d.ts.map +1 -1
  67. package/dist/core/agent-session.js +150 -58
  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 +8 -1
  74. package/dist/core/compaction/branch-summarization.js.map +1 -1
  75. package/dist/core/compaction/compaction.d.ts +9 -4
  76. package/dist/core/compaction/compaction.d.ts.map +1 -1
  77. package/dist/core/compaction/compaction.js +42 -22
  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/exec.d.ts.map +1 -1
  84. package/dist/core/exec.js +3 -1
  85. package/dist/core/exec.js.map +1 -1
  86. package/dist/core/extensions/index.d.ts +2 -2
  87. package/dist/core/extensions/index.d.ts.map +1 -1
  88. package/dist/core/extensions/index.js +1 -1
  89. package/dist/core/extensions/index.js.map +1 -1
  90. package/dist/core/extensions/loader.d.ts.map +1 -1
  91. package/dist/core/extensions/loader.js +115 -48
  92. package/dist/core/extensions/loader.js.map +1 -1
  93. package/dist/core/extensions/runner.d.ts +5 -0
  94. package/dist/core/extensions/runner.d.ts.map +1 -1
  95. package/dist/core/extensions/runner.js +45 -1
  96. package/dist/core/extensions/runner.js.map +1 -1
  97. package/dist/core/extensions/types.d.ts +56 -10
  98. package/dist/core/extensions/types.d.ts.map +1 -1
  99. package/dist/core/extensions/types.js +3 -0
  100. package/dist/core/extensions/types.js.map +1 -1
  101. package/dist/core/footer-data-provider.d.ts +1 -1
  102. package/dist/core/footer-data-provider.d.ts.map +1 -1
  103. package/dist/core/footer-data-provider.js +1 -1
  104. package/dist/core/footer-data-provider.js.map +1 -1
  105. package/dist/core/index.d.ts +1 -0
  106. package/dist/core/index.d.ts.map +1 -1
  107. package/dist/core/index.js +1 -0
  108. package/dist/core/index.js.map +1 -1
  109. package/dist/core/keybindings.d.ts +20 -19
  110. package/dist/core/keybindings.d.ts.map +1 -1
  111. package/dist/core/keybindings.js +26 -5
  112. package/dist/core/keybindings.js.map +1 -1
  113. package/dist/core/model-config.d.ts +5 -0
  114. package/dist/core/model-config.d.ts.map +1 -1
  115. package/dist/core/model-config.js +3 -1
  116. package/dist/core/model-config.js.map +1 -1
  117. package/dist/core/model-resolver.d.ts +1 -0
  118. package/dist/core/model-resolver.d.ts.map +1 -1
  119. package/dist/core/model-resolver.js +14 -8
  120. package/dist/core/model-resolver.js.map +1 -1
  121. package/dist/core/models-store.d.ts.map +1 -1
  122. package/dist/core/models-store.js +2 -1
  123. package/dist/core/models-store.js.map +1 -1
  124. package/dist/core/package-manager.d.ts.map +1 -1
  125. package/dist/core/package-manager.js +22 -14
  126. package/dist/core/package-manager.js.map +1 -1
  127. package/dist/core/pi-client-cli-adapter.d.ts +11 -0
  128. package/dist/core/pi-client-cli-adapter.d.ts.map +1 -0
  129. package/dist/core/pi-client-cli-adapter.js +91 -0
  130. package/dist/core/pi-client-cli-adapter.js.map +1 -0
  131. package/dist/core/pi-manifest.d.ts.map +1 -1
  132. package/dist/core/pi-manifest.js +2 -1
  133. package/dist/core/pi-manifest.js.map +1 -1
  134. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  135. package/dist/core/remote-catalog-provider.js +2 -1
  136. package/dist/core/remote-catalog-provider.js.map +1 -1
  137. package/dist/core/resource-loader.d.ts.map +1 -1
  138. package/dist/core/resource-loader.js +3 -2
  139. package/dist/core/resource-loader.js.map +1 -1
  140. package/dist/core/sdk.d.ts +2 -2
  141. package/dist/core/sdk.d.ts.map +1 -1
  142. package/dist/core/sdk.js +10 -3
  143. package/dist/core/sdk.js.map +1 -1
  144. package/dist/core/session-export.d.ts +4 -0
  145. package/dist/core/session-export.d.ts.map +1 -0
  146. package/dist/core/session-export.js +32 -0
  147. package/dist/core/session-export.js.map +1 -0
  148. package/dist/core/session-manager.d.ts.map +1 -1
  149. package/dist/core/session-manager.js +6 -3
  150. package/dist/core/session-manager.js.map +1 -1
  151. package/dist/core/settings-diagnostics.d.ts +9 -0
  152. package/dist/core/settings-diagnostics.d.ts.map +1 -0
  153. package/dist/core/settings-diagnostics.js +21 -0
  154. package/dist/core/settings-diagnostics.js.map +1 -0
  155. package/dist/core/settings-manager.d.ts +17 -1
  156. package/dist/core/settings-manager.d.ts.map +1 -1
  157. package/dist/core/settings-manager.js +69 -10
  158. package/dist/core/settings-manager.js.map +1 -1
  159. package/dist/core/skills.d.ts.map +1 -1
  160. package/dist/core/skills.js +51 -33
  161. package/dist/core/skills.js.map +1 -1
  162. package/dist/core/slash-commands.d.ts.map +1 -1
  163. package/dist/core/slash-commands.js +2 -1
  164. package/dist/core/slash-commands.js.map +1 -1
  165. package/dist/core/system-prompt.d.ts.map +1 -1
  166. package/dist/core/system-prompt.js +11 -2
  167. package/dist/core/system-prompt.js.map +1 -1
  168. package/dist/core/tools/bash.d.ts +13 -1
  169. package/dist/core/tools/bash.d.ts.map +1 -1
  170. package/dist/core/tools/bash.js +30 -14
  171. package/dist/core/tools/bash.js.map +1 -1
  172. package/dist/core/tools/edit-diff.d.ts +0 -5
  173. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  174. package/dist/core/tools/edit-diff.js +2 -5
  175. package/dist/core/tools/edit-diff.js.map +1 -1
  176. package/dist/core/tools/edit.d.ts.map +1 -1
  177. package/dist/core/tools/edit.js +20 -4
  178. package/dist/core/tools/edit.js.map +1 -1
  179. package/dist/core/tools/index.d.ts +4 -1
  180. package/dist/core/tools/index.d.ts.map +1 -1
  181. package/dist/core/tools/index.js +18 -1
  182. package/dist/core/tools/index.js.map +1 -1
  183. package/dist/core/tools/powershell.d.ts +16 -0
  184. package/dist/core/tools/powershell.d.ts.map +1 -0
  185. package/dist/core/tools/powershell.js +39 -0
  186. package/dist/core/tools/powershell.js.map +1 -0
  187. package/dist/core/trust-manager.d.ts.map +1 -1
  188. package/dist/core/trust-manager.js +2 -1
  189. package/dist/core/trust-manager.js.map +1 -1
  190. package/dist/extensions/llama/client.d.ts +6 -0
  191. package/dist/extensions/llama/client.d.ts.map +1 -1
  192. package/dist/extensions/llama/client.js +7 -0
  193. package/dist/extensions/llama/client.js.map +1 -1
  194. package/dist/extensions/llama/index.d.ts.map +1 -1
  195. package/dist/extensions/llama/index.js +2 -0
  196. package/dist/extensions/llama/index.js.map +1 -1
  197. package/dist/extensions/llama/provider.d.ts +3 -1
  198. package/dist/extensions/llama/provider.d.ts.map +1 -1
  199. package/dist/extensions/llama/provider.js +29 -4
  200. package/dist/extensions/llama/provider.js.map +1 -1
  201. package/dist/index.d.ts +4 -3
  202. package/dist/index.d.ts.map +1 -1
  203. package/dist/index.js +4 -3
  204. package/dist/index.js.map +1 -1
  205. package/dist/main.d.ts +4 -0
  206. package/dist/main.d.ts.map +1 -1
  207. package/dist/main.js +22 -15
  208. package/dist/main.js.map +1 -1
  209. package/dist/migrations.d.ts.map +1 -1
  210. package/dist/migrations.js +4 -3
  211. package/dist/migrations.js.map +1 -1
  212. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  213. package/dist/modes/interactive/components/bash-execution.js +2 -1
  214. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  215. package/dist/modes/interactive/components/model-selector.d.ts +9 -3
  216. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  217. package/dist/modes/interactive/components/model-selector.js +56 -12
  218. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  219. package/dist/modes/interactive/components/settings-selector.d.ts +9 -2
  220. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  221. package/dist/modes/interactive/components/settings-selector.js +128 -56
  222. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  223. package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
  224. package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
  225. package/dist/modes/interactive/components/settings-submenu.js +165 -0
  226. package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
  227. package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
  228. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  229. package/dist/modes/interactive/components/thinking-selector.js +74 -17
  230. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  231. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  232. package/dist/modes/interactive/external-editor.js +2 -1
  233. package/dist/modes/interactive/external-editor.js.map +1 -1
  234. package/dist/modes/interactive/interactive-mode.d.ts +17 -12
  235. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  236. package/dist/modes/interactive/interactive-mode.js +244 -250
  237. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  238. package/dist/modes/interactive/session-share.d.ts +16 -0
  239. package/dist/modes/interactive/session-share.d.ts.map +1 -0
  240. package/dist/modes/interactive/session-share.js +188 -0
  241. package/dist/modes/interactive/session-share.js.map +1 -0
  242. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  243. package/dist/modes/interactive/theme/theme.js +4 -3
  244. package/dist/modes/interactive/theme/theme.js.map +1 -1
  245. package/dist/modes/json-event.d.ts +16 -13
  246. package/dist/modes/json-event.d.ts.map +1 -1
  247. package/dist/modes/json-event.js +20 -6
  248. package/dist/modes/json-event.js.map +1 -1
  249. package/dist/modes/rpc/rpc-client.d.ts +7 -0
  250. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  251. package/dist/modes/rpc/rpc-client.js +7 -0
  252. package/dist/modes/rpc/rpc-client.js.map +1 -1
  253. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  254. package/dist/modes/rpc/rpc-mode.js +3 -0
  255. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  256. package/dist/modes/rpc/rpc-types.d.ts +12 -0
  257. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  258. package/dist/modes/rpc/rpc-types.js.map +1 -1
  259. package/dist/package-manager-cli.d.ts +1 -0
  260. package/dist/package-manager-cli.d.ts.map +1 -1
  261. package/dist/package-manager-cli.js +186 -2
  262. package/dist/package-manager-cli.js.map +1 -1
  263. package/dist/utils/child-process.d.ts.map +1 -1
  264. package/dist/utils/child-process.js +8 -3
  265. package/dist/utils/child-process.js.map +1 -1
  266. package/dist/utils/frontmatter.d.ts.map +1 -1
  267. package/dist/utils/frontmatter.js +2 -1
  268. package/dist/utils/frontmatter.js.map +1 -1
  269. package/dist/utils/management-http.d.ts +5 -3
  270. package/dist/utils/management-http.d.ts.map +1 -1
  271. package/dist/utils/management-http.js +14 -10
  272. package/dist/utils/management-http.js.map +1 -1
  273. package/dist/utils/shell.d.ts +3 -0
  274. package/dist/utils/shell.d.ts.map +1 -1
  275. package/dist/utils/shell.js +27 -11
  276. package/dist/utils/shell.js.map +1 -1
  277. package/dist/utils/syntax-highlight.d.ts +1 -0
  278. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  279. package/dist/utils/syntax-highlight.js +60 -1
  280. package/dist/utils/syntax-highlight.js.map +1 -1
  281. package/dist/utils/text.d.ts +8 -0
  282. package/dist/utils/text.d.ts.map +1 -0
  283. package/dist/utils/text.js +9 -0
  284. package/dist/utils/text.js.map +1 -0
  285. package/dist/utils/version-check.d.ts.map +1 -1
  286. package/dist/utils/version-check.js +2 -1
  287. package/dist/utils/version-check.js.map +1 -1
  288. package/docs/compaction.md +17 -0
  289. package/docs/custom-provider.md +12 -9
  290. package/docs/environment-variables.md +11 -8
  291. package/docs/extensions.md +35 -7
  292. package/docs/json.md +7 -2
  293. package/docs/keybindings.md +9 -9
  294. package/docs/llama-cpp.md +2 -0
  295. package/docs/models.md +8 -2
  296. package/docs/packages.md +2 -2
  297. package/docs/quickstart.md +1 -1
  298. package/docs/rpc.md +32 -3
  299. package/docs/sdk.md +7 -2
  300. package/docs/settings.md +23 -7
  301. package/docs/skills.md +4 -3
  302. package/docs/terminal-setup.md +23 -9
  303. package/docs/usage.md +10 -5
  304. package/docs/windows.md +24 -2
  305. package/examples/extensions/border-status-editor.ts +1 -1
  306. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  307. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  308. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  309. package/examples/extensions/git-checkpoint.ts +2 -2
  310. package/examples/extensions/gondolin/package-lock.json +2 -2
  311. package/examples/extensions/gondolin/package.json +1 -1
  312. package/examples/extensions/notify.ts +3 -1
  313. package/examples/extensions/sandbox/package-lock.json +2 -2
  314. package/examples/extensions/sandbox/package.json +1 -1
  315. package/examples/extensions/subagent/README.md +1 -1
  316. package/examples/extensions/subagent/index.ts +6 -1
  317. package/examples/extensions/titlebar-spinner.ts +1 -1
  318. package/examples/extensions/with-deps/package-lock.json +2 -2
  319. package/examples/extensions/with-deps/package.json +1 -1
  320. package/examples/rpc-extension-ui.ts +1 -1
  321. package/examples/sdk/06-extensions.ts +1 -1
  322. package/examples/sdk/README.md +1 -1
  323. package/package.json +12 -14
@@ -7,20 +7,22 @@ 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";
21
+ import { createCompactionSummaryMessage } from "../../core/messages.js";
20
22
  import { defaultModelPerProvider, findExactModelReferenceMatch, resolveModelScopeFromModels, } from "../../core/model-resolver.js";
21
23
  import { CredentialSynchronizationError } from "../../core/model-runtime.js";
22
24
  import { DefaultPackageManager } from "../../core/package-manager.js";
23
- import { getPiClientUpdateMarkerPath, PI_CLIENT_RUNTIME_RELOAD_EXIT_CODE, readPiClientUpdateMarker, writePiClientRuntimeReloadState, } from "../../core/pi-client-runtime-update.js";
25
+ import { getCliBinName } from "../../core/pi-client-cli-adapter.js";
24
26
  import { formatMissingSessionCwdPrompt, MissingSessionCwdError } from "../../core/session-cwd.js";
25
27
  import { SessionManager, sessionEntryToContextMessages } from "../../core/session-manager.js";
26
28
  import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
@@ -35,12 +37,12 @@ import { openBrowser } from "../../utils/open-browser.js";
35
37
  import { getCwdRelativePath } from "../../utils/paths.js";
36
38
  import { getPiUserAgent } from "../../utils/pi-user-agent.js";
37
39
  import { killTrackedDetachedChildren } from "../../utils/shell.js";
40
+ import { loadAllHighlightLanguages } from "../../utils/syntax-highlight.js";
38
41
  import { ensureTool } from "../../utils/tools-manager.js";
39
42
  import { checkForNewPiVersion } from "../../utils/version-check.js";
40
43
  import { ArminComponent } from "./components/armin.js";
41
44
  import { AssistantMessageComponent } from "./components/assistant-message.js";
42
45
  import { BashExecutionComponent } from "./components/bash-execution.js";
43
- import { BorderedLoader } from "./components/bordered-loader.js";
44
46
  import { BranchSummaryMessageComponent } from "./components/branch-summary-message.js";
45
47
  import { CompactionSummaryMessageComponent } from "./components/compaction-summary-message.js";
46
48
  import { CustomEditor } from "./components/custom-editor.js";
@@ -63,6 +65,7 @@ import { SessionSelectorComponent } from "./components/session-selector.js";
63
65
  import { SettingsSelectorComponent } from "./components/settings-selector.js";
64
66
  import { SkillInvocationMessageComponent } from "./components/skill-invocation-message.js";
65
67
  import { BranchSummaryStatusIndicator, CompactionStatusIndicator, IdleStatus, RetryStatusIndicator, WorkingStatusIndicator, } from "./components/status-indicator.js";
68
+ import { ThinkingSelectorComponent } from "./components/thinking-selector.js";
66
69
  import { ToolExecutionComponent } from "./components/tool-execution.js";
67
70
  import { TreeSelectorComponent } from "./components/tree-selector.js";
68
71
  import { TrustSelectorComponent } from "./components/trust-selector.js";
@@ -71,9 +74,9 @@ import { UserMessageSelectorComponent } from "./components/user-message-selector
71
74
  import { editInExternalEditor } from "./external-editor.js";
72
75
  import { refreshModelCatalogs } from "./model-catalog-refresh.js";
73
76
  import { getModelSearchText } from "./model-search.js";
77
+ import { shareSession } from "./session-share.js";
74
78
  import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, onThemeChange, setRegisteredThemes, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
75
79
  import { InteractiveThemeController } from "./theme/theme-controller.js";
76
- const TRANSCRIPT_LAZY_LOAD_BATCH_SIZE = 20;
77
80
  function isExpandable(obj) {
78
81
  return typeof obj === "object" && obj !== null && "setExpanded" in obj && typeof obj.setExpanded === "function";
79
82
  }
@@ -92,6 +95,9 @@ class ExpandableText extends Text {
92
95
  function isCustomSessionEntry(item) {
93
96
  return "type" in item && item.type === "custom";
94
97
  }
98
+ function isCompactionCostNotice(item) {
99
+ return "type" in item && item.type === "compaction_cost";
100
+ }
95
101
  const DEAD_TERMINAL_ERROR_CODES = new Set(["EIO", "EPIPE", "ENOTCONN"]);
96
102
  function isDeadTerminalError(error) {
97
103
  if (!error || typeof error !== "object" || !("code" in error)) {
@@ -121,7 +127,7 @@ export function formatResumeCommand(sessionManager) {
121
127
  const sessionFile = sessionManager.getSessionFile();
122
128
  if (!sessionFile || !fs.existsSync(sessionFile))
123
129
  return undefined;
124
- const args = [process.env.PI_SERVER_MODE === "true" ? "pi-client" : APP_NAME];
130
+ const args = [getCliBinName()];
125
131
  if (!sessionManager.usesDefaultSessionDir()) {
126
132
  args.push("--session-dir", quoteIfNeeded(sessionManager.getSessionDir()));
127
133
  }
@@ -131,6 +137,11 @@ export function formatResumeCommand(sessionManager) {
131
137
  function hasDefaultModelProvider(providerId) {
132
138
  return providerId in defaultModelPerProvider;
133
139
  }
140
+ function llamaCppPostLoginGuidance(actionLabel, loadedModelCount) {
141
+ return loadedModelCount === 0
142
+ ? `${actionLabel}. No llama.cpp models are loaded. Use /llama to load a model, then /model to select it.`
143
+ : `${actionLabel}. Use /model to select a loaded llama.cpp model, or /llama to manage models.`;
144
+ }
134
145
  const AUTH_TYPE_ORDER = { oauth: 0, api_key: 1 };
135
146
  function createFuzzyAutocompleteItems(items, prefix, getSearchText, toAutocompleteItem) {
136
147
  const filtered = fuzzyFilter(items, prefix, getSearchText);
@@ -177,6 +188,7 @@ export function createInteractiveTui(options) {
177
188
  searchCurrentMatchStyle: (text) => theme.bold(theme.inverse(styleSearchMatch(text))),
178
189
  openUrl: openBrowser,
179
190
  onRightClickPaste: options.onRightClickPaste,
191
+ copyOnSelect: options.fullscreenCopyOnSelect,
180
192
  copySelection: async (text) => {
181
193
  try {
182
194
  await copyToClipboard(text);
@@ -230,11 +242,6 @@ export class InteractiveMode {
230
242
  chatContainer;
231
243
  documentContainer;
232
244
  transcriptScrollView;
233
- transcriptLazyEntries = [];
234
- loadedTranscriptEntryIds = new Set();
235
- transcriptWindowInitialized = false;
236
- transcriptLatestCompactionId;
237
- loadingEarlierTranscript = false;
238
245
  fullscreenLayoutRoot;
239
246
  pendingMessagesContainer;
240
247
  statusContainer;
@@ -345,6 +352,7 @@ export class InteractiveMode {
345
352
  }
346
353
  constructor(runtimeHost, options = {}) {
347
354
  this.runtimeHost = runtimeHost;
355
+ setCapabilityOverrides(this.settingsManager.getTerminalCapabilityOverrides());
348
356
  const tuiMode = options.tuiMode ?? this.settingsManager.getTuiMode();
349
357
  this.options = { ...options, tuiMode };
350
358
  this.autoTrustOnReloadCwd = options.autoTrustOnReloadCwd;
@@ -361,6 +369,7 @@ export class InteractiveMode {
361
369
  showHardwareCursor: this.settingsManager.getShowHardwareCursor(),
362
370
  logDirectory: getAgentDir(),
363
371
  onRightClickPaste: this.onRightClickPaste,
372
+ fullscreenCopyOnSelect: this.settingsManager.getFullscreenCopyOnSelect(),
364
373
  });
365
374
  this.ui = createInteractiveTuiReference(() => this.renderer);
366
375
  this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink());
@@ -471,6 +480,15 @@ export class InteractiveMode {
471
480
  }));
472
481
  };
473
482
  }
483
+ const thinkingCommand = slashCommands.find((command) => command.name === "thinking");
484
+ if (thinkingCommand) {
485
+ thinkingCommand.getArgumentCompletions = (prefix) => {
486
+ return createFuzzyAutocompleteItems(this.session.getAvailableThinkingLevels(), prefix, (level) => level, (level) => ({
487
+ value: level,
488
+ label: level,
489
+ }));
490
+ };
491
+ }
474
492
  const loginCommand = slashCommands.find((command) => command.name === "login");
475
493
  if (loginCommand) {
476
494
  loginCommand.getArgumentCompletions = (prefix) => {
@@ -599,6 +617,7 @@ export class InteractiveMode {
599
617
  logDirectory: getAgentDir(),
600
618
  terminal,
601
619
  onRightClickPaste: this.onRightClickPaste,
620
+ fullscreenCopyOnSelect: this.settingsManager.getFullscreenCopyOnSelect(),
602
621
  });
603
622
  nextUi.setClearOnShrink(clearOnShrink);
604
623
  nextUi.onDebug = onDebug;
@@ -647,7 +666,6 @@ export class InteractiveMode {
647
666
  follow: "end",
648
667
  primary: true,
649
668
  overscroll: "chain",
650
- onScroll: (scrollTop) => this.handleTranscriptScroll(scrollTop),
651
669
  scrollbar: this.settingsManager.getFullscreenScrollbar(),
652
670
  scrollbarStyle: (text) => theme.bg("scrollbarThumb", text),
653
671
  });
@@ -756,6 +774,14 @@ export class InteractiveMode {
756
774
  });
757
775
  // Initialize available provider count for footer display
758
776
  await this.updateAvailableProviderCount();
777
+ // Flush the completed startup state before loading the remaining syntax grammars.
778
+ this.ui.renderNow();
779
+ void loadAllHighlightLanguages().then(() => {
780
+ if (!this.isInitialized)
781
+ return;
782
+ this.ui.invalidate();
783
+ this.ui.requestRender();
784
+ });
759
785
  }
760
786
  /**
761
787
  * Update terminal title with session name and cwd.
@@ -785,7 +811,7 @@ export class InteractiveMode {
785
811
  .finally(() => clearTimeout(timeout));
786
812
  }
787
813
  // Start version check asynchronously
788
- checkForNewPiVersion(this.version).then((newRelease) => {
814
+ checkForNewPiVersion(process.env.PI_CLIENT_VERSION ?? this.version).then((newRelease) => {
789
815
  if (newRelease) {
790
816
  this.showNewVersionNotification(newRelease);
791
817
  }
@@ -811,7 +837,18 @@ export class InteractiveMode {
811
837
  }
812
838
  });
813
839
  // Show startup warnings
814
- const { migratedProviders, modelFallbackMessage, initialMessage, initialImages, initialMessages } = this.options;
840
+ const { migratedProviders, startupDiagnostics, modelFallbackMessage, initialMessage, initialImages, initialMessages, } = this.options;
841
+ for (const diagnostic of startupDiagnostics ?? []) {
842
+ if (diagnostic.type === "error") {
843
+ this.showError(diagnostic.message);
844
+ }
845
+ else if (diagnostic.type === "warning") {
846
+ this.showWarning(diagnostic.message);
847
+ }
848
+ else {
849
+ this.showStatus(diagnostic.message);
850
+ }
851
+ }
815
852
  if (migratedProviders && migratedProviders.length > 0) {
816
853
  this.showWarning(`Migrated credentials to auth.json: ${migratedProviders.join(", ")}`);
817
854
  }
@@ -966,14 +1003,13 @@ export class InteractiveMode {
966
1003
  // Extension System
967
1004
  // =========================================================================
968
1005
  formatDisplayPath(p) {
969
- const normalizedPath = p.replaceAll("\\", "/");
970
- const normalizedHome = os.homedir().replaceAll("\\", "/").replace(/\/+$/, "");
971
- if (normalizedPath === normalizedHome)
972
- return "~";
973
- if (normalizedPath.startsWith(`${normalizedHome}/`)) {
974
- return `~/${normalizedPath.slice(normalizedHome.length + 1)}`;
1006
+ const home = os.homedir();
1007
+ let result = p;
1008
+ // Replace home directory with ~
1009
+ if (result.startsWith(home)) {
1010
+ result = `~${result.slice(home.length)}`;
975
1011
  }
976
- return p;
1012
+ return result;
977
1013
  }
978
1014
  formatExtensionDisplayPath(path) {
979
1015
  let result = this.formatDisplayPath(path);
@@ -1490,8 +1526,12 @@ export class InteractiveMode {
1490
1526
  this.transcriptScrollView?.setScrollbar(this.settingsManager.getFullscreenScrollbar());
1491
1527
  }
1492
1528
  applyRuntimeSettings() {
1529
+ setCapabilityOverrides(this.settingsManager.getTerminalCapabilityOverrides());
1493
1530
  configureHttpDispatcher(this.settingsManager.getHttpIdleTimeoutMs());
1494
1531
  this.applyFullscreenScrollbarSetting();
1532
+ if (this.renderer instanceof TuiAltScreen) {
1533
+ this.renderer.setCopyOnSelect(this.settingsManager.getFullscreenCopyOnSelect());
1534
+ }
1495
1535
  this.footer.setSession(this.session);
1496
1536
  this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
1497
1537
  this.footerDataProvider.setCwd(this.sessionManager.getCwd());
@@ -1641,6 +1681,9 @@ export class InteractiveMode {
1641
1681
  this.statusContainer.addChild(this.idleStatus);
1642
1682
  }
1643
1683
  }
1684
+ showWorkingStatusIndicator() {
1685
+ this.showStatusIndicator(new WorkingStatusIndicator(this.ui, this.workingMessage ?? this.defaultWorkingMessage, this.workingIndicatorOptions));
1686
+ }
1644
1687
  setWorkingVisible(visible) {
1645
1688
  this.workingVisible = visible;
1646
1689
  if (!visible) {
@@ -1649,7 +1692,7 @@ export class InteractiveMode {
1649
1692
  return;
1650
1693
  }
1651
1694
  if (this.session.isStreaming && this.activeStatusIndicator?.kind !== "working") {
1652
- this.showStatusIndicator(new WorkingStatusIndicator(this.ui, this.workingMessage ?? this.defaultWorkingMessage, this.workingIndicatorOptions));
1695
+ this.showWorkingStatusIndicator();
1653
1696
  }
1654
1697
  this.ui.requestRender();
1655
1698
  }
@@ -1660,15 +1703,6 @@ export class InteractiveMode {
1660
1703
  }
1661
1704
  this.ui.requestRender();
1662
1705
  }
1663
- restoreWorkingIndicator() {
1664
- if (!this.session.isStreaming || !this.workingVisible) {
1665
- return;
1666
- }
1667
- if (this.settingsManager.getShowTerminalProgress()) {
1668
- this.ui.terminal.setProgress(true);
1669
- }
1670
- this.showStatusIndicator(new WorkingStatusIndicator(this.ui, this.workingMessage ?? this.defaultWorkingMessage, this.workingIndicatorOptions));
1671
- }
1672
1706
  setHiddenThinkingLabel(label) {
1673
1707
  this.hiddenThinkingLabel = label ?? this.defaultHiddenThinkingLabel;
1674
1708
  for (const child of this.chatContainer.children) {
@@ -2278,14 +2312,13 @@ export class InteractiveMode {
2278
2312
  this.defaultEditor.onAction("app.tools.expand", () => this.toggleToolOutputExpansion());
2279
2313
  this.defaultEditor.onAction("app.thinking.toggle", () => this.toggleThinkingBlockVisibility());
2280
2314
  this.defaultEditor.onAction("app.editor.external", () => void this.handleOpenExternalEditor());
2281
- this.defaultEditor.onAction("app.message.copy", () => void this.handleCopyCommand({ flashConfirmation: true }));
2315
+ this.defaultEditor.onAction("app.message.copy", () => void this.handleCopyCommand({ flashConfirmation: true, preferSelection: true }));
2282
2316
  this.defaultEditor.onAction("app.message.followUp", () => this.handleFollowUp());
2283
2317
  this.defaultEditor.onAction("app.message.dequeue", () => this.handleDequeue());
2284
2318
  this.defaultEditor.onAction("app.session.new", () => this.handleClearCommand());
2285
2319
  this.defaultEditor.onAction("app.session.tree", () => this.showTreeSelector());
2286
2320
  this.defaultEditor.onAction("app.session.fork", () => this.showUserMessageSelector());
2287
2321
  this.defaultEditor.onAction("app.session.resume", () => this.showSessionSelector());
2288
- this.defaultEditor.onAction("app.session.loadEarlier", () => this.loadEarlierTranscript());
2289
2322
  this.defaultEditor.onChange = (text) => {
2290
2323
  const wasBashMode = this.isBashMode;
2291
2324
  this.isBashMode = text.trimStart().startsWith("!");
@@ -2364,6 +2397,12 @@ export class InteractiveMode {
2364
2397
  await this.handleModelCommand(searchTerm);
2365
2398
  return;
2366
2399
  }
2400
+ if (text === "/thinking" || text.startsWith("/thinking ")) {
2401
+ const searchTerm = text.startsWith("/thinking ") ? text.slice(10).trim() : undefined;
2402
+ this.editor.setText("");
2403
+ this.handleThinkingCommand(searchTerm);
2404
+ return;
2405
+ }
2367
2406
  if (text === "/export" || text.startsWith("/export ")) {
2368
2407
  await this.handleExportCommand(text);
2369
2408
  this.editor.setText("");
@@ -2451,12 +2490,6 @@ export class InteractiveMode {
2451
2490
  await this.handleReloadCommand();
2452
2491
  return;
2453
2492
  }
2454
- const pendingPiClientUpdate = readPiClientUpdateMarker();
2455
- if (pendingPiClientUpdate) {
2456
- this.showWarning(`pi-client ${pendingPiClientUpdate.version} is installed. Run /reload to continue this session with the new version.`);
2457
- this.editor.setText(text);
2458
- return;
2459
- }
2460
2493
  if (text === "/debug") {
2461
2494
  this.handleDebugCommand();
2462
2495
  this.editor.setText("");
@@ -2546,17 +2579,21 @@ export class InteractiveMode {
2546
2579
  switch (event.type) {
2547
2580
  case "agent_start":
2548
2581
  this.pendingTools.clear();
2549
- if (this.settingsManager.getShowTerminalProgress()) {
2550
- this.ui.terminal.setProgress(true);
2551
- }
2552
2582
  // Restore main escape handler if retry handler is still active
2553
2583
  // (retry success event fires later, but we need main handler now)
2554
2584
  if (this.retryEscapeHandler) {
2555
2585
  this.defaultEditor.onEscape = this.retryEscapeHandler;
2556
2586
  this.retryEscapeHandler = undefined;
2557
2587
  }
2588
+ break;
2589
+ case "turn_start":
2590
+ if (this.settingsManager.getShowTerminalProgress()) {
2591
+ this.ui.terminal.setProgress(true);
2592
+ }
2558
2593
  if (this.workingVisible) {
2559
- this.showStatusIndicator(new WorkingStatusIndicator(this.ui, this.workingMessage ?? this.defaultWorkingMessage, this.workingIndicatorOptions));
2594
+ if (this.activeStatusIndicator?.kind !== "working") {
2595
+ this.showWorkingStatusIndicator();
2596
+ }
2560
2597
  }
2561
2598
  else {
2562
2599
  this.clearStatusIndicator();
@@ -2702,6 +2739,10 @@ export class InteractiveMode {
2702
2739
  break;
2703
2740
  }
2704
2741
  case "agent_end":
2742
+ if (this.settingsManager.getShowTerminalProgress()) {
2743
+ this.ui.terminal.setProgress(false);
2744
+ }
2745
+ this.clearStatusIndicator("working");
2705
2746
  if (this.streamingComponent) {
2706
2747
  this.chatContainer.removeChild(this.streamingComponent);
2707
2748
  this.streamingComponent = undefined;
@@ -2711,12 +2752,7 @@ export class InteractiveMode {
2711
2752
  this.ui.requestRender();
2712
2753
  break;
2713
2754
  case "agent_settled":
2714
- if (this.settingsManager.getShowTerminalProgress()) {
2715
- this.ui.terminal.setProgress(false);
2716
- }
2717
- this.clearStatusIndicator("working");
2718
2755
  await this.checkShutdownRequested();
2719
- this.ui.requestRender();
2720
2756
  break;
2721
2757
  case "compaction_start": {
2722
2758
  if (this.settingsManager.getShowTerminalProgress()) {
@@ -2749,7 +2785,21 @@ export class InteractiveMode {
2749
2785
  }
2750
2786
  }
2751
2787
  else if (event.result) {
2752
- this.rebuildChatFromMessages();
2788
+ const entries = this.sessionManager.buildContextEntries();
2789
+ if (entries[0]?.type !== "compaction") {
2790
+ throw new Error("Completed compaction is missing from the session context");
2791
+ }
2792
+ this.chatContainer.clear();
2793
+ // The latest compaction is prepended for model context; append it below at its chronological position.
2794
+ this.renderSessionEntries(entries.slice(1));
2795
+ this.addMessageToChat(createCompactionSummaryMessage(event.result.summary, event.result.tokensBefore, new Date().toISOString()));
2796
+ if (event.result.usage) {
2797
+ this.addCompactionCostNotice({
2798
+ type: "compaction_cost",
2799
+ kind: "compaction",
2800
+ usage: event.result.usage,
2801
+ });
2802
+ }
2753
2803
  this.footer.invalidate();
2754
2804
  }
2755
2805
  else if (event.errorMessage) {
@@ -2761,7 +2811,6 @@ export class InteractiveMode {
2761
2811
  this.chatContainer.addChild(new Text(theme.fg("error", event.errorMessage), 1, 0));
2762
2812
  }
2763
2813
  }
2764
- this.restoreWorkingIndicator();
2765
2814
  void this.flushCompactionQueue({ willRetry: event.willRetry });
2766
2815
  this.ui.requestRender();
2767
2816
  break;
@@ -2787,7 +2836,6 @@ export class InteractiveMode {
2787
2836
  if (!event.success) {
2788
2837
  this.showError(`Retry failed after ${event.attempt} attempts: ${event.finalError || "Unknown error"}`);
2789
2838
  }
2790
- this.restoreWorkingIndicator();
2791
2839
  this.ui.requestRender();
2792
2840
  break;
2793
2841
  }
@@ -2973,6 +3021,10 @@ export class InteractiveMode {
2973
3021
  this.addCustomEntryToChat(item);
2974
3022
  continue;
2975
3023
  }
3024
+ if (isCompactionCostNotice(item)) {
3025
+ this.addCompactionCostNotice(item);
3026
+ continue;
3027
+ }
2976
3028
  const message = item;
2977
3029
  // Assistant messages need special handling for tool calls
2978
3030
  if (message.role === "assistant") {
@@ -3030,8 +3082,8 @@ export class InteractiveMode {
3030
3082
  this.ui.requestRender();
3031
3083
  }
3032
3084
  /**
3033
- * Render session entries to chat. Used for initial load and transcript rebuilds.
3034
- * @param entries Active-branch session entries to render
3085
+ * Render session entries to chat. Used for initial load and rebuild after compaction.
3086
+ * @param entries Compaction-aware session entries to render
3035
3087
  * @param options.updateFooter Update footer state
3036
3088
  * @param options.populateHistory Add user messages to editor history
3037
3089
  */
@@ -3040,10 +3092,28 @@ export class InteractiveMode {
3040
3092
  if (entry.type === "custom") {
3041
3093
  return [entry];
3042
3094
  }
3043
- return sessionEntryToContextMessages(entry);
3095
+ const messages = sessionEntryToContextMessages(entry);
3096
+ if ((entry.type === "compaction" || entry.type === "branch_summary") && entry.usage && messages.length > 0) {
3097
+ return [...messages, { type: "compaction_cost", kind: entry.type, usage: entry.usage }];
3098
+ }
3099
+ return messages;
3044
3100
  });
3045
3101
  this.renderSessionItems(items, options);
3046
3102
  }
3103
+ /**
3104
+ * Render billing usage for a compaction or branch summary. The notice is derived
3105
+ * from persisted summary usage and is not stored as a separate session entry.
3106
+ */
3107
+ addCompactionCostNotice(notice) {
3108
+ if (!this.settingsManager.getShowCacheMissNotices())
3109
+ return;
3110
+ const { usage } = notice;
3111
+ const tokens = usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
3112
+ const cost = usage.cost.total >= 0.01 ? ` (~$${usage.cost.total.toFixed(2)})` : "";
3113
+ const label = notice.kind === "compaction" ? "Compaction" : "Branch summary";
3114
+ this.chatContainer.addChild(new Spacer(1));
3115
+ this.chatContainer.addChild(new Text(theme.fg("warning", `${label}: ${formatTokens(tokens)} tokens billed${cost}`), 1, 0));
3116
+ }
3047
3117
  /**
3048
3118
  * Show a transcript notice when a completed assistant message paid for a
3049
3119
  * significant cache miss. Only states observable facts: the miss itself,
@@ -3073,66 +3143,9 @@ export class InteractiveMode {
3073
3143
  this.chatContainer.addChild(new Spacer(1));
3074
3144
  this.chatContainer.addChild(new Text(text, 1, 0));
3075
3145
  }
3076
- getTranscriptEntriesForRender(reset = false) {
3077
- const branch = this.sessionManager.getBranch();
3078
- const contextEntries = this.sessionManager.buildContextEntries();
3079
- let latestCompactionId;
3080
- for (let index = branch.length - 1; index >= 0; index--) {
3081
- const entry = branch[index];
3082
- if (entry?.type === "compaction") {
3083
- latestCompactionId = entry.id;
3084
- break;
3085
- }
3086
- }
3087
- if (reset || (this.transcriptWindowInitialized && this.transcriptLatestCompactionId !== latestCompactionId)) {
3088
- this.loadedTranscriptEntryIds.clear();
3089
- }
3090
- this.transcriptWindowInitialized = true;
3091
- this.transcriptLatestCompactionId = latestCompactionId;
3092
- const contextEntryIds = new Set(contextEntries.map((entry) => entry.id));
3093
- const earlierEntries = branch.filter((entry) => !contextEntryIds.has(entry.id));
3094
- const loadedEntries = earlierEntries.filter((entry) => this.loadedTranscriptEntryIds.has(entry.id));
3095
- this.transcriptLazyEntries = earlierEntries.filter((entry) => !this.loadedTranscriptEntryIds.has(entry.id));
3096
- return [...loadedEntries, ...contextEntries];
3097
- }
3098
- renderTranscriptEntries(entries, options = {}) {
3099
- if (this.transcriptLazyEntries.length > 0) {
3100
- const count = this.transcriptLazyEntries.length.toLocaleString();
3101
- this.chatContainer.addChild(new Text(theme.fg("dim", `${count} earlier transcript entries hidden (${keyText("app.session.loadEarlier")} to load)`), 1, 0));
3102
- this.chatContainer.addChild(new Spacer(1));
3103
- }
3104
- this.renderSessionEntries(entries, options);
3105
- }
3106
- handleTranscriptScroll(scrollTop) {
3107
- if (scrollTop === 0) {
3108
- this.loadEarlierTranscript();
3109
- }
3110
- }
3111
- loadEarlierTranscript() {
3112
- if (this.loadingEarlierTranscript || this.session.isStreaming || this.transcriptLazyEntries.length === 0) {
3113
- return;
3114
- }
3115
- this.loadingEarlierTranscript = true;
3116
- try {
3117
- const firstLoadedIndex = Math.max(0, this.transcriptLazyEntries.length - TRANSCRIPT_LAZY_LOAD_BATCH_SIZE);
3118
- const entriesToLoad = this.transcriptLazyEntries.slice(firstLoadedIndex);
3119
- for (const entry of entriesToLoad) {
3120
- this.loadedTranscriptEntryIds.add(entry.id);
3121
- }
3122
- this.rebuildChatFromMessages();
3123
- const remaining = this.transcriptLazyEntries.length;
3124
- this.showStatus(remaining > 0
3125
- ? `Loaded ${entriesToLoad.length} earlier transcript entries (${remaining} remaining)`
3126
- : "Loaded the beginning of the transcript");
3127
- this.ui.requestRender();
3128
- }
3129
- finally {
3130
- this.loadingEarlierTranscript = false;
3131
- }
3132
- }
3133
3146
  renderInitialMessages() {
3134
- const entries = this.getTranscriptEntriesForRender(true);
3135
- this.renderTranscriptEntries(entries, {
3147
+ const entries = this.sessionManager.buildContextEntries();
3148
+ this.renderSessionEntries(entries, {
3136
3149
  updateFooter: true,
3137
3150
  populateHistory: true,
3138
3151
  });
@@ -3168,7 +3181,7 @@ export class InteractiveMode {
3168
3181
  }
3169
3182
  rebuildChatFromMessages() {
3170
3183
  this.chatContainer.clear();
3171
- this.renderTranscriptEntries(this.getTranscriptEntriesForRender());
3184
+ this.renderSessionEntries(this.sessionManager.buildContextEntries());
3172
3185
  }
3173
3186
  // =========================================================================
3174
3187
  // Key handlers
@@ -3450,18 +3463,19 @@ export class InteractiveMode {
3450
3463
  }
3451
3464
  this.showStatus(`Tool output: ${expanded ? "expanded" : "collapsed"}`);
3452
3465
  }
3466
+ /** Update rendered assistant messages without rebuilding live tool components. */
3467
+ updateThinkingBlockVisibility() {
3468
+ for (const child of this.chatContainer.children) {
3469
+ if (child instanceof AssistantMessageComponent) {
3470
+ child.setHideThinkingBlock(this.hideThinkingBlock);
3471
+ }
3472
+ }
3473
+ this.ui.requestRender();
3474
+ }
3453
3475
  toggleThinkingBlockVisibility() {
3454
3476
  this.hideThinkingBlock = !this.hideThinkingBlock;
3455
3477
  this.settingsManager.setHideThinkingBlock(this.hideThinkingBlock);
3456
- // Rebuild chat from session messages
3457
- this.chatContainer.clear();
3458
- this.rebuildChatFromMessages();
3459
- // If streaming, re-add the streaming component with updated visibility and re-render
3460
- if (this.streamingComponent && this.streamingMessage) {
3461
- this.streamingComponent.setHideThinkingBlock(this.hideThinkingBlock);
3462
- this.streamingComponent.updateContent(this.streamingMessage);
3463
- this.chatContainer.addChild(this.streamingComponent);
3464
- }
3478
+ this.updateThinkingBlockVisibility();
3465
3479
  this.showStatus(`Thinking blocks: ${this.hideThinkingBlock ? "hidden" : "visible"}`);
3466
3480
  }
3467
3481
  async handleOpenExternalEditor() {
@@ -3737,8 +3751,14 @@ export class InteractiveMode {
3737
3751
  showSettingsSelector() {
3738
3752
  this.showSelector((done) => {
3739
3753
  let selector;
3754
+ const defaultProvider = this.settingsManager.getDefaultProvider();
3755
+ const defaultModelId = this.settingsManager.getDefaultModel();
3756
+ const defaultModel = defaultProvider && defaultModelId ? `${defaultProvider}/${defaultModelId}` : "not set";
3740
3757
  selector = new SettingsSelectorComponent({
3741
3758
  autoCompact: this.session.autoCompactionEnabled,
3759
+ defaultModel,
3760
+ currentModel: this.session.model,
3761
+ availableDefaultModels: this.session.modelRuntime.getAvailableSnapshot(),
3742
3762
  showImages: this.settingsManager.getShowImages(),
3743
3763
  imageWidthCells: this.settingsManager.getImageWidthCells(),
3744
3764
  autoResizeImages: this.settingsManager.getImageAutoResize(),
@@ -3748,8 +3768,9 @@ export class InteractiveMode {
3748
3768
  followUpMode: this.session.followUpMode,
3749
3769
  transport: this.settingsManager.getTransport(),
3750
3770
  httpIdleTimeoutMs: this.settingsManager.getHttpIdleTimeoutMs(),
3751
- thinkingLevel: this.session.thinkingLevel,
3752
- availableThinkingLevels: this.session.getAvailableThinkingLevels(),
3771
+ thinkingLevel: this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL,
3772
+ availableThinkingLevels: [...THINKING_LEVEL_OPTIONS],
3773
+ modelThinkingLevels: this.settingsManager.getAllModelThinkingLevels(),
3753
3774
  currentTheme: this.themeController.getThemeSelection() || "dark",
3754
3775
  terminalTheme: this.themeController.getTerminalTheme(),
3755
3776
  availableThemes: getAvailableThemes(),
@@ -3771,6 +3792,7 @@ export class InteractiveMode {
3771
3792
  tuiMode: this.ui.mode,
3772
3793
  fullscreenExitOutput: this.settingsManager.getFullscreenExitOutput(),
3773
3794
  fullscreenScrollbar: this.settingsManager.getFullscreenScrollbar(),
3795
+ fullscreenCopyOnSelect: this.settingsManager.getFullscreenCopyOnSelect(),
3774
3796
  warnings: this.settingsManager.getWarnings(),
3775
3797
  }, {
3776
3798
  onAutoCompactChange: (enabled) => {
@@ -3818,10 +3840,26 @@ export class InteractiveMode {
3818
3840
  configureHttpDispatcher(timeoutMs);
3819
3841
  this.showStatus(`HTTP idle timeout: ${formatHttpIdleTimeoutMs(timeoutMs)}`);
3820
3842
  },
3821
- onThinkingLevelChange: (level) => {
3822
- this.session.setThinkingLevel(level);
3823
- this.footer.invalidate();
3824
- this.updateEditorBorderColor();
3843
+ onModelThinkingLevelChange: (provider, modelId, level) => {
3844
+ this.settingsManager.setModelThinkingLevel(provider, modelId, level);
3845
+ // If the override is for the current model, apply it to the session too
3846
+ const current = this.session.model;
3847
+ if (current && current.provider === provider && current.id === modelId) {
3848
+ this.session.setThinkingLevel(level);
3849
+ this.footer.invalidate();
3850
+ this.updateEditorBorderColor();
3851
+ }
3852
+ },
3853
+ onModelThinkingLevelRemove: (provider, modelId) => {
3854
+ this.settingsManager.removeModelThinkingLevel(provider, modelId);
3855
+ // If the override was for the current model, revert to global default
3856
+ const current = this.session.model;
3857
+ if (current && current.provider === provider && current.id === modelId) {
3858
+ const globalDefault = this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
3859
+ this.session.setThinkingLevel(globalDefault);
3860
+ this.footer.invalidate();
3861
+ this.updateEditorBorderColor();
3862
+ }
3825
3863
  },
3826
3864
  onThemeChange: (themeSetting) => {
3827
3865
  this.settingsManager.setTheme(themeSetting);
@@ -3831,13 +3869,7 @@ export class InteractiveMode {
3831
3869
  onHideThinkingBlockChange: (hidden) => {
3832
3870
  this.hideThinkingBlock = hidden;
3833
3871
  this.settingsManager.setHideThinkingBlock(hidden);
3834
- for (const child of this.chatContainer.children) {
3835
- if (child instanceof AssistantMessageComponent) {
3836
- child.setHideThinkingBlock(hidden);
3837
- }
3838
- }
3839
- this.chatContainer.clear();
3840
- this.rebuildChatFromMessages();
3872
+ this.updateThinkingBlockVisibility();
3841
3873
  },
3842
3874
  onMermaidRenderingModeChange: (mode) => {
3843
3875
  this.settingsManager.setMermaidRenderingMode(mode);
@@ -3931,6 +3963,11 @@ export class InteractiveMode {
3931
3963
  this.settingsManager.setFullscreenScrollbar(mode);
3932
3964
  this.applyFullscreenScrollbarSetting();
3933
3965
  },
3966
+ onFullscreenCopyOnSelectChange: (enabled) => {
3967
+ this.settingsManager.setFullscreenCopyOnSelect(enabled);
3968
+ if (this.renderer instanceof TuiAltScreen)
3969
+ this.renderer.setCopyOnSelect(enabled);
3970
+ },
3934
3971
  onWarningsChange: (warnings) => {
3935
3972
  this.settingsManager.setWarnings(warnings);
3936
3973
  },
@@ -3942,6 +3979,44 @@ export class InteractiveMode {
3942
3979
  return { component: selector, focus: selector.getSettingsList() };
3943
3980
  });
3944
3981
  }
3982
+ handleThinkingCommand(searchTerm) {
3983
+ const availableLevels = this.session.getAvailableThinkingLevels();
3984
+ if (!searchTerm) {
3985
+ this.showThinkingSelector();
3986
+ return;
3987
+ }
3988
+ const normalized = searchTerm.trim().toLowerCase();
3989
+ const level = availableLevels.find((candidate) => candidate.toLowerCase() === normalized);
3990
+ if (!level) {
3991
+ this.showError(`Unknown thinking level "${searchTerm}". Available levels: ${availableLevels.join(", ")}.`);
3992
+ return;
3993
+ }
3994
+ this.selectThinkingLevel(level, false);
3995
+ }
3996
+ selectThinkingLevel(level, persist) {
3997
+ try {
3998
+ this.session.setThinkingLevel(level, { persist });
3999
+ this.footer.invalidate();
4000
+ this.updateEditorBorderColor();
4001
+ this.showStatus(persist ? `Default thinking level: ${level}` : `Thinking level: ${level}`);
4002
+ }
4003
+ catch (error) {
4004
+ this.showError(error instanceof Error ? error.message : String(error));
4005
+ }
4006
+ }
4007
+ showThinkingSelector() {
4008
+ this.showSelector((done) => {
4009
+ const selectLevel = (level, persist) => {
4010
+ this.selectThinkingLevel(level, persist);
4011
+ done();
4012
+ };
4013
+ const selector = new ThinkingSelectorComponent(this.session.thinkingLevel ?? DEFAULT_THINKING_LEVEL, this.session.getAvailableThinkingLevels(), (level) => selectLevel(level, false), () => {
4014
+ done();
4015
+ this.ui.requestRender();
4016
+ }, (level) => selectLevel(level, true), this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);
4017
+ return { component: selector, focus: selector };
4018
+ });
4019
+ }
3945
4020
  async handleModelCommand(searchTerm) {
3946
4021
  if (!searchTerm) {
3947
4022
  this.showModelSelector();
@@ -3950,7 +4025,7 @@ export class InteractiveMode {
3950
4025
  const model = await this.findExactModelMatch(searchTerm);
3951
4026
  if (model) {
3952
4027
  try {
3953
- await this.session.setModel(model);
4028
+ await this.session.setModel(model, { persist: false });
3954
4029
  this.footer.invalidate();
3955
4030
  this.updateEditorBorderColor();
3956
4031
  this.showStatus(`Model: ${model.id}`);
@@ -4079,13 +4154,14 @@ export class InteractiveMode {
4079
4154
  }
4080
4155
  showModelSelector(initialSearchInput) {
4081
4156
  this.showSelector((done) => {
4082
- const selector = new ModelSelectorComponent(this.ui, this.session.model, this.settingsManager, this.session.modelRuntime, this.session.scopedModels, async (model) => {
4157
+ const selectModel = async (model, persist) => {
4083
4158
  try {
4084
- await this.session.setModel(model);
4159
+ await this.session.setModel(model, { persist });
4160
+ this.updateAvailableProviderCount();
4085
4161
  this.footer.invalidate();
4086
4162
  this.updateEditorBorderColor();
4087
4163
  done();
4088
- this.showStatus(`Model: ${model.id}`);
4164
+ this.showStatus(persist ? `Default model: ${model.provider}/${model.id}` : `Model: ${model.id}`);
4089
4165
  void this.maybeWarnAboutAnthropicSubscriptionAuth(model);
4090
4166
  this.checkDaxnutsEasterEgg(model);
4091
4167
  }
@@ -4093,10 +4169,13 @@ export class InteractiveMode {
4093
4169
  done();
4094
4170
  this.showError(error instanceof Error ? error.message : String(error));
4095
4171
  }
4096
- }, () => {
4172
+ };
4173
+ const defaultProvider = this.settingsManager.getDefaultProvider();
4174
+ const defaultModel = this.settingsManager.getDefaultModel();
4175
+ const selector = new ModelSelectorComponent(this.ui, this.session.model, this.session.modelRuntime, this.session.scopedModels, (model) => selectModel(model, false), () => {
4097
4176
  done();
4098
4177
  this.ui.requestRender();
4099
- }, initialSearchInput);
4178
+ }, initialSearchInput, (model) => selectModel(model, true), defaultProvider && defaultModel ? { provider: defaultProvider, id: defaultModel } : undefined);
4100
4179
  return { component: selector, focus: selector, dispose: () => selector.dispose() };
4101
4180
  });
4102
4181
  }
@@ -4652,7 +4731,11 @@ export class InteractiveMode {
4652
4731
  if (isUnknownModel(previousModel)) {
4653
4732
  const availableModels = this.session.modelRuntime.getAvailableSnapshot();
4654
4733
  const providerModels = availableModels.filter((model) => model.provider === providerId);
4655
- if (!hasDefaultModelProvider(providerId)) {
4734
+ // Matches LLAMA_PROVIDER_ID from extensions/llama/provider.ts; kept inline to avoid coupling interactive mode to the built-in extension.
4735
+ if (providerId === "llama.cpp") {
4736
+ selectionError = llamaCppPostLoginGuidance(actionLabel, providerModels.length);
4737
+ }
4738
+ else if (!hasDefaultModelProvider(providerId)) {
4656
4739
  selectionError = `${actionLabel}, but no default model is configured for provider "${providerId}". Use /model to select a model.`;
4657
4740
  }
4658
4741
  else if (providerModels.length === 0) {
@@ -4666,7 +4749,7 @@ export class InteractiveMode {
4666
4749
  }
4667
4750
  else {
4668
4751
  try {
4669
- await this.session.setModel(selectedModel);
4752
+ await this.session.setModel(selectedModel, { persist: true });
4670
4753
  }
4671
4754
  catch (error) {
4672
4755
  selectedModel = undefined;
@@ -4883,25 +4966,6 @@ export class InteractiveMode {
4883
4966
  this.showWarning("Wait for compaction to finish before reloading.");
4884
4967
  return;
4885
4968
  }
4886
- const pendingPiClientUpdate = readPiClientUpdateMarker();
4887
- if (pendingPiClientUpdate) {
4888
- const reloadStatePath = process.env.PI_CLIENT_RELOAD_STATE_PATH;
4889
- if (!reloadStatePath) {
4890
- this.showWarning(`pi-client ${pendingPiClientUpdate.version} is installed. Restart pi-client to load the new runtime.`);
4891
- return;
4892
- }
4893
- writePiClientRuntimeReloadState(reloadStatePath, {
4894
- sessionId: this.sessionManager.getSessionId(),
4895
- sessionDir: this.sessionManager.usesDefaultSessionDir() ? undefined : this.sessionManager.getSessionDir(),
4896
- updateMarkerPath: getPiClientUpdateMarkerPath(),
4897
- });
4898
- this.isShuttingDown = true;
4899
- this.themeController.disableAutoSync();
4900
- await this.ui.terminal.drainInput(1000);
4901
- this.stop();
4902
- await this.runtimeHost.dispose();
4903
- process.exit(PI_CLIENT_RUNTIME_RELOAD_EXIT_CODE);
4904
- }
4905
4969
  this.resetExtensionUI();
4906
4970
  const reloadBox = new Container();
4907
4971
  const borderColor = (s) => theme.fg("border", s);
@@ -4942,8 +5006,8 @@ export class InteractiveMode {
4942
5006
  activeHeader.setExpanded(this.toolOutputExpanded);
4943
5007
  }
4944
5008
  setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
4945
- await this.themeController.applyFromSettings();
4946
5009
  this.applyRuntimeSettings();
5010
+ await this.themeController.applyFromSettings();
4947
5011
  this.setupAutocompleteProvider();
4948
5012
  const runner = this.session.extensionRunner;
4949
5013
  this.setupExtensionShortcuts(runner);
@@ -5055,93 +5119,23 @@ export class InteractiveMode {
5055
5119
  }
5056
5120
  }
5057
5121
  async handleShareCommand() {
5058
- // Check if gh is available and logged in
5059
- try {
5060
- const authResult = spawnSync("gh", ["auth", "status"], { encoding: "utf-8" });
5061
- if (authResult.status !== 0) {
5062
- this.showError("GitHub CLI is not logged in. Run 'gh auth login' first.");
5063
- return;
5064
- }
5065
- }
5066
- catch {
5067
- this.showError("GitHub CLI (gh) is not installed. Install it from https://cli.github.com/");
5068
- return;
5069
- }
5070
- // Export to a temp file
5071
- const tmpFile = path.join(os.tmpdir(), "session.html");
5072
- try {
5073
- await this.session.exportToHtml(tmpFile, { themeName: theme.name });
5074
- }
5075
- catch (error) {
5076
- this.showError(`Failed to export session: ${error instanceof Error ? error.message : "Unknown error"}`);
5077
- return;
5078
- }
5079
- // Show cancellable loader, replacing the editor
5080
- const loader = new BorderedLoader(this.ui, theme, "Creating gist...");
5081
- this.editorContainer.clear();
5082
- this.editorContainer.addChild(loader);
5083
- this.ui.setFocus(loader);
5084
- this.ui.requestRender();
5085
- const restoreEditor = () => {
5086
- loader.dispose();
5087
- this.editorContainer.clear();
5088
- this.editorContainer.addChild(this.editor);
5089
- this.ui.setFocus(this.editor);
5090
- try {
5091
- fs.unlinkSync(tmpFile);
5092
- }
5093
- catch {
5094
- // Ignore cleanup errors
5095
- }
5096
- };
5097
- // Create a secret gist asynchronously
5098
- let proc = null;
5099
- loader.onAbort = () => {
5100
- proc?.kill();
5101
- restoreEditor();
5102
- this.showStatus("Share cancelled");
5103
- };
5104
- try {
5105
- const result = await new Promise((resolve) => {
5106
- proc = spawn("gh", ["gist", "create", "--public=false", tmpFile]);
5107
- let stdout = "";
5108
- let stderr = "";
5109
- proc.stdout?.on("data", (data) => {
5110
- stdout += data.toString();
5111
- });
5112
- proc.stderr?.on("data", (data) => {
5113
- stderr += data.toString();
5114
- });
5115
- proc.on("close", (code) => resolve({ stdout, stderr, code }));
5116
- });
5117
- if (loader.signal.aborted)
5118
- return;
5119
- restoreEditor();
5120
- if (result.code !== 0) {
5121
- const errorMsg = result.stderr?.trim() || "Unknown error";
5122
- this.showError(`Failed to create gist: ${errorMsg}`);
5123
- return;
5124
- }
5125
- // Extract gist ID from the URL returned by gh
5126
- // gh returns something like: https://gist.github.com/username/GIST_ID
5127
- const gistUrl = result.stdout?.trim();
5128
- const gistId = gistUrl?.split("/").pop();
5129
- if (!gistId) {
5130
- this.showError("Failed to parse gist ID from gh output");
5131
- return;
5132
- }
5133
- // Create the preview URL
5134
- const previewUrl = getShareViewerUrl(gistId);
5135
- this.showStatus(`Share URL: ${previewUrl}\nGist: ${gistUrl}`);
5136
- }
5137
- catch (error) {
5138
- if (!loader.signal.aborted) {
5139
- restoreEditor();
5140
- this.showError(`Failed to create gist: ${error instanceof Error ? error.message : "Unknown error"}`);
5141
- }
5142
- }
5122
+ await shareSession({
5123
+ session: this.session,
5124
+ ui: this.ui,
5125
+ editorContainer: this.editorContainer,
5126
+ editor: this.editor,
5127
+ showStatus: (message) => this.showStatus(message),
5128
+ showError: (message) => this.showError(message),
5129
+ });
5143
5130
  }
5144
5131
  async handleCopyCommand(options = {}) {
5132
+ if (options.preferSelection &&
5133
+ this.ui instanceof TuiAltScreen &&
5134
+ !this.ui.getCopyOnSelect() &&
5135
+ this.ui.hasActiveSelection()) {
5136
+ await this.ui.copyActiveSelectionToClipboard();
5137
+ return;
5138
+ }
5145
5139
  const text = this.session.getLastAssistantText();
5146
5140
  if (!text) {
5147
5141
  this.showError("No agent messages to copy yet.");