@averyyy/pi-coding-agent 0.84.2-piclient.1 → 0.84.3-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 (302) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +8 -4
  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-4L3WN2XY.js +2 -0
  8. package/dist/bundle/chunks/chunk-6CYCI3EJ.js +12 -0
  9. package/dist/bundle/chunks/chunk-6R7UI4IY.js +2 -0
  10. package/dist/bundle/chunks/chunk-AXIIZGTV.js +2 -0
  11. package/dist/bundle/chunks/chunk-DRQDKNXA.js +2 -0
  12. package/dist/bundle/chunks/chunk-GMWCTUPB.js +2 -0
  13. package/dist/bundle/chunks/chunk-GPPBJGBU.js +50 -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-LRBSW3NQ.js +11 -0
  17. package/dist/bundle/chunks/chunk-MNAIPA3J.js +2 -0
  18. package/dist/bundle/chunks/chunk-NUHFSC37.js +25 -0
  19. package/dist/bundle/chunks/chunk-PGXDEOVV.js +2 -0
  20. package/dist/bundle/chunks/chunk-PJACZCIR.js +2 -0
  21. package/dist/bundle/chunks/chunk-PRRNXB7S.js +2 -0
  22. package/dist/bundle/chunks/chunk-QI42RBZ4.js +1632 -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/dist-RDWOYWHR.js +13 -0
  29. package/dist/bundle/chunks/github-copilot.js +2 -0
  30. package/dist/bundle/chunks/google-generative-ai-XDKMGBCJ.js +2 -0
  31. package/dist/bundle/chunks/google-vertex-D5FGEO3Y.js +2 -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-YK73UAOZ.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-JD4WAC3R.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/config.d.ts +4 -3
  59. package/dist/config.d.ts.map +1 -1
  60. package/dist/config.js +22 -14
  61. package/dist/config.js.map +1 -1
  62. package/dist/core/agent-session.d.ts +15 -8
  63. package/dist/core/agent-session.d.ts.map +1 -1
  64. package/dist/core/agent-session.js +48 -39
  65. package/dist/core/agent-session.js.map +1 -1
  66. package/dist/core/auth-storage.d.ts.map +1 -1
  67. package/dist/core/auth-storage.js +6 -7
  68. package/dist/core/auth-storage.js.map +1 -1
  69. package/dist/core/defaults.d.ts +1 -0
  70. package/dist/core/defaults.d.ts.map +1 -1
  71. package/dist/core/defaults.js +9 -0
  72. package/dist/core/defaults.js.map +1 -1
  73. package/dist/core/exec.d.ts.map +1 -1
  74. package/dist/core/exec.js +3 -1
  75. package/dist/core/exec.js.map +1 -1
  76. package/dist/core/extensions/index.d.ts +2 -2
  77. package/dist/core/extensions/index.d.ts.map +1 -1
  78. package/dist/core/extensions/index.js +1 -1
  79. package/dist/core/extensions/index.js.map +1 -1
  80. package/dist/core/extensions/loader.d.ts.map +1 -1
  81. package/dist/core/extensions/loader.js +115 -48
  82. package/dist/core/extensions/loader.js.map +1 -1
  83. package/dist/core/extensions/types.d.ts +38 -9
  84. package/dist/core/extensions/types.d.ts.map +1 -1
  85. package/dist/core/extensions/types.js +3 -0
  86. package/dist/core/extensions/types.js.map +1 -1
  87. package/dist/core/footer-data-provider.d.ts +1 -1
  88. package/dist/core/footer-data-provider.d.ts.map +1 -1
  89. package/dist/core/footer-data-provider.js +1 -1
  90. package/dist/core/footer-data-provider.js.map +1 -1
  91. package/dist/core/index.d.ts +1 -0
  92. package/dist/core/index.d.ts.map +1 -1
  93. package/dist/core/index.js +1 -0
  94. package/dist/core/index.js.map +1 -1
  95. package/dist/core/keybindings.d.ts +20 -19
  96. package/dist/core/keybindings.d.ts.map +1 -1
  97. package/dist/core/keybindings.js +26 -5
  98. package/dist/core/keybindings.js.map +1 -1
  99. package/dist/core/model-config.d.ts +5 -0
  100. package/dist/core/model-config.d.ts.map +1 -1
  101. package/dist/core/model-config.js +3 -1
  102. package/dist/core/model-config.js.map +1 -1
  103. package/dist/core/model-resolver.d.ts +1 -0
  104. package/dist/core/model-resolver.d.ts.map +1 -1
  105. package/dist/core/model-resolver.js +14 -8
  106. package/dist/core/model-resolver.js.map +1 -1
  107. package/dist/core/models-store.d.ts.map +1 -1
  108. package/dist/core/models-store.js +2 -1
  109. package/dist/core/models-store.js.map +1 -1
  110. package/dist/core/package-manager.d.ts.map +1 -1
  111. package/dist/core/package-manager.js +22 -14
  112. package/dist/core/package-manager.js.map +1 -1
  113. package/dist/core/pi-client-cli-adapter.d.ts +11 -0
  114. package/dist/core/pi-client-cli-adapter.d.ts.map +1 -0
  115. package/dist/core/pi-client-cli-adapter.js +91 -0
  116. package/dist/core/pi-client-cli-adapter.js.map +1 -0
  117. package/dist/core/pi-manifest.d.ts.map +1 -1
  118. package/dist/core/pi-manifest.js +2 -1
  119. package/dist/core/pi-manifest.js.map +1 -1
  120. package/dist/core/pi-server-client.d.ts +0 -1
  121. package/dist/core/pi-server-client.d.ts.map +1 -1
  122. package/dist/core/pi-server-client.js +148 -65
  123. package/dist/core/pi-server-client.js.map +1 -1
  124. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  125. package/dist/core/remote-catalog-provider.js +2 -1
  126. package/dist/core/remote-catalog-provider.js.map +1 -1
  127. package/dist/core/resource-loader.d.ts.map +1 -1
  128. package/dist/core/resource-loader.js +3 -2
  129. package/dist/core/resource-loader.js.map +1 -1
  130. package/dist/core/sdk.d.ts +2 -2
  131. package/dist/core/sdk.d.ts.map +1 -1
  132. package/dist/core/sdk.js +10 -3
  133. package/dist/core/sdk.js.map +1 -1
  134. package/dist/core/session-export.d.ts +4 -0
  135. package/dist/core/session-export.d.ts.map +1 -0
  136. package/dist/core/session-export.js +32 -0
  137. package/dist/core/session-export.js.map +1 -0
  138. package/dist/core/session-manager.d.ts.map +1 -1
  139. package/dist/core/session-manager.js +2 -1
  140. package/dist/core/session-manager.js.map +1 -1
  141. package/dist/core/settings-diagnostics.d.ts +9 -0
  142. package/dist/core/settings-diagnostics.d.ts.map +1 -0
  143. package/dist/core/settings-diagnostics.js +21 -0
  144. package/dist/core/settings-diagnostics.js.map +1 -0
  145. package/dist/core/settings-manager.d.ts +9 -0
  146. package/dist/core/settings-manager.d.ts.map +1 -1
  147. package/dist/core/settings-manager.js +52 -10
  148. package/dist/core/settings-manager.js.map +1 -1
  149. package/dist/core/skills.d.ts.map +1 -1
  150. package/dist/core/skills.js +51 -33
  151. package/dist/core/skills.js.map +1 -1
  152. package/dist/core/slash-commands.d.ts.map +1 -1
  153. package/dist/core/slash-commands.js +2 -1
  154. package/dist/core/slash-commands.js.map +1 -1
  155. package/dist/core/system-prompt.d.ts.map +1 -1
  156. package/dist/core/system-prompt.js +11 -2
  157. package/dist/core/system-prompt.js.map +1 -1
  158. package/dist/core/tools/bash.d.ts +13 -1
  159. package/dist/core/tools/bash.d.ts.map +1 -1
  160. package/dist/core/tools/bash.js +30 -14
  161. package/dist/core/tools/bash.js.map +1 -1
  162. package/dist/core/tools/edit-diff.d.ts +0 -5
  163. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  164. package/dist/core/tools/edit-diff.js +2 -5
  165. package/dist/core/tools/edit-diff.js.map +1 -1
  166. package/dist/core/tools/edit.d.ts.map +1 -1
  167. package/dist/core/tools/edit.js +20 -4
  168. package/dist/core/tools/edit.js.map +1 -1
  169. package/dist/core/tools/index.d.ts +4 -1
  170. package/dist/core/tools/index.d.ts.map +1 -1
  171. package/dist/core/tools/index.js +18 -1
  172. package/dist/core/tools/index.js.map +1 -1
  173. package/dist/core/tools/powershell.d.ts +16 -0
  174. package/dist/core/tools/powershell.d.ts.map +1 -0
  175. package/dist/core/tools/powershell.js +39 -0
  176. package/dist/core/tools/powershell.js.map +1 -0
  177. package/dist/core/trust-manager.d.ts.map +1 -1
  178. package/dist/core/trust-manager.js +2 -1
  179. package/dist/core/trust-manager.js.map +1 -1
  180. package/dist/extensions/llama/client.d.ts +6 -0
  181. package/dist/extensions/llama/client.d.ts.map +1 -1
  182. package/dist/extensions/llama/client.js +7 -0
  183. package/dist/extensions/llama/client.js.map +1 -1
  184. package/dist/extensions/llama/index.d.ts.map +1 -1
  185. package/dist/extensions/llama/index.js +2 -0
  186. package/dist/extensions/llama/index.js.map +1 -1
  187. package/dist/extensions/llama/provider.d.ts +3 -1
  188. package/dist/extensions/llama/provider.d.ts.map +1 -1
  189. package/dist/extensions/llama/provider.js +29 -4
  190. package/dist/extensions/llama/provider.js.map +1 -1
  191. package/dist/index.d.ts +2 -2
  192. package/dist/index.d.ts.map +1 -1
  193. package/dist/index.js +3 -3
  194. package/dist/index.js.map +1 -1
  195. package/dist/main.d.ts +4 -0
  196. package/dist/main.d.ts.map +1 -1
  197. package/dist/main.js +20 -15
  198. package/dist/main.js.map +1 -1
  199. package/dist/migrations.d.ts.map +1 -1
  200. package/dist/migrations.js +4 -3
  201. package/dist/migrations.js.map +1 -1
  202. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  203. package/dist/modes/interactive/components/bash-execution.js +2 -1
  204. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  205. package/dist/modes/interactive/components/model-selector.d.ts +9 -3
  206. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  207. package/dist/modes/interactive/components/model-selector.js +56 -12
  208. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  209. package/dist/modes/interactive/components/settings-selector.d.ts +7 -2
  210. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  211. package/dist/modes/interactive/components/settings-selector.js +118 -56
  212. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  213. package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
  214. package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
  215. package/dist/modes/interactive/components/settings-submenu.js +165 -0
  216. package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
  217. package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
  218. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  219. package/dist/modes/interactive/components/thinking-selector.js +74 -17
  220. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  221. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  222. package/dist/modes/interactive/external-editor.js +2 -1
  223. package/dist/modes/interactive/external-editor.js.map +1 -1
  224. package/dist/modes/interactive/interactive-mode.d.ts +13 -12
  225. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  226. package/dist/modes/interactive/interactive-mode.js +196 -226
  227. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  228. package/dist/modes/interactive/session-share.d.ts +16 -0
  229. package/dist/modes/interactive/session-share.d.ts.map +1 -0
  230. package/dist/modes/interactive/session-share.js +188 -0
  231. package/dist/modes/interactive/session-share.js.map +1 -0
  232. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  233. package/dist/modes/interactive/theme/theme.js +4 -3
  234. package/dist/modes/interactive/theme/theme.js.map +1 -1
  235. package/dist/modes/json-event.d.ts +16 -13
  236. package/dist/modes/json-event.d.ts.map +1 -1
  237. package/dist/modes/json-event.js +20 -6
  238. package/dist/modes/json-event.js.map +1 -1
  239. package/dist/package-manager-cli.d.ts +1 -0
  240. package/dist/package-manager-cli.d.ts.map +1 -1
  241. package/dist/package-manager-cli.js +186 -2
  242. package/dist/package-manager-cli.js.map +1 -1
  243. package/dist/utils/child-process.d.ts.map +1 -1
  244. package/dist/utils/child-process.js +8 -3
  245. package/dist/utils/child-process.js.map +1 -1
  246. package/dist/utils/frontmatter.d.ts.map +1 -1
  247. package/dist/utils/frontmatter.js +2 -1
  248. package/dist/utils/frontmatter.js.map +1 -1
  249. package/dist/utils/management-http.d.ts +5 -3
  250. package/dist/utils/management-http.d.ts.map +1 -1
  251. package/dist/utils/management-http.js +14 -10
  252. package/dist/utils/management-http.js.map +1 -1
  253. package/dist/utils/shell.d.ts +3 -0
  254. package/dist/utils/shell.d.ts.map +1 -1
  255. package/dist/utils/shell.js +21 -7
  256. package/dist/utils/shell.js.map +1 -1
  257. package/dist/utils/syntax-highlight.d.ts +1 -0
  258. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  259. package/dist/utils/syntax-highlight.js +60 -1
  260. package/dist/utils/syntax-highlight.js.map +1 -1
  261. package/dist/utils/text.d.ts +8 -0
  262. package/dist/utils/text.d.ts.map +1 -0
  263. package/dist/utils/text.js +9 -0
  264. package/dist/utils/text.js.map +1 -0
  265. package/dist/utils/version-check.d.ts.map +1 -1
  266. package/dist/utils/version-check.js +2 -1
  267. package/dist/utils/version-check.js.map +1 -1
  268. package/docs/compaction.md +15 -0
  269. package/docs/custom-provider.md +5 -2
  270. package/docs/environment-variables.md +8 -8
  271. package/docs/extensions.md +17 -7
  272. package/docs/json.md +7 -2
  273. package/docs/keybindings.md +8 -8
  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/rpc.md +9 -3
  278. package/docs/sdk.md +7 -2
  279. package/docs/settings.md +11 -3
  280. package/docs/skills.md +4 -3
  281. package/docs/terminal-setup.md +11 -9
  282. package/docs/usage.md +6 -2
  283. package/docs/windows.md +24 -2
  284. package/examples/extensions/border-status-editor.ts +1 -1
  285. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  286. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  287. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  288. package/examples/extensions/git-checkpoint.ts +2 -2
  289. package/examples/extensions/gondolin/package-lock.json +2 -2
  290. package/examples/extensions/gondolin/package.json +1 -1
  291. package/examples/extensions/notify.ts +3 -1
  292. package/examples/extensions/sandbox/package-lock.json +2 -2
  293. package/examples/extensions/sandbox/package.json +1 -1
  294. package/examples/extensions/subagent/README.md +1 -1
  295. package/examples/extensions/subagent/index.ts +6 -1
  296. package/examples/extensions/titlebar-spinner.ts +1 -1
  297. package/examples/extensions/with-deps/package-lock.json +2 -2
  298. package/examples/extensions/with-deps/package.json +1 -1
  299. package/examples/rpc-extension-ui.ts +1 -1
  300. package/examples/sdk/06-extensions.ts +1 -1
  301. package/examples/sdk/README.md +1 -1
  302. package/package.json +12 -14
@@ -9,18 +9,20 @@ import * as path from "node:path";
9
9
  import * as TuiLayouts from "@earendil-works/pi-tui";
10
10
  import { CombinedAutocompleteProvider, Container, fuzzyFilter, getCapabilities, hyperlink, Markdown, matchesKey, ProcessTerminal, Spacer, setKeybindings, Text, TruncatedText, TuiAltScreen, TuiMainScreen, visibleWidth, } from "@earendil-works/pi-tui";
11
11
  import chalk from "chalk";
12
- import { spawn, 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);
@@ -230,11 +241,6 @@ export class InteractiveMode {
230
241
  chatContainer;
231
242
  documentContainer;
232
243
  transcriptScrollView;
233
- transcriptLazyEntries = [];
234
- loadedTranscriptEntryIds = new Set();
235
- transcriptWindowInitialized = false;
236
- transcriptLatestCompactionId;
237
- loadingEarlierTranscript = false;
238
244
  fullscreenLayoutRoot;
239
245
  pendingMessagesContainer;
240
246
  statusContainer;
@@ -471,6 +477,15 @@ export class InteractiveMode {
471
477
  }));
472
478
  };
473
479
  }
480
+ const thinkingCommand = slashCommands.find((command) => command.name === "thinking");
481
+ if (thinkingCommand) {
482
+ thinkingCommand.getArgumentCompletions = (prefix) => {
483
+ return createFuzzyAutocompleteItems(this.session.getAvailableThinkingLevels(), prefix, (level) => level, (level) => ({
484
+ value: level,
485
+ label: level,
486
+ }));
487
+ };
488
+ }
474
489
  const loginCommand = slashCommands.find((command) => command.name === "login");
475
490
  if (loginCommand) {
476
491
  loginCommand.getArgumentCompletions = (prefix) => {
@@ -647,7 +662,6 @@ export class InteractiveMode {
647
662
  follow: "end",
648
663
  primary: true,
649
664
  overscroll: "chain",
650
- onScroll: (scrollTop) => this.handleTranscriptScroll(scrollTop),
651
665
  scrollbar: this.settingsManager.getFullscreenScrollbar(),
652
666
  scrollbarStyle: (text) => theme.bg("scrollbarThumb", text),
653
667
  });
@@ -756,6 +770,14 @@ export class InteractiveMode {
756
770
  });
757
771
  // Initialize available provider count for footer display
758
772
  await this.updateAvailableProviderCount();
773
+ // Flush the completed startup state before loading the remaining syntax grammars.
774
+ this.ui.renderNow();
775
+ void loadAllHighlightLanguages().then(() => {
776
+ if (!this.isInitialized)
777
+ return;
778
+ this.ui.invalidate();
779
+ this.ui.requestRender();
780
+ });
759
781
  }
760
782
  /**
761
783
  * Update terminal title with session name and cwd.
@@ -785,7 +807,7 @@ export class InteractiveMode {
785
807
  .finally(() => clearTimeout(timeout));
786
808
  }
787
809
  // Start version check asynchronously
788
- checkForNewPiVersion(this.version).then((newRelease) => {
810
+ checkForNewPiVersion(process.env.PI_CLIENT_VERSION ?? this.version).then((newRelease) => {
789
811
  if (newRelease) {
790
812
  this.showNewVersionNotification(newRelease);
791
813
  }
@@ -811,7 +833,18 @@ export class InteractiveMode {
811
833
  }
812
834
  });
813
835
  // Show startup warnings
814
- const { migratedProviders, modelFallbackMessage, initialMessage, initialImages, initialMessages } = this.options;
836
+ const { migratedProviders, startupDiagnostics, modelFallbackMessage, initialMessage, initialImages, initialMessages, } = this.options;
837
+ for (const diagnostic of startupDiagnostics ?? []) {
838
+ if (diagnostic.type === "error") {
839
+ this.showError(diagnostic.message);
840
+ }
841
+ else if (diagnostic.type === "warning") {
842
+ this.showWarning(diagnostic.message);
843
+ }
844
+ else {
845
+ this.showStatus(diagnostic.message);
846
+ }
847
+ }
815
848
  if (migratedProviders && migratedProviders.length > 0) {
816
849
  this.showWarning(`Migrated credentials to auth.json: ${migratedProviders.join(", ")}`);
817
850
  }
@@ -966,14 +999,13 @@ export class InteractiveMode {
966
999
  // Extension System
967
1000
  // =========================================================================
968
1001
  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)}`;
1002
+ const home = os.homedir();
1003
+ let result = p;
1004
+ // Replace home directory with ~
1005
+ if (result.startsWith(home)) {
1006
+ result = `~${result.slice(home.length)}`;
975
1007
  }
976
- return p;
1008
+ return result;
977
1009
  }
978
1010
  formatExtensionDisplayPath(path) {
979
1011
  let result = this.formatDisplayPath(path);
@@ -1660,15 +1692,6 @@ export class InteractiveMode {
1660
1692
  }
1661
1693
  this.ui.requestRender();
1662
1694
  }
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
1695
  setHiddenThinkingLabel(label) {
1673
1696
  this.hiddenThinkingLabel = label ?? this.defaultHiddenThinkingLabel;
1674
1697
  for (const child of this.chatContainer.children) {
@@ -2285,7 +2308,6 @@ export class InteractiveMode {
2285
2308
  this.defaultEditor.onAction("app.session.tree", () => this.showTreeSelector());
2286
2309
  this.defaultEditor.onAction("app.session.fork", () => this.showUserMessageSelector());
2287
2310
  this.defaultEditor.onAction("app.session.resume", () => this.showSessionSelector());
2288
- this.defaultEditor.onAction("app.session.loadEarlier", () => this.loadEarlierTranscript());
2289
2311
  this.defaultEditor.onChange = (text) => {
2290
2312
  const wasBashMode = this.isBashMode;
2291
2313
  this.isBashMode = text.trimStart().startsWith("!");
@@ -2364,6 +2386,12 @@ export class InteractiveMode {
2364
2386
  await this.handleModelCommand(searchTerm);
2365
2387
  return;
2366
2388
  }
2389
+ if (text === "/thinking" || text.startsWith("/thinking ")) {
2390
+ const searchTerm = text.startsWith("/thinking ") ? text.slice(10).trim() : undefined;
2391
+ this.editor.setText("");
2392
+ this.handleThinkingCommand(searchTerm);
2393
+ return;
2394
+ }
2367
2395
  if (text === "/export" || text.startsWith("/export ")) {
2368
2396
  await this.handleExportCommand(text);
2369
2397
  this.editor.setText("");
@@ -2451,12 +2479,6 @@ export class InteractiveMode {
2451
2479
  await this.handleReloadCommand();
2452
2480
  return;
2453
2481
  }
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
2482
  if (text === "/debug") {
2461
2483
  this.handleDebugCommand();
2462
2484
  this.editor.setText("");
@@ -2702,6 +2724,10 @@ export class InteractiveMode {
2702
2724
  break;
2703
2725
  }
2704
2726
  case "agent_end":
2727
+ if (this.settingsManager.getShowTerminalProgress()) {
2728
+ this.ui.terminal.setProgress(false);
2729
+ }
2730
+ this.clearStatusIndicator("working");
2705
2731
  if (this.streamingComponent) {
2706
2732
  this.chatContainer.removeChild(this.streamingComponent);
2707
2733
  this.streamingComponent = undefined;
@@ -2711,12 +2737,7 @@ export class InteractiveMode {
2711
2737
  this.ui.requestRender();
2712
2738
  break;
2713
2739
  case "agent_settled":
2714
- if (this.settingsManager.getShowTerminalProgress()) {
2715
- this.ui.terminal.setProgress(false);
2716
- }
2717
- this.clearStatusIndicator("working");
2718
2740
  await this.checkShutdownRequested();
2719
- this.ui.requestRender();
2720
2741
  break;
2721
2742
  case "compaction_start": {
2722
2743
  if (this.settingsManager.getShowTerminalProgress()) {
@@ -2749,7 +2770,21 @@ export class InteractiveMode {
2749
2770
  }
2750
2771
  }
2751
2772
  else if (event.result) {
2752
- this.rebuildChatFromMessages();
2773
+ const entries = this.sessionManager.buildContextEntries();
2774
+ if (entries[0]?.type !== "compaction") {
2775
+ throw new Error("Completed compaction is missing from the session context");
2776
+ }
2777
+ this.chatContainer.clear();
2778
+ // The latest compaction is prepended for model context; append it below at its chronological position.
2779
+ this.renderSessionEntries(entries.slice(1));
2780
+ this.addMessageToChat(createCompactionSummaryMessage(event.result.summary, event.result.tokensBefore, new Date().toISOString()));
2781
+ if (event.result.usage) {
2782
+ this.addCompactionCostNotice({
2783
+ type: "compaction_cost",
2784
+ kind: "compaction",
2785
+ usage: event.result.usage,
2786
+ });
2787
+ }
2753
2788
  this.footer.invalidate();
2754
2789
  }
2755
2790
  else if (event.errorMessage) {
@@ -2761,7 +2796,6 @@ export class InteractiveMode {
2761
2796
  this.chatContainer.addChild(new Text(theme.fg("error", event.errorMessage), 1, 0));
2762
2797
  }
2763
2798
  }
2764
- this.restoreWorkingIndicator();
2765
2799
  void this.flushCompactionQueue({ willRetry: event.willRetry });
2766
2800
  this.ui.requestRender();
2767
2801
  break;
@@ -2787,7 +2821,6 @@ export class InteractiveMode {
2787
2821
  if (!event.success) {
2788
2822
  this.showError(`Retry failed after ${event.attempt} attempts: ${event.finalError || "Unknown error"}`);
2789
2823
  }
2790
- this.restoreWorkingIndicator();
2791
2824
  this.ui.requestRender();
2792
2825
  break;
2793
2826
  }
@@ -2973,6 +3006,10 @@ export class InteractiveMode {
2973
3006
  this.addCustomEntryToChat(item);
2974
3007
  continue;
2975
3008
  }
3009
+ if (isCompactionCostNotice(item)) {
3010
+ this.addCompactionCostNotice(item);
3011
+ continue;
3012
+ }
2976
3013
  const message = item;
2977
3014
  // Assistant messages need special handling for tool calls
2978
3015
  if (message.role === "assistant") {
@@ -3030,8 +3067,8 @@ export class InteractiveMode {
3030
3067
  this.ui.requestRender();
3031
3068
  }
3032
3069
  /**
3033
- * Render session entries to chat. Used for initial load and transcript rebuilds.
3034
- * @param entries Active-branch session entries to render
3070
+ * Render session entries to chat. Used for initial load and rebuild after compaction.
3071
+ * @param entries Compaction-aware session entries to render
3035
3072
  * @param options.updateFooter Update footer state
3036
3073
  * @param options.populateHistory Add user messages to editor history
3037
3074
  */
@@ -3040,10 +3077,28 @@ export class InteractiveMode {
3040
3077
  if (entry.type === "custom") {
3041
3078
  return [entry];
3042
3079
  }
3043
- return sessionEntryToContextMessages(entry);
3080
+ const messages = sessionEntryToContextMessages(entry);
3081
+ if ((entry.type === "compaction" || entry.type === "branch_summary") && entry.usage && messages.length > 0) {
3082
+ return [...messages, { type: "compaction_cost", kind: entry.type, usage: entry.usage }];
3083
+ }
3084
+ return messages;
3044
3085
  });
3045
3086
  this.renderSessionItems(items, options);
3046
3087
  }
3088
+ /**
3089
+ * Render billing usage for a compaction or branch summary. The notice is derived
3090
+ * from persisted summary usage and is not stored as a separate session entry.
3091
+ */
3092
+ addCompactionCostNotice(notice) {
3093
+ if (!this.settingsManager.getShowCacheMissNotices())
3094
+ return;
3095
+ const { usage } = notice;
3096
+ const tokens = usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
3097
+ const cost = usage.cost.total >= 0.01 ? ` (~$${usage.cost.total.toFixed(2)})` : "";
3098
+ const label = notice.kind === "compaction" ? "Compaction" : "Branch summary";
3099
+ this.chatContainer.addChild(new Spacer(1));
3100
+ this.chatContainer.addChild(new Text(theme.fg("warning", `${label}: ${formatTokens(tokens)} tokens billed${cost}`), 1, 0));
3101
+ }
3047
3102
  /**
3048
3103
  * Show a transcript notice when a completed assistant message paid for a
3049
3104
  * significant cache miss. Only states observable facts: the miss itself,
@@ -3073,66 +3128,9 @@ export class InteractiveMode {
3073
3128
  this.chatContainer.addChild(new Spacer(1));
3074
3129
  this.chatContainer.addChild(new Text(text, 1, 0));
3075
3130
  }
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
3131
  renderInitialMessages() {
3134
- const entries = this.getTranscriptEntriesForRender(true);
3135
- this.renderTranscriptEntries(entries, {
3132
+ const entries = this.sessionManager.buildContextEntries();
3133
+ this.renderSessionEntries(entries, {
3136
3134
  updateFooter: true,
3137
3135
  populateHistory: true,
3138
3136
  });
@@ -3168,7 +3166,7 @@ export class InteractiveMode {
3168
3166
  }
3169
3167
  rebuildChatFromMessages() {
3170
3168
  this.chatContainer.clear();
3171
- this.renderTranscriptEntries(this.getTranscriptEntriesForRender());
3169
+ this.renderSessionEntries(this.sessionManager.buildContextEntries());
3172
3170
  }
3173
3171
  // =========================================================================
3174
3172
  // Key handlers
@@ -3737,8 +3735,14 @@ export class InteractiveMode {
3737
3735
  showSettingsSelector() {
3738
3736
  this.showSelector((done) => {
3739
3737
  let selector;
3738
+ const defaultProvider = this.settingsManager.getDefaultProvider();
3739
+ const defaultModelId = this.settingsManager.getDefaultModel();
3740
+ const defaultModel = defaultProvider && defaultModelId ? `${defaultProvider}/${defaultModelId}` : "not set";
3740
3741
  selector = new SettingsSelectorComponent({
3741
3742
  autoCompact: this.session.autoCompactionEnabled,
3743
+ defaultModel,
3744
+ currentModel: this.session.model,
3745
+ availableDefaultModels: this.session.modelRuntime.getAvailableSnapshot(),
3742
3746
  showImages: this.settingsManager.getShowImages(),
3743
3747
  imageWidthCells: this.settingsManager.getImageWidthCells(),
3744
3748
  autoResizeImages: this.settingsManager.getImageAutoResize(),
@@ -3748,8 +3752,9 @@ export class InteractiveMode {
3748
3752
  followUpMode: this.session.followUpMode,
3749
3753
  transport: this.settingsManager.getTransport(),
3750
3754
  httpIdleTimeoutMs: this.settingsManager.getHttpIdleTimeoutMs(),
3751
- thinkingLevel: this.session.thinkingLevel,
3752
- availableThinkingLevels: this.session.getAvailableThinkingLevels(),
3755
+ thinkingLevel: this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL,
3756
+ availableThinkingLevels: [...THINKING_LEVEL_OPTIONS],
3757
+ modelThinkingLevels: this.settingsManager.getAllModelThinkingLevels(),
3753
3758
  currentTheme: this.themeController.getThemeSelection() || "dark",
3754
3759
  terminalTheme: this.themeController.getTerminalTheme(),
3755
3760
  availableThemes: getAvailableThemes(),
@@ -3818,10 +3823,26 @@ export class InteractiveMode {
3818
3823
  configureHttpDispatcher(timeoutMs);
3819
3824
  this.showStatus(`HTTP idle timeout: ${formatHttpIdleTimeoutMs(timeoutMs)}`);
3820
3825
  },
3821
- onThinkingLevelChange: (level) => {
3822
- this.session.setThinkingLevel(level);
3823
- this.footer.invalidate();
3824
- this.updateEditorBorderColor();
3826
+ onModelThinkingLevelChange: (provider, modelId, level) => {
3827
+ this.settingsManager.setModelThinkingLevel(provider, modelId, level);
3828
+ // If the override is for the current model, apply it to the session too
3829
+ const current = this.session.model;
3830
+ if (current && current.provider === provider && current.id === modelId) {
3831
+ this.session.setThinkingLevel(level);
3832
+ this.footer.invalidate();
3833
+ this.updateEditorBorderColor();
3834
+ }
3835
+ },
3836
+ onModelThinkingLevelRemove: (provider, modelId) => {
3837
+ this.settingsManager.removeModelThinkingLevel(provider, modelId);
3838
+ // If the override was for the current model, revert to global default
3839
+ const current = this.session.model;
3840
+ if (current && current.provider === provider && current.id === modelId) {
3841
+ const globalDefault = this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
3842
+ this.session.setThinkingLevel(globalDefault);
3843
+ this.footer.invalidate();
3844
+ this.updateEditorBorderColor();
3845
+ }
3825
3846
  },
3826
3847
  onThemeChange: (themeSetting) => {
3827
3848
  this.settingsManager.setTheme(themeSetting);
@@ -3942,6 +3963,44 @@ export class InteractiveMode {
3942
3963
  return { component: selector, focus: selector.getSettingsList() };
3943
3964
  });
3944
3965
  }
3966
+ handleThinkingCommand(searchTerm) {
3967
+ const availableLevels = this.session.getAvailableThinkingLevels();
3968
+ if (!searchTerm) {
3969
+ this.showThinkingSelector();
3970
+ return;
3971
+ }
3972
+ const normalized = searchTerm.trim().toLowerCase();
3973
+ const level = availableLevels.find((candidate) => candidate.toLowerCase() === normalized);
3974
+ if (!level) {
3975
+ this.showError(`Unknown thinking level "${searchTerm}". Available levels: ${availableLevels.join(", ")}.`);
3976
+ return;
3977
+ }
3978
+ this.selectThinkingLevel(level, false);
3979
+ }
3980
+ selectThinkingLevel(level, persist) {
3981
+ try {
3982
+ this.session.setThinkingLevel(level, { persist });
3983
+ this.footer.invalidate();
3984
+ this.updateEditorBorderColor();
3985
+ this.showStatus(persist ? `Default thinking level: ${level}` : `Thinking level: ${level}`);
3986
+ }
3987
+ catch (error) {
3988
+ this.showError(error instanceof Error ? error.message : String(error));
3989
+ }
3990
+ }
3991
+ showThinkingSelector() {
3992
+ this.showSelector((done) => {
3993
+ const selectLevel = (level, persist) => {
3994
+ this.selectThinkingLevel(level, persist);
3995
+ done();
3996
+ };
3997
+ const selector = new ThinkingSelectorComponent(this.session.thinkingLevel ?? DEFAULT_THINKING_LEVEL, this.session.getAvailableThinkingLevels(), (level) => selectLevel(level, false), () => {
3998
+ done();
3999
+ this.ui.requestRender();
4000
+ }, (level) => selectLevel(level, true), this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);
4001
+ return { component: selector, focus: selector };
4002
+ });
4003
+ }
3945
4004
  async handleModelCommand(searchTerm) {
3946
4005
  if (!searchTerm) {
3947
4006
  this.showModelSelector();
@@ -3950,7 +4009,7 @@ export class InteractiveMode {
3950
4009
  const model = await this.findExactModelMatch(searchTerm);
3951
4010
  if (model) {
3952
4011
  try {
3953
- await this.session.setModel(model);
4012
+ await this.session.setModel(model, { persist: false });
3954
4013
  this.footer.invalidate();
3955
4014
  this.updateEditorBorderColor();
3956
4015
  this.showStatus(`Model: ${model.id}`);
@@ -4079,13 +4138,13 @@ export class InteractiveMode {
4079
4138
  }
4080
4139
  showModelSelector(initialSearchInput) {
4081
4140
  this.showSelector((done) => {
4082
- const selector = new ModelSelectorComponent(this.ui, this.session.model, this.settingsManager, this.session.modelRuntime, this.session.scopedModels, async (model) => {
4141
+ const selectModel = async (model, persist) => {
4083
4142
  try {
4084
- await this.session.setModel(model);
4143
+ await this.session.setModel(model, { persist });
4085
4144
  this.footer.invalidate();
4086
4145
  this.updateEditorBorderColor();
4087
4146
  done();
4088
- this.showStatus(`Model: ${model.id}`);
4147
+ this.showStatus(persist ? `Default model: ${model.provider}/${model.id}` : `Model: ${model.id}`);
4089
4148
  void this.maybeWarnAboutAnthropicSubscriptionAuth(model);
4090
4149
  this.checkDaxnutsEasterEgg(model);
4091
4150
  }
@@ -4093,10 +4152,13 @@ export class InteractiveMode {
4093
4152
  done();
4094
4153
  this.showError(error instanceof Error ? error.message : String(error));
4095
4154
  }
4096
- }, () => {
4155
+ };
4156
+ const defaultProvider = this.settingsManager.getDefaultProvider();
4157
+ const defaultModel = this.settingsManager.getDefaultModel();
4158
+ const selector = new ModelSelectorComponent(this.ui, this.session.model, this.session.modelRuntime, this.session.scopedModels, (model) => selectModel(model, false), () => {
4097
4159
  done();
4098
4160
  this.ui.requestRender();
4099
- }, initialSearchInput);
4161
+ }, initialSearchInput, (model) => selectModel(model, true), defaultProvider && defaultModel ? { provider: defaultProvider, id: defaultModel } : undefined);
4100
4162
  return { component: selector, focus: selector, dispose: () => selector.dispose() };
4101
4163
  });
4102
4164
  }
@@ -4652,7 +4714,11 @@ export class InteractiveMode {
4652
4714
  if (isUnknownModel(previousModel)) {
4653
4715
  const availableModels = this.session.modelRuntime.getAvailableSnapshot();
4654
4716
  const providerModels = availableModels.filter((model) => model.provider === providerId);
4655
- if (!hasDefaultModelProvider(providerId)) {
4717
+ // Matches LLAMA_PROVIDER_ID from extensions/llama/provider.ts; kept inline to avoid coupling interactive mode to the built-in extension.
4718
+ if (providerId === "llama.cpp") {
4719
+ selectionError = llamaCppPostLoginGuidance(actionLabel, providerModels.length);
4720
+ }
4721
+ else if (!hasDefaultModelProvider(providerId)) {
4656
4722
  selectionError = `${actionLabel}, but no default model is configured for provider "${providerId}". Use /model to select a model.`;
4657
4723
  }
4658
4724
  else if (providerModels.length === 0) {
@@ -4666,7 +4732,7 @@ export class InteractiveMode {
4666
4732
  }
4667
4733
  else {
4668
4734
  try {
4669
- await this.session.setModel(selectedModel);
4735
+ await this.session.setModel(selectedModel, { persist: true });
4670
4736
  }
4671
4737
  catch (error) {
4672
4738
  selectedModel = undefined;
@@ -4883,25 +4949,6 @@ export class InteractiveMode {
4883
4949
  this.showWarning("Wait for compaction to finish before reloading.");
4884
4950
  return;
4885
4951
  }
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
4952
  this.resetExtensionUI();
4906
4953
  const reloadBox = new Container();
4907
4954
  const borderColor = (s) => theme.fg("border", s);
@@ -5055,91 +5102,14 @@ export class InteractiveMode {
5055
5102
  }
5056
5103
  }
5057
5104
  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
- }
5105
+ await shareSession({
5106
+ session: this.session,
5107
+ ui: this.ui,
5108
+ editorContainer: this.editorContainer,
5109
+ editor: this.editor,
5110
+ showStatus: (message) => this.showStatus(message),
5111
+ showError: (message) => this.showError(message),
5112
+ });
5143
5113
  }
5144
5114
  async handleCopyCommand(options = {}) {
5145
5115
  const text = this.session.getLastAssistantText();