@astralyn/pi 0.84.9 → 0.84.10

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 (263) hide show
  1. package/CHANGELOG.md +11 -2
  2. package/README.md +3 -2
  3. package/dist/cli/args.d.ts +1 -0
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +29 -9
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/file-processor.d.ts.map +1 -1
  8. package/dist/cli/file-processor.js +2 -1
  9. package/dist/cli/file-processor.js.map +1 -1
  10. package/dist/config.d.ts +4 -3
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/config.js +22 -14
  13. package/dist/config.js.map +1 -1
  14. package/dist/core/agent-session.d.ts +26 -7
  15. package/dist/core/agent-session.d.ts.map +1 -1
  16. package/dist/core/agent-session.js +83 -64
  17. package/dist/core/agent-session.js.map +1 -1
  18. package/dist/core/auth-storage.d.ts.map +1 -1
  19. package/dist/core/auth-storage.js +6 -7
  20. package/dist/core/auth-storage.js.map +1 -1
  21. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  22. package/dist/core/compaction/branch-summarization.js +3 -0
  23. package/dist/core/compaction/branch-summarization.js.map +1 -1
  24. package/dist/core/compaction/compaction.d.ts +4 -3
  25. package/dist/core/compaction/compaction.d.ts.map +1 -1
  26. package/dist/core/compaction/compaction.js +41 -32
  27. package/dist/core/compaction/compaction.js.map +1 -1
  28. package/dist/core/compaction-controller.d.ts +2 -1
  29. package/dist/core/compaction-controller.d.ts.map +1 -1
  30. package/dist/core/compaction-controller.js +64 -21
  31. package/dist/core/compaction-controller.js.map +1 -1
  32. package/dist/core/defaults.d.ts +1 -0
  33. package/dist/core/defaults.d.ts.map +1 -1
  34. package/dist/core/defaults.js +9 -0
  35. package/dist/core/defaults.js.map +1 -1
  36. package/dist/core/extensions/index.d.ts +2 -2
  37. package/dist/core/extensions/index.d.ts.map +1 -1
  38. package/dist/core/extensions/index.js +1 -1
  39. package/dist/core/extensions/index.js.map +1 -1
  40. package/dist/core/extensions/loader.d.ts.map +1 -1
  41. package/dist/core/extensions/loader.js +115 -48
  42. package/dist/core/extensions/loader.js.map +1 -1
  43. package/dist/core/extensions/types.d.ts +38 -9
  44. package/dist/core/extensions/types.d.ts.map +1 -1
  45. package/dist/core/extensions/types.js +3 -0
  46. package/dist/core/extensions/types.js.map +1 -1
  47. package/dist/core/footer-data-provider.d.ts +1 -1
  48. package/dist/core/footer-data-provider.js +1 -1
  49. package/dist/core/footer-data-provider.js.map +1 -1
  50. package/dist/core/keybindings.d.ts +20 -19
  51. package/dist/core/keybindings.d.ts.map +1 -1
  52. package/dist/core/keybindings.js +26 -5
  53. package/dist/core/keybindings.js.map +1 -1
  54. package/dist/core/model-config.d.ts +5 -0
  55. package/dist/core/model-config.d.ts.map +1 -1
  56. package/dist/core/model-config.js +3 -1
  57. package/dist/core/model-config.js.map +1 -1
  58. package/dist/core/model-resolver.d.ts +1 -0
  59. package/dist/core/model-resolver.d.ts.map +1 -1
  60. package/dist/core/model-resolver.js +14 -8
  61. package/dist/core/model-resolver.js.map +1 -1
  62. package/dist/core/models-store.d.ts.map +1 -1
  63. package/dist/core/models-store.js +2 -1
  64. package/dist/core/models-store.js.map +1 -1
  65. package/dist/core/package-manager.d.ts.map +1 -1
  66. package/dist/core/package-manager.js +22 -14
  67. package/dist/core/package-manager.js.map +1 -1
  68. package/dist/core/pi-manifest.d.ts.map +1 -1
  69. package/dist/core/pi-manifest.js +2 -1
  70. package/dist/core/pi-manifest.js.map +1 -1
  71. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  72. package/dist/core/remote-catalog-provider.js +2 -1
  73. package/dist/core/remote-catalog-provider.js.map +1 -1
  74. package/dist/core/resource-loader.d.ts.map +1 -1
  75. package/dist/core/resource-loader.js +3 -2
  76. package/dist/core/resource-loader.js.map +1 -1
  77. package/dist/core/sdk.d.ts +2 -2
  78. package/dist/core/sdk.d.ts.map +1 -1
  79. package/dist/core/sdk.js +10 -3
  80. package/dist/core/sdk.js.map +1 -1
  81. package/dist/core/session-export.d.ts +4 -0
  82. package/dist/core/session-export.d.ts.map +1 -0
  83. package/dist/core/session-export.js +32 -0
  84. package/dist/core/session-export.js.map +1 -0
  85. package/dist/core/session-manager.d.ts.map +1 -1
  86. package/dist/core/session-manager.js +4 -3
  87. package/dist/core/session-manager.js.map +1 -1
  88. package/dist/core/settings-diagnostics.d.ts +9 -0
  89. package/dist/core/settings-diagnostics.d.ts.map +1 -0
  90. package/dist/core/settings-diagnostics.js +21 -0
  91. package/dist/core/settings-diagnostics.js.map +1 -0
  92. package/dist/core/settings-manager.d.ts +9 -0
  93. package/dist/core/settings-manager.d.ts.map +1 -1
  94. package/dist/core/settings-manager.js +51 -10
  95. package/dist/core/settings-manager.js.map +1 -1
  96. package/dist/core/skills.d.ts.map +1 -1
  97. package/dist/core/skills.js +51 -33
  98. package/dist/core/skills.js.map +1 -1
  99. package/dist/core/slash-commands.d.ts.map +1 -1
  100. package/dist/core/slash-commands.js +2 -1
  101. package/dist/core/slash-commands.js.map +1 -1
  102. package/dist/core/system-prompt.d.ts.map +1 -1
  103. package/dist/core/system-prompt.js +11 -2
  104. package/dist/core/system-prompt.js.map +1 -1
  105. package/dist/core/tools/bash.d.ts +19 -1
  106. package/dist/core/tools/bash.d.ts.map +1 -1
  107. package/dist/core/tools/bash.js +59 -37
  108. package/dist/core/tools/bash.js.map +1 -1
  109. package/dist/core/tools/edit-diff.d.ts +0 -5
  110. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  111. package/dist/core/tools/edit-diff.js +2 -5
  112. package/dist/core/tools/edit-diff.js.map +1 -1
  113. package/dist/core/tools/edit.d.ts.map +1 -1
  114. package/dist/core/tools/edit.js +20 -4
  115. package/dist/core/tools/edit.js.map +1 -1
  116. package/dist/core/tools/index.d.ts +4 -1
  117. package/dist/core/tools/index.d.ts.map +1 -1
  118. package/dist/core/tools/index.js +18 -1
  119. package/dist/core/tools/index.js.map +1 -1
  120. package/dist/core/tools/powershell.d.ts +16 -0
  121. package/dist/core/tools/powershell.d.ts.map +1 -0
  122. package/dist/core/tools/powershell.js +39 -0
  123. package/dist/core/tools/powershell.js.map +1 -0
  124. package/dist/core/trust-manager.d.ts.map +1 -1
  125. package/dist/core/trust-manager.js +2 -1
  126. package/dist/core/trust-manager.js.map +1 -1
  127. package/dist/extensions/background/registry.d.ts +2 -3
  128. package/dist/extensions/background/registry.d.ts.map +1 -1
  129. package/dist/extensions/background/registry.js +2 -6
  130. package/dist/extensions/background/registry.js.map +1 -1
  131. package/dist/extensions/llama/index.d.ts.map +1 -1
  132. package/dist/extensions/llama/index.js +2 -0
  133. package/dist/extensions/llama/index.js.map +1 -1
  134. package/dist/extensions/llama/provider.d.ts.map +1 -1
  135. package/dist/extensions/llama/provider.js +6 -2
  136. package/dist/extensions/llama/provider.js.map +1 -1
  137. package/dist/extensions/question/types.d.ts +0 -1
  138. package/dist/extensions/question/types.d.ts.map +1 -1
  139. package/dist/extensions/question/types.js +1 -1
  140. package/dist/extensions/question/types.js.map +1 -1
  141. package/dist/extensions/router/register.d.ts +0 -1
  142. package/dist/extensions/router/register.d.ts.map +1 -1
  143. package/dist/extensions/router/register.js +0 -3
  144. package/dist/extensions/router/register.js.map +1 -1
  145. package/dist/extensions/router/stream.d.ts +0 -2
  146. package/dist/extensions/router/stream.d.ts.map +1 -1
  147. package/dist/extensions/router/stream.js +1 -12
  148. package/dist/extensions/router/stream.js.map +1 -1
  149. package/dist/extensions/subagent/text.d.ts +0 -1
  150. package/dist/extensions/subagent/text.d.ts.map +1 -1
  151. package/dist/extensions/subagent/text.js +1 -30
  152. package/dist/extensions/subagent/text.js.map +1 -1
  153. package/dist/extensions/subagent/ui/model-list.d.ts +0 -3
  154. package/dist/extensions/subagent/ui/model-list.d.ts.map +1 -1
  155. package/dist/extensions/subagent/ui/model-list.js +1 -10
  156. package/dist/extensions/subagent/ui/model-list.js.map +1 -1
  157. package/dist/index.d.ts +5 -5
  158. package/dist/index.d.ts.map +1 -1
  159. package/dist/index.js +4 -4
  160. package/dist/index.js.map +1 -1
  161. package/dist/main.d.ts +4 -0
  162. package/dist/main.d.ts.map +1 -1
  163. package/dist/main.js +18 -15
  164. package/dist/main.js.map +1 -1
  165. package/dist/migrations.js +5 -5
  166. package/dist/migrations.js.map +1 -1
  167. package/dist/modes/interactive/components/index.d.ts +0 -1
  168. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  169. package/dist/modes/interactive/components/index.js +0 -1
  170. package/dist/modes/interactive/components/index.js.map +1 -1
  171. package/dist/modes/interactive/components/model-selector.d.ts +9 -3
  172. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  173. package/dist/modes/interactive/components/model-selector.js +54 -11
  174. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  175. package/dist/modes/interactive/components/settings-selector.d.ts +7 -2
  176. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  177. package/dist/modes/interactive/components/settings-selector.js +118 -55
  178. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  179. package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
  180. package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
  181. package/dist/modes/interactive/components/settings-submenu.js +151 -0
  182. package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
  183. package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
  184. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  185. package/dist/modes/interactive/components/thinking-selector.js +68 -17
  186. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  187. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  188. package/dist/modes/interactive/external-editor.js +2 -1
  189. package/dist/modes/interactive/external-editor.js.map +1 -1
  190. package/dist/modes/interactive/interactive-mode.d.ts +11 -0
  191. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  192. package/dist/modes/interactive/interactive-mode.js +167 -105
  193. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  194. package/dist/modes/interactive/session-share.d.ts +16 -0
  195. package/dist/modes/interactive/session-share.d.ts.map +1 -0
  196. package/dist/modes/interactive/session-share.js +188 -0
  197. package/dist/modes/interactive/session-share.js.map +1 -0
  198. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  199. package/dist/modes/interactive/theme/theme.js +3 -2
  200. package/dist/modes/interactive/theme/theme.js.map +1 -1
  201. package/dist/modes/json-event.d.ts +16 -13
  202. package/dist/modes/json-event.d.ts.map +1 -1
  203. package/dist/modes/json-event.js +20 -6
  204. package/dist/modes/json-event.js.map +1 -1
  205. package/dist/package-manager-cli.d.ts +1 -0
  206. package/dist/package-manager-cli.d.ts.map +1 -1
  207. package/dist/package-manager-cli.js +186 -2
  208. package/dist/package-manager-cli.js.map +1 -1
  209. package/dist/utils/frontmatter.d.ts.map +1 -1
  210. package/dist/utils/frontmatter.js +2 -1
  211. package/dist/utils/frontmatter.js.map +1 -1
  212. package/dist/utils/management-http.d.ts +5 -3
  213. package/dist/utils/management-http.d.ts.map +1 -1
  214. package/dist/utils/management-http.js +14 -10
  215. package/dist/utils/management-http.js.map +1 -1
  216. package/dist/utils/paths.d.ts +0 -5
  217. package/dist/utils/paths.d.ts.map +1 -1
  218. package/dist/utils/paths.js +0 -7
  219. package/dist/utils/paths.js.map +1 -1
  220. package/dist/utils/shell.d.ts +3 -0
  221. package/dist/utils/shell.d.ts.map +1 -1
  222. package/dist/utils/shell.js +17 -5
  223. package/dist/utils/shell.js.map +1 -1
  224. package/dist/utils/text.d.ts +8 -0
  225. package/dist/utils/text.d.ts.map +1 -0
  226. package/dist/utils/text.js +9 -0
  227. package/dist/utils/text.js.map +1 -0
  228. package/docs/compaction.md +19 -2
  229. package/docs/custom-provider.md +5 -2
  230. package/docs/environment-variables.md +8 -8
  231. package/docs/extensions.md +22 -10
  232. package/docs/json.md +7 -2
  233. package/docs/keybindings.md +10 -8
  234. package/docs/llama-cpp.md +6 -4
  235. package/docs/models.md +10 -3
  236. package/docs/packages.md +2 -1
  237. package/docs/rpc.md +9 -3
  238. package/docs/sdk.md +12 -2
  239. package/docs/settings.md +16 -5
  240. package/docs/skills.md +5 -4
  241. package/docs/terminal-setup.md +11 -9
  242. package/docs/usage.md +6 -2
  243. package/docs/windows.md +24 -2
  244. package/examples/extensions/border-status-editor.ts +1 -1
  245. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  246. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  247. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  248. package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
  249. package/examples/extensions/doom-overlay/doom/build.sh +0 -0
  250. package/examples/extensions/git-checkpoint.ts +2 -2
  251. package/examples/extensions/gondolin/package-lock.json +2 -2
  252. package/examples/extensions/gondolin/package.json +1 -1
  253. package/examples/extensions/notify.ts +3 -1
  254. package/examples/extensions/sandbox/package-lock.json +2 -2
  255. package/examples/extensions/sandbox/package.json +1 -1
  256. package/examples/extensions/titlebar-spinner.ts +1 -1
  257. package/examples/extensions/with-deps/package-lock.json +2 -2
  258. package/examples/extensions/with-deps/package.json +1 -1
  259. package/examples/rpc-extension-ui.ts +1 -1
  260. package/examples/sdk/06-extensions.ts +1 -1
  261. package/examples/sdk/README.md +1 -1
  262. package/npm-shrinkwrap.json +196 -257
  263. package/package.json +6 -8
@@ -9,11 +9,12 @@ import * as path from "node:path";
9
9
  import * as TuiLayouts from "@earendil-works/pi-tui";
10
10
  import { CombinedAutocompleteProvider, Container, fuzzyFilter, 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, INSTALL_TELEMETRY_URL, UPDATE_CHANGELOG_HINT, VERSION, } from "../../config.js";
12
+ import { spawn } from "child_process";
13
+ import { APP_NAME, APP_TITLE, CONFIG_DIR_NAME, getAgentDir, getAuthPath, getDebugLogPath, getDocsPath, INSTALL_TELEMETRY_URL, UPDATE_CHANGELOG_HINT, 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";
@@ -40,7 +41,6 @@ import { checkForNewPiVersion } from "../../utils/version-check.js";
40
41
  import { ArminComponent } from "./components/armin.js";
41
42
  import { AssistantMessageComponent } from "./components/assistant-message.js";
42
43
  import { BashExecutionComponent } from "./components/bash-execution.js";
43
- import { BorderedLoader } from "./components/bordered-loader.js";
44
44
  import { BranchSummaryMessageComponent } from "./components/branch-summary-message.js";
45
45
  import { CompactionSummaryMessageComponent } from "./components/compaction-summary-message.js";
46
46
  import { CustomEditor } from "./components/custom-editor.js";
@@ -63,6 +63,7 @@ import { SessionSelectorComponent } from "./components/session-selector.js";
63
63
  import { SettingsSelectorComponent } from "./components/settings-selector.js";
64
64
  import { SkillInvocationMessageComponent } from "./components/skill-invocation-message.js";
65
65
  import { BranchSummaryStatusIndicator, CompactionStatusIndicator, IdleStatus, RetryStatusIndicator, WorkingStatusIndicator, } from "./components/status-indicator.js";
66
+ import { ThinkingSelectorComponent } from "./components/thinking-selector.js";
66
67
  import { ToolExecutionComponent } from "./components/tool-execution.js";
67
68
  import { ToolGroupComponent } from "./components/tool-group.js";
68
69
  import { TreeSelectorComponent } from "./components/tree-selector.js";
@@ -72,6 +73,7 @@ import { UserMessageSelectorComponent } from "./components/user-message-selector
72
73
  import { editInExternalEditor } from "./external-editor.js";
73
74
  import { refreshModelCatalogs } from "./model-catalog-refresh.js";
74
75
  import { getModelSearchText } from "./model-search.js";
76
+ import { shareSession } from "./session-share.js";
75
77
  import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, onThemeChange, setRegisteredThemes, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
76
78
  import { InteractiveThemeController } from "./theme/theme-controller.js";
77
79
  function isExpandable(obj) {
@@ -90,6 +92,9 @@ class ExpandableText extends Text {
90
92
  function isCustomSessionEntry(item) {
91
93
  return "type" in item && item.type === "custom";
92
94
  }
95
+ function isCompactionCostNotice(item) {
96
+ return "type" in item && item.type === "compaction_cost";
97
+ }
93
98
  const DEAD_TERMINAL_ERROR_CODES = new Set(["EIO", "EPIPE", "ENOTCONN"]);
94
99
  function isDeadTerminalError(error) {
95
100
  if (!error || typeof error !== "object" || !("code" in error)) {
@@ -129,6 +134,11 @@ export function formatResumeCommand(sessionManager) {
129
134
  function hasDefaultModelProvider(providerId) {
130
135
  return providerId in defaultModelPerProvider;
131
136
  }
137
+ function llamaCppPostLoginGuidance(actionLabel, loadedModelCount) {
138
+ return loadedModelCount === 0
139
+ ? `${actionLabel}. No llama.cpp models are loaded. Use /llama to load a model, then /model to select it.`
140
+ : `${actionLabel}. Use /model to select a loaded llama.cpp model, or /llama to manage models.`;
141
+ }
132
142
  const AUTH_TYPE_ORDER = { oauth: 0, api_key: 1 };
133
143
  function createFuzzyAutocompleteItems(items, prefix, getSearchText, toAutocompleteItem) {
134
144
  const filtered = fuzzyFilter(items, prefix, getSearchText);
@@ -435,6 +445,15 @@ export class InteractiveMode {
435
445
  }));
436
446
  };
437
447
  }
448
+ const thinkingCommand = slashCommands.find((command) => command.name === "thinking");
449
+ if (thinkingCommand) {
450
+ thinkingCommand.getArgumentCompletions = (prefix) => {
451
+ return createFuzzyAutocompleteItems(this.session.getAvailableThinkingLevels(), prefix, (level) => level, (level) => ({
452
+ value: level,
453
+ label: level,
454
+ }));
455
+ };
456
+ }
438
457
  const loginCommand = slashCommands.find((command) => command.name === "login");
439
458
  if (loginCommand) {
440
459
  loginCommand.getArgumentCompletions = (prefix) => {
@@ -774,7 +793,18 @@ export class InteractiveMode {
774
793
  }
775
794
  });
776
795
  // Show startup warnings
777
- const { migratedProviders, modelFallbackMessage, initialMessage, initialImages, initialMessages } = this.options;
796
+ const { migratedProviders, startupDiagnostics, modelFallbackMessage, initialMessage, initialImages, initialMessages, } = this.options;
797
+ for (const diagnostic of startupDiagnostics ?? []) {
798
+ if (diagnostic.type === "error") {
799
+ this.showError(diagnostic.message);
800
+ }
801
+ else if (diagnostic.type === "warning") {
802
+ this.showWarning(diagnostic.message);
803
+ }
804
+ else {
805
+ this.showStatus(diagnostic.message);
806
+ }
807
+ }
778
808
  if (migratedProviders && migratedProviders.length > 0) {
779
809
  this.showWarning(`Migrated credentials to auth.json: ${migratedProviders.join(", ")}`);
780
810
  }
@@ -2373,6 +2403,12 @@ export class InteractiveMode {
2373
2403
  await this.handleModelCommand(searchTerm);
2374
2404
  return;
2375
2405
  }
2406
+ if (text === "/thinking" || text.startsWith("/thinking ")) {
2407
+ const searchTerm = text.startsWith("/thinking ") ? text.slice(10).trim() : undefined;
2408
+ this.editor.setText("");
2409
+ this.handleThinkingCommand(searchTerm);
2410
+ return;
2411
+ }
2376
2412
  if (text === "/export" || text.startsWith("/export ")) {
2377
2413
  await this.handleExportCommand(text);
2378
2414
  this.editor.setText("");
@@ -2755,8 +2791,21 @@ export class InteractiveMode {
2755
2791
  }
2756
2792
  else {
2757
2793
  if (event.result) {
2758
- this.rebuildChatFromMessages();
2794
+ const entries = this.sessionManager.buildContextEntries();
2795
+ if (entries[0]?.type !== "compaction") {
2796
+ throw new Error("Completed compaction is missing from the session context");
2797
+ }
2798
+ this.clearChat();
2799
+ // The latest compaction is prepended for model context; append it below at its chronological position.
2800
+ this.renderSessionEntries(entries.slice(1));
2759
2801
  this.addMessageToChat(createCompactionSummaryMessage(event.result.summary, event.result.tokensBefore, new Date().toISOString()));
2802
+ if (event.result.usage) {
2803
+ this.addCompactionCostNotice({
2804
+ type: "compaction_cost",
2805
+ kind: "compaction",
2806
+ usage: event.result.usage,
2807
+ });
2808
+ }
2760
2809
  this.footer.invalidate();
2761
2810
  }
2762
2811
  if (event.errorMessage) {
@@ -2979,6 +3028,10 @@ export class InteractiveMode {
2979
3028
  this.addCustomEntryToChat(item);
2980
3029
  continue;
2981
3030
  }
3031
+ if (isCompactionCostNotice(item)) {
3032
+ this.addCompactionCostNotice(item);
3033
+ continue;
3034
+ }
2982
3035
  const message = item;
2983
3036
  // Assistant messages need special handling for tool calls
2984
3037
  if (message.role === "assistant") {
@@ -3042,10 +3095,28 @@ export class InteractiveMode {
3042
3095
  if (entry.type === "custom") {
3043
3096
  return [entry];
3044
3097
  }
3045
- return sessionEntryToContextMessages(entry);
3098
+ const messages = sessionEntryToContextMessages(entry);
3099
+ if ((entry.type === "compaction" || entry.type === "branch_summary") && entry.usage && messages.length > 0) {
3100
+ return [...messages, { type: "compaction_cost", kind: entry.type, usage: entry.usage }];
3101
+ }
3102
+ return messages;
3046
3103
  });
3047
3104
  this.renderSessionItems(items, options);
3048
3105
  }
3106
+ /**
3107
+ * Render billing usage for a compaction or branch summary. The notice is derived
3108
+ * from persisted summary usage and is not stored as a separate session entry.
3109
+ */
3110
+ addCompactionCostNotice(notice) {
3111
+ if (!this.settingsManager.getShowCacheMissNotices())
3112
+ return;
3113
+ const { usage } = notice;
3114
+ const tokens = usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
3115
+ const cost = usage.cost.total >= 0.01 ? ` (~$${usage.cost.total.toFixed(2)})` : "";
3116
+ const label = notice.kind === "compaction" ? "Compaction" : "Branch summary";
3117
+ this.chatContainer.addChild(new Spacer(1));
3118
+ this.chatContainer.addChild(new Text(theme.fg("warning", `${label}: ${formatTokens(tokens)} tokens billed${cost}`), 1, 0));
3119
+ }
3049
3120
  /**
3050
3121
  * Show a transcript notice when a completed assistant message paid for a
3051
3122
  * significant cache miss. Only states observable facts: the miss itself,
@@ -3678,8 +3749,14 @@ export class InteractiveMode {
3678
3749
  showSettingsSelector() {
3679
3750
  this.showSelector((done) => {
3680
3751
  let selector;
3752
+ const defaultProvider = this.settingsManager.getDefaultProvider();
3753
+ const defaultModelId = this.settingsManager.getDefaultModel();
3754
+ const defaultModel = defaultProvider && defaultModelId ? `${defaultProvider}/${defaultModelId}` : "not set";
3681
3755
  selector = new SettingsSelectorComponent({
3682
3756
  autoCompact: this.session.autoCompactionEnabled,
3757
+ defaultModel,
3758
+ currentModel: this.session.model,
3759
+ availableDefaultModels: this.session.modelRuntime.getAvailableSnapshot(),
3683
3760
  showImages: this.settingsManager.getShowImages(),
3684
3761
  imageWidthCells: this.settingsManager.getImageWidthCells(),
3685
3762
  autoResizeImages: this.settingsManager.getImageAutoResize(),
@@ -3689,8 +3766,9 @@ export class InteractiveMode {
3689
3766
  followUpMode: this.session.followUpMode,
3690
3767
  transport: this.settingsManager.getTransport(),
3691
3768
  httpIdleTimeoutMs: this.settingsManager.getHttpIdleTimeoutMs(),
3692
- thinkingLevel: this.session.thinkingLevel,
3693
- availableThinkingLevels: this.session.getAvailableThinkingLevels(),
3769
+ thinkingLevel: this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL,
3770
+ availableThinkingLevels: [...THINKING_LEVEL_OPTIONS],
3771
+ modelThinkingLevels: this.settingsManager.getAllModelThinkingLevels(),
3694
3772
  currentTheme: this.themeController.getThemeSelection() || "dark",
3695
3773
  terminalTheme: this.themeController.getTerminalTheme(),
3696
3774
  availableThemes: getAvailableThemes(),
@@ -3759,10 +3837,26 @@ export class InteractiveMode {
3759
3837
  configureHttpDispatcher(timeoutMs);
3760
3838
  this.showStatus(`HTTP idle timeout: ${formatHttpIdleTimeoutMs(timeoutMs)}`);
3761
3839
  },
3762
- onThinkingLevelChange: (level) => {
3763
- this.session.setThinkingLevel(level);
3764
- this.footer.invalidate();
3765
- this.updateEditorBorderColor();
3840
+ onModelThinkingLevelChange: (provider, modelId, level) => {
3841
+ this.settingsManager.setModelThinkingLevel(provider, modelId, level);
3842
+ // If the override is for the current model, apply it to the session too
3843
+ const current = this.session.model;
3844
+ if (current && current.provider === provider && current.id === modelId) {
3845
+ this.session.setThinkingLevel(level);
3846
+ this.footer.invalidate();
3847
+ this.updateEditorBorderColor();
3848
+ }
3849
+ },
3850
+ onModelThinkingLevelRemove: (provider, modelId) => {
3851
+ this.settingsManager.removeModelThinkingLevel(provider, modelId);
3852
+ // If the override was for the current model, revert to global default
3853
+ const current = this.session.model;
3854
+ if (current && current.provider === provider && current.id === modelId) {
3855
+ const globalDefault = this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
3856
+ this.session.setThinkingLevel(globalDefault);
3857
+ this.footer.invalidate();
3858
+ this.updateEditorBorderColor();
3859
+ }
3766
3860
  },
3767
3861
  onThemeChange: (themeSetting) => {
3768
3862
  this.settingsManager.setTheme(themeSetting);
@@ -3882,6 +3976,44 @@ export class InteractiveMode {
3882
3976
  return { component: selector, focus: selector.getSettingsList() };
3883
3977
  });
3884
3978
  }
3979
+ handleThinkingCommand(searchTerm) {
3980
+ const availableLevels = this.session.getAvailableThinkingLevels();
3981
+ if (!searchTerm) {
3982
+ this.showThinkingSelector();
3983
+ return;
3984
+ }
3985
+ const normalized = searchTerm.trim().toLowerCase();
3986
+ const level = availableLevels.find((candidate) => candidate.toLowerCase() === normalized);
3987
+ if (!level) {
3988
+ this.showError(`Unknown thinking level "${searchTerm}". Available levels: ${availableLevels.join(", ")}.`);
3989
+ return;
3990
+ }
3991
+ this.selectThinkingLevel(level, false);
3992
+ }
3993
+ selectThinkingLevel(level, persist) {
3994
+ try {
3995
+ this.session.setThinkingLevel(level, { persist });
3996
+ this.footer.invalidate();
3997
+ this.updateEditorBorderColor();
3998
+ this.showStatus(persist ? `Default thinking level: ${level}` : `Thinking level: ${level}`);
3999
+ }
4000
+ catch (error) {
4001
+ this.showError(error instanceof Error ? error.message : String(error));
4002
+ }
4003
+ }
4004
+ showThinkingSelector() {
4005
+ this.showSelector((done) => {
4006
+ const selectLevel = (level, persist) => {
4007
+ this.selectThinkingLevel(level, persist);
4008
+ done();
4009
+ };
4010
+ const selector = new ThinkingSelectorComponent(this.session.thinkingLevel ?? DEFAULT_THINKING_LEVEL, this.session.getAvailableThinkingLevels(), (level) => selectLevel(level, false), () => {
4011
+ done();
4012
+ this.ui.requestRender();
4013
+ }, (level) => selectLevel(level, true), this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);
4014
+ return { component: selector, focus: selector };
4015
+ });
4016
+ }
3885
4017
  async handleModelCommand(searchTerm) {
3886
4018
  if (!searchTerm) {
3887
4019
  this.showModelSelector();
@@ -3890,7 +4022,7 @@ export class InteractiveMode {
3890
4022
  const model = await this.findExactModelMatch(searchTerm);
3891
4023
  if (model) {
3892
4024
  try {
3893
- await this.session.setModel(model);
4025
+ await this.session.setModel(model, { persist: false });
3894
4026
  this.footer.invalidate();
3895
4027
  this.updateEditorBorderColor();
3896
4028
  this.showStatus(`Model: ${model.id}`);
@@ -4019,13 +4151,13 @@ export class InteractiveMode {
4019
4151
  }
4020
4152
  showModelSelector(initialSearchInput) {
4021
4153
  this.showSelector((done) => {
4022
- const selector = new ModelSelectorComponent(this.ui, this.session.model, this.settingsManager, this.session.modelRuntime, this.session.scopedModels, async (model) => {
4154
+ const selectModel = async (model, persist) => {
4023
4155
  try {
4024
- await this.session.setModel(model);
4156
+ await this.session.setModel(model, { persist });
4025
4157
  this.footer.invalidate();
4026
4158
  this.updateEditorBorderColor();
4027
4159
  done();
4028
- this.showStatus(`Model: ${model.id}`);
4160
+ this.showStatus(persist ? `Default model: ${model.provider}/${model.id}` : `Model: ${model.id}`);
4029
4161
  void this.maybeWarnAboutAnthropicSubscriptionAuth(model);
4030
4162
  this.checkDaxnutsEasterEgg(model);
4031
4163
  }
@@ -4033,10 +4165,13 @@ export class InteractiveMode {
4033
4165
  done();
4034
4166
  this.showError(error instanceof Error ? error.message : String(error));
4035
4167
  }
4036
- }, () => {
4168
+ };
4169
+ const defaultProvider = this.settingsManager.getDefaultProvider();
4170
+ const defaultModel = this.settingsManager.getDefaultModel();
4171
+ const selector = new ModelSelectorComponent(this.ui, this.session.model, this.session.modelRuntime, this.session.scopedModels, (model) => selectModel(model, false), () => {
4037
4172
  done();
4038
4173
  this.ui.requestRender();
4039
- }, initialSearchInput);
4174
+ }, initialSearchInput, (model) => selectModel(model, true), defaultProvider && defaultModel ? { provider: defaultProvider, id: defaultModel } : undefined);
4040
4175
  return { component: selector, focus: selector, dispose: () => selector.dispose() };
4041
4176
  });
4042
4177
  }
@@ -4592,7 +4727,11 @@ export class InteractiveMode {
4592
4727
  if (isUnknownModel(previousModel)) {
4593
4728
  const availableModels = this.session.modelRuntime.getAvailableSnapshot();
4594
4729
  const providerModels = availableModels.filter((model) => model.provider === providerId);
4595
- if (!hasDefaultModelProvider(providerId)) {
4730
+ // Matches LLAMA_PROVIDER_ID from extensions/llama/provider.ts; kept inline to avoid coupling interactive mode to the built-in extension.
4731
+ if (providerId === "llama.cpp") {
4732
+ selectionError = llamaCppPostLoginGuidance(actionLabel, providerModels.length);
4733
+ }
4734
+ else if (!hasDefaultModelProvider(providerId)) {
4596
4735
  selectionError = `${actionLabel}, but no default model is configured for provider "${providerId}". Use /model to select a model.`;
4597
4736
  }
4598
4737
  else if (providerModels.length === 0) {
@@ -4606,7 +4745,7 @@ export class InteractiveMode {
4606
4745
  }
4607
4746
  else {
4608
4747
  try {
4609
- await this.session.setModel(selectedModel);
4748
+ await this.session.setModel(selectedModel, { persist: true });
4610
4749
  }
4611
4750
  catch (error) {
4612
4751
  selectedModel = undefined;
@@ -4976,91 +5115,14 @@ export class InteractiveMode {
4976
5115
  }
4977
5116
  }
4978
5117
  async handleShareCommand() {
4979
- // Check if gh is available and logged in
4980
- try {
4981
- const authResult = spawnSync("gh", ["auth", "status"], { encoding: "utf-8" });
4982
- if (authResult.status !== 0) {
4983
- this.showError("GitHub CLI is not logged in. Run 'gh auth login' first.");
4984
- return;
4985
- }
4986
- }
4987
- catch {
4988
- this.showError("GitHub CLI (gh) is not installed. Install it from https://cli.github.com/");
4989
- return;
4990
- }
4991
- // Export to a temp file
4992
- const tmpFile = path.join(os.tmpdir(), "session.html");
4993
- try {
4994
- await this.session.exportToHtml(tmpFile, { themeName: theme.name });
4995
- }
4996
- catch (error) {
4997
- this.showError(`Failed to export session: ${error instanceof Error ? error.message : "Unknown error"}`);
4998
- return;
4999
- }
5000
- // Show cancellable loader, replacing the editor
5001
- const loader = new BorderedLoader(this.ui, theme, "Creating gist...");
5002
- this.editorContainer.clear();
5003
- this.editorContainer.addChild(loader);
5004
- this.ui.setFocus(loader);
5005
- this.ui.requestRender();
5006
- const restoreEditor = () => {
5007
- loader.dispose();
5008
- this.editorContainer.clear();
5009
- this.editorContainer.addChild(this.editor);
5010
- this.ui.setFocus(this.editor);
5011
- try {
5012
- fs.unlinkSync(tmpFile);
5013
- }
5014
- catch {
5015
- // Ignore cleanup errors
5016
- }
5017
- };
5018
- // Create a secret gist asynchronously
5019
- let proc = null;
5020
- loader.onAbort = () => {
5021
- proc?.kill();
5022
- restoreEditor();
5023
- this.showStatus("Share cancelled");
5024
- };
5025
- try {
5026
- const result = await new Promise((resolve) => {
5027
- proc = spawn("gh", ["gist", "create", "--public=false", tmpFile]);
5028
- let stdout = "";
5029
- let stderr = "";
5030
- proc.stdout?.on("data", (data) => {
5031
- stdout += data.toString();
5032
- });
5033
- proc.stderr?.on("data", (data) => {
5034
- stderr += data.toString();
5035
- });
5036
- proc.on("close", (code) => resolve({ stdout, stderr, code }));
5037
- });
5038
- if (loader.signal.aborted)
5039
- return;
5040
- restoreEditor();
5041
- if (result.code !== 0) {
5042
- const errorMsg = result.stderr?.trim() || "Unknown error";
5043
- this.showError(`Failed to create gist: ${errorMsg}`);
5044
- return;
5045
- }
5046
- // Extract gist ID from the URL returned by gh
5047
- // gh returns something like: https://gist.github.com/username/GIST_ID
5048
- const gistUrl = result.stdout?.trim();
5049
- const gistId = gistUrl?.split("/").pop();
5050
- if (!gistId) {
5051
- this.showError("Failed to parse gist ID from gh output");
5052
- return;
5053
- }
5054
- // Create the preview URL
5055
- const previewUrl = getShareViewerUrl(gistId);
5056
- this.showStatus(`Share URL: ${previewUrl}\nGist: ${gistUrl}`);
5057
- }
5058
- catch (error) {
5059
- if (!loader.signal.aborted) {
5060
- restoreEditor();
5061
- this.showError(`Failed to create gist: ${error instanceof Error ? error.message : "Unknown error"}`);
5062
- }
5063
- }
5118
+ await shareSession({
5119
+ session: this.session,
5120
+ ui: this.ui,
5121
+ editorContainer: this.editorContainer,
5122
+ editor: this.editor,
5123
+ showStatus: (message) => this.showStatus(message),
5124
+ showError: (message) => this.showError(message),
5125
+ });
5064
5126
  }
5065
5127
  async handleCopyCommand(options = {}) {
5066
5128
  const text = this.session.getLastAssistantText();