apex-code 0.0.1-alpha.8 → 0.0.1-alpha.9

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 (230) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/dist/cli/args.d.ts +1 -0
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +29 -9
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/cli/file-processor.d.ts.map +1 -1
  7. package/dist/cli/file-processor.js +2 -1
  8. package/dist/cli/file-processor.js.map +1 -1
  9. package/dist/config.d.ts +3 -2
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/config.js +21 -13
  12. package/dist/config.js.map +1 -1
  13. package/dist/core/agent-session.d.ts +26 -7
  14. package/dist/core/agent-session.d.ts.map +1 -1
  15. package/dist/core/agent-session.js +163 -95
  16. package/dist/core/agent-session.js.map +1 -1
  17. package/dist/core/auth-storage.d.ts.map +1 -1
  18. package/dist/core/auth-storage.js +6 -7
  19. package/dist/core/auth-storage.js.map +1 -1
  20. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  21. package/dist/core/compaction/branch-summarization.js +3 -0
  22. package/dist/core/compaction/branch-summarization.js.map +1 -1
  23. package/dist/core/compaction/compaction.d.ts +4 -3
  24. package/dist/core/compaction/compaction.d.ts.map +1 -1
  25. package/dist/core/compaction/compaction.js +41 -32
  26. package/dist/core/compaction/compaction.js.map +1 -1
  27. package/dist/core/defaults.d.ts +1 -0
  28. package/dist/core/defaults.d.ts.map +1 -1
  29. package/dist/core/defaults.js +9 -0
  30. package/dist/core/defaults.js.map +1 -1
  31. package/dist/core/durable-state/sqlite.d.ts.map +1 -1
  32. package/dist/core/durable-state/sqlite.js +26 -2
  33. package/dist/core/durable-state/sqlite.js.map +1 -1
  34. package/dist/core/extensions/index.d.ts +2 -2
  35. package/dist/core/extensions/index.d.ts.map +1 -1
  36. package/dist/core/extensions/index.js +1 -1
  37. package/dist/core/extensions/index.js.map +1 -1
  38. package/dist/core/extensions/loader.d.ts.map +1 -1
  39. package/dist/core/extensions/loader.js +115 -48
  40. package/dist/core/extensions/loader.js.map +1 -1
  41. package/dist/core/extensions/types.d.ts +38 -9
  42. package/dist/core/extensions/types.d.ts.map +1 -1
  43. package/dist/core/extensions/types.js +3 -0
  44. package/dist/core/extensions/types.js.map +1 -1
  45. package/dist/core/footer-data-provider.d.ts +1 -1
  46. package/dist/core/footer-data-provider.d.ts.map +1 -1
  47. package/dist/core/footer-data-provider.js +1 -1
  48. package/dist/core/footer-data-provider.js.map +1 -1
  49. package/dist/core/keybindings.d.ts +20 -19
  50. package/dist/core/keybindings.d.ts.map +1 -1
  51. package/dist/core/keybindings.js +26 -5
  52. package/dist/core/keybindings.js.map +1 -1
  53. package/dist/core/model-config.d.ts +5 -0
  54. package/dist/core/model-config.d.ts.map +1 -1
  55. package/dist/core/model-config.js +3 -1
  56. package/dist/core/model-config.js.map +1 -1
  57. package/dist/core/model-resolver.d.ts +1 -0
  58. package/dist/core/model-resolver.d.ts.map +1 -1
  59. package/dist/core/model-resolver.js +14 -8
  60. package/dist/core/model-resolver.js.map +1 -1
  61. package/dist/core/models-store.d.ts.map +1 -1
  62. package/dist/core/models-store.js +2 -1
  63. package/dist/core/models-store.js.map +1 -1
  64. package/dist/core/package-manager.d.ts.map +1 -1
  65. package/dist/core/package-manager.js +22 -14
  66. package/dist/core/package-manager.js.map +1 -1
  67. package/dist/core/pi-manifest.d.ts.map +1 -1
  68. package/dist/core/pi-manifest.js +2 -1
  69. package/dist/core/pi-manifest.js.map +1 -1
  70. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  71. package/dist/core/remote-catalog-provider.js +2 -1
  72. package/dist/core/remote-catalog-provider.js.map +1 -1
  73. package/dist/core/resource-loader.d.ts.map +1 -1
  74. package/dist/core/resource-loader.js +3 -2
  75. package/dist/core/resource-loader.js.map +1 -1
  76. package/dist/core/sdk.d.ts +2 -2
  77. package/dist/core/sdk.d.ts.map +1 -1
  78. package/dist/core/sdk.js +10 -3
  79. package/dist/core/sdk.js.map +1 -1
  80. package/dist/core/session-export.d.ts +4 -0
  81. package/dist/core/session-export.d.ts.map +1 -0
  82. package/dist/core/session-export.js +32 -0
  83. package/dist/core/session-export.js.map +1 -0
  84. package/dist/core/session-manager.d.ts.map +1 -1
  85. package/dist/core/session-manager.js +2 -1
  86. package/dist/core/session-manager.js.map +1 -1
  87. package/dist/core/settings-diagnostics.d.ts +9 -0
  88. package/dist/core/settings-diagnostics.d.ts.map +1 -0
  89. package/dist/core/settings-diagnostics.js +21 -0
  90. package/dist/core/settings-diagnostics.js.map +1 -0
  91. package/dist/core/settings-manager.d.ts +9 -0
  92. package/dist/core/settings-manager.d.ts.map +1 -1
  93. package/dist/core/settings-manager.js +53 -11
  94. package/dist/core/settings-manager.js.map +1 -1
  95. package/dist/core/skills.d.ts.map +1 -1
  96. package/dist/core/skills.js +51 -33
  97. package/dist/core/skills.js.map +1 -1
  98. package/dist/core/slash-commands.d.ts.map +1 -1
  99. package/dist/core/slash-commands.js +2 -1
  100. package/dist/core/slash-commands.js.map +1 -1
  101. package/dist/core/system-prompt.d.ts.map +1 -1
  102. package/dist/core/system-prompt.js +8 -2
  103. package/dist/core/system-prompt.js.map +1 -1
  104. package/dist/core/tools/bash.d.ts +14 -1
  105. package/dist/core/tools/bash.d.ts.map +1 -1
  106. package/dist/core/tools/bash.js +36 -20
  107. package/dist/core/tools/bash.js.map +1 -1
  108. package/dist/core/tools/edit-diff.d.ts +0 -5
  109. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  110. package/dist/core/tools/edit-diff.js +2 -5
  111. package/dist/core/tools/edit-diff.js.map +1 -1
  112. package/dist/core/tools/edit.d.ts.map +1 -1
  113. package/dist/core/tools/edit.js +20 -4
  114. package/dist/core/tools/edit.js.map +1 -1
  115. package/dist/core/tools/index.d.ts +4 -1
  116. package/dist/core/tools/index.d.ts.map +1 -1
  117. package/dist/core/tools/index.js +9 -0
  118. package/dist/core/tools/index.js.map +1 -1
  119. package/dist/core/tools/powershell.d.ts +16 -0
  120. package/dist/core/tools/powershell.d.ts.map +1 -0
  121. package/dist/core/tools/powershell.js +39 -0
  122. package/dist/core/tools/powershell.js.map +1 -0
  123. package/dist/core/trust-manager.d.ts.map +1 -1
  124. package/dist/core/trust-manager.js +2 -1
  125. package/dist/core/trust-manager.js.map +1 -1
  126. package/dist/extensions/llama/index.d.ts.map +1 -1
  127. package/dist/extensions/llama/index.js +2 -0
  128. package/dist/extensions/llama/index.js.map +1 -1
  129. package/dist/extensions/llama/provider.d.ts.map +1 -1
  130. package/dist/extensions/llama/provider.js +6 -2
  131. package/dist/extensions/llama/provider.js.map +1 -1
  132. package/dist/index.d.ts +5 -5
  133. package/dist/index.d.ts.map +1 -1
  134. package/dist/index.js +4 -4
  135. package/dist/index.js.map +1 -1
  136. package/dist/main.d.ts +4 -0
  137. package/dist/main.d.ts.map +1 -1
  138. package/dist/main.js +18 -15
  139. package/dist/main.js.map +1 -1
  140. package/dist/migrations.d.ts.map +1 -1
  141. package/dist/migrations.js +4 -3
  142. package/dist/migrations.js.map +1 -1
  143. package/dist/modes/interactive/components/model-selector.d.ts +10 -3
  144. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  145. package/dist/modes/interactive/components/model-selector.js +51 -9
  146. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  147. package/dist/modes/interactive/components/settings-selector.d.ts +7 -2
  148. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  149. package/dist/modes/interactive/components/settings-selector.js +118 -56
  150. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  151. package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
  152. package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
  153. package/dist/modes/interactive/components/settings-submenu.js +165 -0
  154. package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
  155. package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
  156. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  157. package/dist/modes/interactive/components/thinking-selector.js +74 -17
  158. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  159. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  160. package/dist/modes/interactive/external-editor.js +2 -1
  161. package/dist/modes/interactive/external-editor.js.map +1 -1
  162. package/dist/modes/interactive/interactive-mode.d.ts +11 -0
  163. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  164. package/dist/modes/interactive/interactive-mode.js +164 -17
  165. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  166. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  167. package/dist/modes/interactive/theme/theme.js +5 -4
  168. package/dist/modes/interactive/theme/theme.js.map +1 -1
  169. package/dist/modes/json-event.d.ts +16 -13
  170. package/dist/modes/json-event.d.ts.map +1 -1
  171. package/dist/modes/json-event.js +20 -6
  172. package/dist/modes/json-event.js.map +1 -1
  173. package/dist/package-manager-cli.d.ts +1 -0
  174. package/dist/package-manager-cli.d.ts.map +1 -1
  175. package/dist/package-manager-cli.js +197 -5
  176. package/dist/package-manager-cli.js.map +1 -1
  177. package/dist/utils/frontmatter.d.ts.map +1 -1
  178. package/dist/utils/frontmatter.js +2 -1
  179. package/dist/utils/frontmatter.js.map +1 -1
  180. package/dist/utils/management-http.d.ts +5 -3
  181. package/dist/utils/management-http.d.ts.map +1 -1
  182. package/dist/utils/management-http.js +14 -10
  183. package/dist/utils/management-http.js.map +1 -1
  184. package/dist/utils/shell.d.ts +3 -0
  185. package/dist/utils/shell.d.ts.map +1 -1
  186. package/dist/utils/shell.js +17 -5
  187. package/dist/utils/shell.js.map +1 -1
  188. package/dist/utils/syntax-highlight.d.ts +1 -0
  189. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  190. package/dist/utils/syntax-highlight.js +60 -1
  191. package/dist/utils/syntax-highlight.js.map +1 -1
  192. package/dist/utils/text.d.ts +8 -0
  193. package/dist/utils/text.d.ts.map +1 -0
  194. package/dist/utils/text.js +9 -0
  195. package/dist/utils/text.js.map +1 -0
  196. package/docs/compaction.md +15 -0
  197. package/docs/custom-provider.md +5 -2
  198. package/docs/extensions.md +17 -7
  199. package/docs/json.md +7 -2
  200. package/docs/keybindings.md +8 -8
  201. package/docs/llama-cpp.md +2 -0
  202. package/docs/models.md +8 -2
  203. package/docs/packages.md +2 -2
  204. package/docs/rpc.md +9 -3
  205. package/docs/sdk.md +7 -2
  206. package/docs/settings.md +11 -3
  207. package/docs/skills.md +4 -3
  208. package/docs/terminal-setup.md +11 -7
  209. package/docs/usage.md +6 -2
  210. package/docs/windows.md +24 -2
  211. package/examples/extensions/border-status-editor.ts +1 -1
  212. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  213. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  214. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  215. package/examples/extensions/git-checkpoint.ts +2 -2
  216. package/examples/extensions/gondolin/package-lock.json +2 -2
  217. package/examples/extensions/gondolin/package.json +1 -1
  218. package/examples/extensions/notify.ts +3 -1
  219. package/examples/extensions/sandbox/package-lock.json +2 -2
  220. package/examples/extensions/sandbox/package.json +1 -1
  221. package/examples/extensions/subagent/README.md +1 -1
  222. package/examples/extensions/subagent/index.ts +6 -1
  223. package/examples/extensions/titlebar-spinner.ts +1 -1
  224. package/examples/extensions/with-deps/package-lock.json +2 -2
  225. package/examples/extensions/with-deps/package.json +1 -1
  226. package/examples/rpc-extension-ui.ts +1 -1
  227. package/examples/sdk/06-extensions.ts +1 -1
  228. package/examples/sdk/README.md +1 -1
  229. package/npm-shrinkwrap.json +276 -390
  230. package/package.json +9 -11
@@ -14,6 +14,7 @@ import { APP_NAME, APP_TITLE, CONFIG_DIR_NAME, getAgentDir, getAuthPath, getDebu
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 { getApexEnvironment } from "../../core/environment.js";
18
19
  import { FooterDataProvider } from "../../core/footer-data-provider.js";
19
20
  import { configureHttpDispatcher, formatHttpIdleTimeoutMs } from "../../core/http-dispatcher.js";
@@ -37,6 +38,7 @@ import { parseGitUrl } from "../../utils/git.js";
37
38
  import { openBrowser } from "../../utils/open-browser.js";
38
39
  import { getCwdRelativePath } from "../../utils/paths.js";
39
40
  import { killTrackedDetachedChildren } from "../../utils/shell.js";
41
+ import { loadAllHighlightLanguages } from "../../utils/syntax-highlight.js";
40
42
  import { ensureTool } from "../../utils/tools-manager.js";
41
43
  import { checkForNewApexCodeVersion } from "../../utils/version-check.js";
42
44
  import { ArminComponent } from "./components/armin.js";
@@ -67,6 +69,7 @@ import { PERMISSION_MODE_OVERRIDE_HINTS, SettingsSelectorComponent } from "./com
67
69
  import { SkillInvocationMessageComponent } from "./components/skill-invocation-message.js";
68
70
  import { ApexSplashHeader } from "./components/splash-header.js";
69
71
  import { BranchSummaryStatusIndicator, CompactionStatusIndicator, IdleStatus, RetryStatusIndicator, WorkingStatusIndicator, } from "./components/status-indicator.js";
72
+ import { ThinkingSelectorComponent } from "./components/thinking-selector.js";
70
73
  import { ToolExecutionComponent } from "./components/tool-execution.js";
71
74
  import { TreeSelectorComponent } from "./components/tree-selector.js";
72
75
  import { TrustSelectorComponent } from "./components/trust-selector.js";
@@ -95,6 +98,9 @@ class ExpandableText extends Text {
95
98
  function isCustomSessionEntry(item) {
96
99
  return "type" in item && item.type === "custom";
97
100
  }
101
+ function isCompactionCostNotice(item) {
102
+ return "type" in item && item.type === "compaction_cost";
103
+ }
98
104
  const DEAD_TERMINAL_ERROR_CODES = new Set(["EIO", "EPIPE", "ENOTCONN"]);
99
105
  function isDeadTerminalError(error) {
100
106
  if (!error || typeof error !== "object" || !("code" in error)) {
@@ -134,6 +140,11 @@ export function formatResumeCommand(sessionManager) {
134
140
  function hasDefaultModelProvider(providerId) {
135
141
  return providerId in defaultModelPerProvider;
136
142
  }
143
+ function llamaCppPostLoginGuidance(actionLabel, loadedModelCount) {
144
+ return loadedModelCount === 0
145
+ ? `${actionLabel}. No llama.cpp models are loaded. Use /llama to load a model, then /model to select it.`
146
+ : `${actionLabel}. Use /model to select a loaded llama.cpp model, or /llama to manage models.`;
147
+ }
137
148
  const AUTH_TYPE_ORDER = { oauth: 0, api_key: 1 };
138
149
  function createFuzzyAutocompleteItems(items, prefix, getSearchText, toAutocompleteItem) {
139
150
  const filtered = fuzzyFilter(items, prefix, getSearchText);
@@ -496,6 +507,15 @@ export class InteractiveMode {
496
507
  }));
497
508
  };
498
509
  }
510
+ const thinkingCommand = slashCommands.find((command) => command.name === "thinking");
511
+ if (thinkingCommand) {
512
+ thinkingCommand.getArgumentCompletions = (prefix) => {
513
+ return createFuzzyAutocompleteItems(this.session.getAvailableThinkingLevels(), prefix, (level) => level, (level) => ({
514
+ value: level,
515
+ label: level,
516
+ }));
517
+ };
518
+ }
499
519
  const loginCommand = slashCommands.find((command) => command.name === "login");
500
520
  if (loginCommand) {
501
521
  loginCommand.getArgumentCompletions = (prefix) => {
@@ -792,6 +812,14 @@ export class InteractiveMode {
792
812
  });
793
813
  // Initialize available provider count for footer display
794
814
  await this.updateAvailableProviderCount();
815
+ // Flush the completed startup state before loading the remaining syntax grammars.
816
+ this.ui.renderNow();
817
+ void loadAllHighlightLanguages().then(() => {
818
+ if (!this.isInitialized)
819
+ return;
820
+ this.ui.invalidate();
821
+ this.ui.requestRender();
822
+ });
795
823
  }
796
824
  /**
797
825
  * Update terminal title with session name and cwd.
@@ -847,7 +875,18 @@ export class InteractiveMode {
847
875
  }
848
876
  });
849
877
  // Show startup warnings
850
- const { migratedProviders, modelFallbackMessage, initialMessage, initialImages, initialMessages } = this.options;
878
+ const { migratedProviders, startupDiagnostics, modelFallbackMessage, initialMessage, initialImages, initialMessages, } = this.options;
879
+ for (const diagnostic of startupDiagnostics ?? []) {
880
+ if (diagnostic.type === "error") {
881
+ this.showError(diagnostic.message);
882
+ }
883
+ else if (diagnostic.type === "warning") {
884
+ this.showWarning(diagnostic.message);
885
+ }
886
+ else {
887
+ this.showStatus(diagnostic.message);
888
+ }
889
+ }
851
890
  if (migratedProviders && migratedProviders.length > 0) {
852
891
  this.showWarning(`Migrated credentials to auth.json: ${migratedProviders.join(", ")}`);
853
892
  }
@@ -2492,6 +2531,12 @@ export class InteractiveMode {
2492
2531
  await this.handleModelCommand(searchTerm);
2493
2532
  return;
2494
2533
  }
2534
+ if (text === "/thinking" || text.startsWith("/thinking ")) {
2535
+ const searchTerm = text.startsWith("/thinking ") ? text.slice(10).trim() : undefined;
2536
+ this.editor.setText("");
2537
+ this.handleThinkingCommand(searchTerm);
2538
+ return;
2539
+ }
2495
2540
  if (text === "/export" || text.startsWith("/export ")) {
2496
2541
  await this.handleExportCommand(text);
2497
2542
  this.editor.setText("");
@@ -2878,9 +2923,21 @@ export class InteractiveMode {
2878
2923
  }
2879
2924
  }
2880
2925
  else if (event.result) {
2926
+ const entries = this.sessionManager.buildContextEntries();
2927
+ if (entries[0]?.type !== "compaction") {
2928
+ throw new Error("Completed compaction is missing from the session context");
2929
+ }
2881
2930
  this.chatContainer.clear();
2882
- this.rebuildChatFromMessages();
2931
+ // The latest compaction is prepended for model context; append it below at its chronological position.
2932
+ this.renderSessionEntries(entries.slice(1));
2883
2933
  this.addMessageToChat(createCompactionSummaryMessage(event.result.summary, event.result.tokensBefore, new Date().toISOString()));
2934
+ if (event.result.usage) {
2935
+ this.addCompactionCostNotice({
2936
+ type: "compaction_cost",
2937
+ kind: "compaction",
2938
+ usage: event.result.usage,
2939
+ });
2940
+ }
2884
2941
  this.footer.invalidate();
2885
2942
  }
2886
2943
  else if (event.errorMessage) {
@@ -3102,6 +3159,10 @@ export class InteractiveMode {
3102
3159
  this.addCustomEntryToChat(item);
3103
3160
  continue;
3104
3161
  }
3162
+ if (isCompactionCostNotice(item)) {
3163
+ this.addCompactionCostNotice(item);
3164
+ continue;
3165
+ }
3105
3166
  const message = item;
3106
3167
  // Assistant messages need special handling for tool calls
3107
3168
  if (message.role === "assistant") {
@@ -3169,10 +3230,28 @@ export class InteractiveMode {
3169
3230
  if (entry.type === "custom") {
3170
3231
  return [entry];
3171
3232
  }
3172
- return sessionEntryToContextMessages(entry);
3233
+ const messages = sessionEntryToContextMessages(entry);
3234
+ if ((entry.type === "compaction" || entry.type === "branch_summary") && entry.usage && messages.length > 0) {
3235
+ return [...messages, { type: "compaction_cost", kind: entry.type, usage: entry.usage }];
3236
+ }
3237
+ return messages;
3173
3238
  });
3174
3239
  this.renderSessionItems(items, options);
3175
3240
  }
3241
+ /**
3242
+ * Render billing usage for a compaction or branch summary. The notice is derived
3243
+ * from persisted summary usage and is not stored as a separate session entry.
3244
+ */
3245
+ addCompactionCostNotice(notice) {
3246
+ if (!this.settingsManager.getShowCacheMissNotices())
3247
+ return;
3248
+ const { usage } = notice;
3249
+ const tokens = usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
3250
+ const cost = usage.cost.total >= 0.01 ? ` (~$${usage.cost.total.toFixed(2)})` : "";
3251
+ const label = notice.kind === "compaction" ? "Compaction" : "Branch summary";
3252
+ this.chatContainer.addChild(new Spacer(1));
3253
+ this.chatContainer.addChild(new Text(theme.fg("warning", `${label}: ${formatTokens(tokens)} tokens billed${cost}`), 1, 0));
3254
+ }
3176
3255
  /**
3177
3256
  * Show a transcript notice when a completed assistant message paid for a
3178
3257
  * significant cache miss. Only states observable facts: the miss itself,
@@ -3858,8 +3937,14 @@ export class InteractiveMode {
3858
3937
  const permissionMode = await this.session.getPermissionMode();
3859
3938
  this.showSelector((done) => {
3860
3939
  let selector;
3940
+ const defaultProvider = this.settingsManager.getDefaultProvider();
3941
+ const defaultModelId = this.settingsManager.getDefaultModel();
3942
+ const defaultModel = defaultProvider && defaultModelId ? `${defaultProvider}/${defaultModelId}` : "not set";
3861
3943
  selector = new SettingsSelectorComponent({
3862
3944
  autoCompact: this.session.autoCompactionEnabled,
3945
+ defaultModel,
3946
+ currentModel: this.session.model,
3947
+ availableDefaultModels: this.session.modelRuntime.getAvailableSnapshot(),
3863
3948
  showImages: this.settingsManager.getShowImages(),
3864
3949
  imageWidthCells: this.settingsManager.getImageWidthCells(),
3865
3950
  autoResizeImages: this.settingsManager.getImageAutoResize(),
@@ -3869,8 +3954,9 @@ export class InteractiveMode {
3869
3954
  followUpMode: this.session.followUpMode,
3870
3955
  transport: this.settingsManager.getTransport(),
3871
3956
  httpIdleTimeoutMs: this.settingsManager.getHttpIdleTimeoutMs(),
3872
- thinkingLevel: this.session.thinkingLevel,
3873
- availableThinkingLevels: this.session.getAvailableThinkingLevels(),
3957
+ thinkingLevel: this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL,
3958
+ availableThinkingLevels: [...THINKING_LEVEL_OPTIONS],
3959
+ modelThinkingLevels: this.settingsManager.getAllModelThinkingLevels(),
3874
3960
  currentTheme: this.themeController.getThemeSelection() || "dark",
3875
3961
  terminalTheme: this.themeController.getTerminalTheme(),
3876
3962
  availableThemes: getAvailableThemes(),
@@ -3940,10 +4026,26 @@ export class InteractiveMode {
3940
4026
  configureHttpDispatcher(timeoutMs);
3941
4027
  this.showStatus(`HTTP idle timeout: ${formatHttpIdleTimeoutMs(timeoutMs)}`);
3942
4028
  },
3943
- onThinkingLevelChange: (level) => {
3944
- this.session.setThinkingLevel(level);
3945
- this.footer.invalidate();
3946
- this.updateEditorBorderColor();
4029
+ onModelThinkingLevelChange: (provider, modelId, level) => {
4030
+ this.settingsManager.setModelThinkingLevel(provider, modelId, level);
4031
+ // If the override is for the current model, apply it to the session too
4032
+ const current = this.session.model;
4033
+ if (current && current.provider === provider && current.id === modelId) {
4034
+ this.session.setThinkingLevel(level);
4035
+ this.footer.invalidate();
4036
+ this.updateEditorBorderColor();
4037
+ }
4038
+ },
4039
+ onModelThinkingLevelRemove: (provider, modelId) => {
4040
+ this.settingsManager.removeModelThinkingLevel(provider, modelId);
4041
+ // If the override was for the current model, revert to global default
4042
+ const current = this.session.model;
4043
+ if (current && current.provider === provider && current.id === modelId) {
4044
+ const globalDefault = this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
4045
+ this.session.setThinkingLevel(globalDefault);
4046
+ this.footer.invalidate();
4047
+ this.updateEditorBorderColor();
4048
+ }
3947
4049
  },
3948
4050
  onThemeChange: (themeSetting) => {
3949
4051
  this.settingsManager.setTheme(themeSetting);
@@ -4067,6 +4169,44 @@ export class InteractiveMode {
4067
4169
  return { component: selector, focus: selector.getSettingsList() };
4068
4170
  });
4069
4171
  }
4172
+ handleThinkingCommand(searchTerm) {
4173
+ const availableLevels = this.session.getAvailableThinkingLevels();
4174
+ if (!searchTerm) {
4175
+ this.showThinkingSelector();
4176
+ return;
4177
+ }
4178
+ const normalized = searchTerm.trim().toLowerCase();
4179
+ const level = availableLevels.find((candidate) => candidate.toLowerCase() === normalized);
4180
+ if (!level) {
4181
+ this.showError(`Unknown thinking level "${searchTerm}". Available levels: ${availableLevels.join(", ")}.`);
4182
+ return;
4183
+ }
4184
+ this.selectThinkingLevel(level, false);
4185
+ }
4186
+ selectThinkingLevel(level, persist) {
4187
+ try {
4188
+ this.session.setThinkingLevel(level, { persist });
4189
+ this.footer.invalidate();
4190
+ this.updateEditorBorderColor();
4191
+ this.showStatus(persist ? `Default thinking level: ${level}` : `Thinking level: ${level}`);
4192
+ }
4193
+ catch (error) {
4194
+ this.showError(error instanceof Error ? error.message : String(error));
4195
+ }
4196
+ }
4197
+ showThinkingSelector() {
4198
+ this.showSelector((done) => {
4199
+ const selectLevel = (level, persist) => {
4200
+ this.selectThinkingLevel(level, persist);
4201
+ done();
4202
+ };
4203
+ const selector = new ThinkingSelectorComponent(this.session.thinkingLevel ?? DEFAULT_THINKING_LEVEL, this.session.getAvailableThinkingLevels(), (level) => selectLevel(level, false), () => {
4204
+ done();
4205
+ this.ui.requestRender();
4206
+ }, (level) => selectLevel(level, true), this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);
4207
+ return { component: selector, focus: selector };
4208
+ });
4209
+ }
4070
4210
  async handleModelCommand(searchTerm) {
4071
4211
  if (!searchTerm) {
4072
4212
  this.showModelSelector();
@@ -4075,7 +4215,7 @@ export class InteractiveMode {
4075
4215
  const model = await this.findExactModelMatch(searchTerm);
4076
4216
  if (model) {
4077
4217
  try {
4078
- await this.session.setModel(model);
4218
+ await this.session.setModel(model, { persist: false });
4079
4219
  this.footer.invalidate();
4080
4220
  this.updateEditorBorderColor();
4081
4221
  this.showStatus(`Model: ${model.id}`);
@@ -4204,13 +4344,13 @@ export class InteractiveMode {
4204
4344
  }
4205
4345
  showModelSelector(initialSearchInput) {
4206
4346
  this.showSelector((done) => {
4207
- const selector = new ModelSelectorComponent(this.ui, this.session.model, this.settingsManager, this.session.modelRuntime, this.session.scopedModels, async (model) => {
4347
+ const selectModel = async (model, persist) => {
4208
4348
  try {
4209
- await this.session.setModel(model);
4349
+ await this.session.setModel(model, { persist });
4210
4350
  this.footer.invalidate();
4211
4351
  this.updateEditorBorderColor();
4212
4352
  done();
4213
- this.showStatus(`Model: ${model.id}`);
4353
+ this.showStatus(persist ? `Default model: ${model.provider}/${model.id}` : `Model: ${model.id}`);
4214
4354
  void this.maybeWarnAboutAnthropicSubscriptionAuth(model);
4215
4355
  this.checkDaxnutsEasterEgg(model);
4216
4356
  }
@@ -4218,10 +4358,13 @@ export class InteractiveMode {
4218
4358
  done();
4219
4359
  this.showError(error instanceof Error ? error.message : String(error));
4220
4360
  }
4221
- }, () => {
4361
+ };
4362
+ const defaultProvider = this.settingsManager.getDefaultProvider();
4363
+ const defaultModel = this.settingsManager.getDefaultModel();
4364
+ const selector = new ModelSelectorComponent(this.ui, this.session.model, this.session.modelRuntime, this.session.scopedModels, (model) => selectModel(model, false), () => {
4222
4365
  done();
4223
4366
  this.ui.requestRender();
4224
- }, initialSearchInput);
4367
+ }, initialSearchInput, (model) => selectModel(model, true), defaultProvider && defaultModel ? { provider: defaultProvider, id: defaultModel } : undefined);
4225
4368
  return { component: selector, focus: selector, dispose: () => selector.dispose() };
4226
4369
  });
4227
4370
  }
@@ -4777,7 +4920,11 @@ export class InteractiveMode {
4777
4920
  if (isUnknownModel(previousModel)) {
4778
4921
  const availableModels = this.session.modelRuntime.getAvailableSnapshot();
4779
4922
  const providerModels = availableModels.filter((model) => model.provider === providerId);
4780
- if (!hasDefaultModelProvider(providerId)) {
4923
+ // Matches LLAMA_PROVIDER_ID from extensions/llama/provider.ts; kept inline to avoid coupling interactive mode to the built-in extension.
4924
+ if (providerId === "llama.cpp") {
4925
+ selectionError = llamaCppPostLoginGuidance(actionLabel, providerModels.length);
4926
+ }
4927
+ else if (!hasDefaultModelProvider(providerId)) {
4781
4928
  selectionError = `${actionLabel}, but no default model is configured for provider "${providerId}". Use /model to select a model.`;
4782
4929
  }
4783
4930
  else if (providerModels.length === 0) {
@@ -4791,7 +4938,7 @@ export class InteractiveMode {
4791
4938
  }
4792
4939
  else {
4793
4940
  try {
4794
- await this.session.setModel(selectedModel);
4941
+ await this.session.setModel(selectedModel, { persist: true });
4795
4942
  }
4796
4943
  catch (error) {
4797
4944
  selectedModel = undefined;