@bastani/atomic 0.8.30 → 0.8.31-alpha.2

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 (205) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +12 -10
  3. package/dist/builtin/cursor/CHANGELOG.md +4 -0
  4. package/dist/builtin/cursor/package.json +2 -2
  5. package/dist/builtin/intercom/CHANGELOG.md +4 -0
  6. package/dist/builtin/intercom/package.json +2 -2
  7. package/dist/builtin/mcp/CHANGELOG.md +4 -0
  8. package/dist/builtin/mcp/package.json +3 -3
  9. package/dist/builtin/subagents/CHANGELOG.md +13 -0
  10. package/dist/builtin/subagents/agents/codebase-online-researcher.md +8 -8
  11. package/dist/builtin/subagents/agents/debugger.md +6 -6
  12. package/dist/builtin/subagents/package.json +4 -4
  13. package/dist/builtin/subagents/skills/effective-liteparse/SKILL.md +118 -0
  14. package/dist/builtin/subagents/skills/effective-liteparse/scripts/search.py +128 -0
  15. package/dist/builtin/subagents/skills/playwright-cli/SKILL.md +404 -0
  16. package/dist/builtin/subagents/skills/playwright-cli/references/element-attributes.md +23 -0
  17. package/dist/builtin/subagents/skills/playwright-cli/references/playwright-tests.md +39 -0
  18. package/dist/builtin/subagents/skills/playwright-cli/references/request-mocking.md +87 -0
  19. package/dist/builtin/subagents/skills/playwright-cli/references/running-code.md +241 -0
  20. package/dist/builtin/subagents/skills/playwright-cli/references/session-management.md +225 -0
  21. package/dist/builtin/subagents/skills/playwright-cli/references/spec-driven-testing.md +305 -0
  22. package/dist/builtin/subagents/skills/playwright-cli/references/storage-state.md +275 -0
  23. package/dist/builtin/subagents/skills/playwright-cli/references/test-generation.md +134 -0
  24. package/dist/builtin/subagents/skills/playwright-cli/references/tracing.md +139 -0
  25. package/dist/builtin/subagents/skills/playwright-cli/references/video-recording.md +143 -0
  26. package/dist/builtin/web-access/CHANGELOG.md +4 -0
  27. package/dist/builtin/web-access/package.json +2 -2
  28. package/dist/builtin/workflows/CHANGELOG.md +16 -0
  29. package/dist/builtin/workflows/README.md +4 -4
  30. package/dist/builtin/workflows/builtin/deep-research-codebase.ts +1 -1
  31. package/dist/builtin/workflows/builtin/goal.ts +2 -2
  32. package/dist/builtin/workflows/builtin/open-claude-design.ts +60 -57
  33. package/dist/builtin/workflows/builtin/ralph.ts +117 -14
  34. package/dist/builtin/workflows/builtin/shared-prompts.ts +1 -1
  35. package/dist/builtin/workflows/package.json +2 -2
  36. package/dist/builtin/workflows/skills/research-codebase/SKILL.md +1 -1
  37. package/dist/builtin/workflows/src/extension/workflow-schema.ts +3 -1
  38. package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +5 -0
  39. package/dist/builtin/workflows/src/runs/shared/model-fallback.ts +95 -8
  40. package/dist/builtin/workflows/src/shared/authoring-contract.d.ts +11 -0
  41. package/dist/cli/args.d.ts +1 -0
  42. package/dist/cli/args.d.ts.map +1 -1
  43. package/dist/cli/args.js +21 -1
  44. package/dist/cli/args.js.map +1 -1
  45. package/dist/cli/list-models.d.ts.map +1 -1
  46. package/dist/cli/list-models.js +2 -1
  47. package/dist/cli/list-models.js.map +1 -1
  48. package/dist/core/agent-session-services.d.ts +2 -0
  49. package/dist/core/agent-session-services.d.ts.map +1 -1
  50. package/dist/core/agent-session-services.js +2 -0
  51. package/dist/core/agent-session-services.js.map +1 -1
  52. package/dist/core/agent-session.d.ts +18 -0
  53. package/dist/core/agent-session.d.ts.map +1 -1
  54. package/dist/core/agent-session.js +182 -19
  55. package/dist/core/agent-session.js.map +1 -1
  56. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  57. package/dist/core/compaction/branch-summarization.js +20 -5
  58. package/dist/core/compaction/branch-summarization.js.map +1 -1
  59. package/dist/core/compaction/context-compaction.d.ts.map +1 -1
  60. package/dist/core/compaction/context-compaction.js +14 -3
  61. package/dist/core/compaction/context-compaction.js.map +1 -1
  62. package/dist/core/context-window.d.ts +39 -0
  63. package/dist/core/context-window.d.ts.map +1 -0
  64. package/dist/core/context-window.js +99 -0
  65. package/dist/core/context-window.js.map +1 -0
  66. package/dist/core/copilot-errors.d.ts +9 -0
  67. package/dist/core/copilot-errors.d.ts.map +1 -0
  68. package/dist/core/copilot-errors.js +32 -0
  69. package/dist/core/copilot-errors.js.map +1 -0
  70. package/dist/core/copilot-model-catalog.d.ts +135 -0
  71. package/dist/core/copilot-model-catalog.d.ts.map +1 -0
  72. package/dist/core/copilot-model-catalog.js +257 -0
  73. package/dist/core/copilot-model-catalog.js.map +1 -0
  74. package/dist/core/export-html/template.js +10 -1
  75. package/dist/core/extensions/types.d.ts +3 -1
  76. package/dist/core/extensions/types.d.ts.map +1 -1
  77. package/dist/core/extensions/types.js.map +1 -1
  78. package/dist/core/model-registry.d.ts +10 -0
  79. package/dist/core/model-registry.d.ts.map +1 -1
  80. package/dist/core/model-registry.js +107 -4
  81. package/dist/core/model-registry.js.map +1 -1
  82. package/dist/core/model-resolver.d.ts.map +1 -1
  83. package/dist/core/model-resolver.js +4 -0
  84. package/dist/core/model-resolver.js.map +1 -1
  85. package/dist/core/project-trust.d.ts.map +1 -1
  86. package/dist/core/project-trust.js +2 -1
  87. package/dist/core/project-trust.js.map +1 -1
  88. package/dist/core/provider-attribution.d.ts.map +1 -1
  89. package/dist/core/provider-attribution.js +17 -7
  90. package/dist/core/provider-attribution.js.map +1 -1
  91. package/dist/core/sdk.d.ts +8 -0
  92. package/dist/core/sdk.d.ts.map +1 -1
  93. package/dist/core/sdk.js +58 -0
  94. package/dist/core/sdk.js.map +1 -1
  95. package/dist/core/session-manager.d.ts +8 -1
  96. package/dist/core/session-manager.d.ts.map +1 -1
  97. package/dist/core/session-manager.js +19 -3
  98. package/dist/core/session-manager.js.map +1 -1
  99. package/dist/core/settings-manager.d.ts +15 -0
  100. package/dist/core/settings-manager.d.ts.map +1 -1
  101. package/dist/core/settings-manager.js +124 -1
  102. package/dist/core/settings-manager.js.map +1 -1
  103. package/dist/core/system-prompt.d.ts.map +1 -1
  104. package/dist/core/system-prompt.js +1 -0
  105. package/dist/core/system-prompt.js.map +1 -1
  106. package/dist/core/tools/edit-diff.d.ts +1 -2
  107. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  108. package/dist/core/tools/edit-diff.js +1 -2
  109. package/dist/core/tools/edit-diff.js.map +1 -1
  110. package/dist/index.d.ts +3 -1
  111. package/dist/index.d.ts.map +1 -1
  112. package/dist/index.js +2 -0
  113. package/dist/index.js.map +1 -1
  114. package/dist/main.d.ts.map +1 -1
  115. package/dist/main.js +24 -1
  116. package/dist/main.js.map +1 -1
  117. package/dist/modes/index.d.ts +1 -1
  118. package/dist/modes/index.d.ts.map +1 -1
  119. package/dist/modes/index.js.map +1 -1
  120. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  121. package/dist/modes/interactive/components/config-selector.js +5 -7
  122. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  123. package/dist/modes/interactive/components/context-window-selector.d.ts +53 -0
  124. package/dist/modes/interactive/components/context-window-selector.d.ts.map +1 -0
  125. package/dist/modes/interactive/components/context-window-selector.js +136 -0
  126. package/dist/modes/interactive/components/context-window-selector.js.map +1 -0
  127. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  128. package/dist/modes/interactive/components/model-selector.js +2 -1
  129. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  130. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  131. package/dist/modes/interactive/components/scoped-models-selector.js +4 -1
  132. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  133. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  134. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  135. package/dist/modes/interactive/components/settings-selector.js +165 -15
  136. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  137. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  138. package/dist/modes/interactive/components/tree-selector.js +51 -4
  139. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  140. package/dist/modes/interactive/interactive-mode.d.ts +6 -1
  141. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  142. package/dist/modes/interactive/interactive-mode.js +115 -55
  143. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  144. package/dist/modes/interactive/model-search.d.ts +7 -0
  145. package/dist/modes/interactive/model-search.d.ts.map +1 -0
  146. package/dist/modes/interactive/model-search.js +6 -0
  147. package/dist/modes/interactive/model-search.js.map +1 -0
  148. package/dist/modes/interactive/theme/theme-controller.d.ts +30 -0
  149. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -0
  150. package/dist/modes/interactive/theme/theme-controller.js +108 -0
  151. package/dist/modes/interactive/theme/theme-controller.js.map +1 -0
  152. package/dist/modes/interactive/theme/theme-schema.json +2 -1
  153. package/dist/modes/interactive/theme/theme.d.ts +5 -0
  154. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  155. package/dist/modes/interactive/theme/theme.js +70 -29
  156. package/dist/modes/interactive/theme/theme.js.map +1 -1
  157. package/dist/modes/rpc/rpc-client.d.ts +14 -2
  158. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  159. package/dist/modes/rpc/rpc-client.js +23 -3
  160. package/dist/modes/rpc/rpc-client.js.map +1 -1
  161. package/dist/modes/rpc/rpc-mode.d.ts +1 -1
  162. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  163. package/dist/modes/rpc/rpc-mode.js +31 -2
  164. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  165. package/dist/modes/rpc/rpc-types.d.ts +23 -0
  166. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  167. package/dist/modes/rpc/rpc-types.js.map +1 -1
  168. package/dist/package-manager-cli.d.ts.map +1 -1
  169. package/dist/package-manager-cli.js +39 -9
  170. package/dist/package-manager-cli.js.map +1 -1
  171. package/docs/custom-provider.md +4 -1
  172. package/docs/extensions.md +21 -0
  173. package/docs/json.md +3 -1
  174. package/docs/models.md +78 -2
  175. package/docs/packages.md +13 -9
  176. package/docs/providers.md +3 -0
  177. package/docs/quickstart.md +14 -0
  178. package/docs/rpc.md +80 -1
  179. package/docs/sdk.md +35 -11
  180. package/docs/session-format.md +15 -1
  181. package/docs/sessions.md +1 -1
  182. package/docs/settings.md +12 -2
  183. package/docs/themes.md +3 -1
  184. package/docs/tui.md +1 -1
  185. package/docs/usage.md +12 -9
  186. package/docs/workflows.md +34 -10
  187. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  188. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  189. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  190. package/examples/extensions/gondolin/package-lock.json +2 -2
  191. package/examples/extensions/gondolin/package.json +1 -1
  192. package/examples/extensions/preset.ts +10 -4
  193. package/examples/extensions/provider-payload.ts +5 -5
  194. package/examples/extensions/sandbox/index.ts +2 -2
  195. package/examples/extensions/sandbox/package-lock.json +3 -3
  196. package/examples/extensions/sandbox/package.json +2 -2
  197. package/examples/extensions/subagent/agents.ts +2 -2
  198. package/examples/extensions/subagent/index.ts +4 -2
  199. package/examples/extensions/with-deps/package-lock.json +2 -2
  200. package/examples/extensions/with-deps/package.json +1 -1
  201. package/package.json +5 -5
  202. package/dist/builtin/subagents/skills/browser/EXAMPLES.md +0 -151
  203. package/dist/builtin/subagents/skills/browser/LICENSE.txt +0 -21
  204. package/dist/builtin/subagents/skills/browser/REFERENCE.md +0 -451
  205. package/dist/builtin/subagents/skills/browser/SKILL.md +0 -170
@@ -48,6 +48,9 @@ import { CustomMessageComponent } from "./components/custom-message.js";
48
48
  import { DaxnutsComponent } from "./components/daxnuts.js";
49
49
  import { renderAtomicAnsiBanner } from "./components/atomic-banner.js";
50
50
  import { DynamicBorder } from "./components/dynamic-border.js";
51
+ import { ContextWindowSelectorComponent } from "./components/context-window-selector.js";
52
+ import { formatContextWindow } from "../../core/context-window.js";
53
+ import { copilotApiBaseUrlFromToken, copilotCatalogCacheHost, copilotCatalogCachePath, fetchCopilotModelCatalog, readCopilotCatalogCache, setActiveCopilotModelCatalog, writeCopilotCatalogCache, } from "../../core/copilot-model-catalog.js";
51
54
  import { EarendilAnnouncementComponent } from "./components/earendil-announcement.js";
52
55
  import { ExtensionEditorComponent } from "./components/extension-editor.js";
53
56
  import { ExtensionInputComponent } from "./components/extension-input.js";
@@ -68,7 +71,9 @@ import { TrustSelectorComponent } from "./components/trust-selector.js";
68
71
  import { hasProjectConfigDir, ProjectTrustStore } from "../../core/trust-manager.js";
69
72
  import { UserMessageComponent } from "./components/user-message.js";
70
73
  import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
71
- import { detectTerminalBackgroundTheme, getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, initTheme, onThemeChange, setRegisteredThemes, setTheme, setThemeInstance, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
74
+ import { getModelSearchText } from "./model-search.js";
75
+ import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, onThemeChange, setRegisteredThemes, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
76
+ import { InteractiveThemeController } from "./theme/theme-controller.js";
72
77
  function isExpandable(obj) {
73
78
  return (typeof obj === "object" &&
74
79
  obj !== null &&
@@ -161,6 +166,8 @@ export class InteractiveMode {
161
166
  constructor(runtimeHost, options = {}) {
162
167
  this.autocompleteProviderWrappers = [];
163
168
  this.isInitialized = false;
169
+ // GitHub Copilot CAPI context-window catalog load state (gated on the Copilot provider).
170
+ this.copilotCatalogApplied = false;
164
171
  this.pendingUserInputs = [];
165
172
  this.loadingAnimation = undefined;
166
173
  this.workingMessage = undefined;
@@ -265,23 +272,7 @@ export class InteractiveMode {
265
272
  this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
266
273
  // Register themes from resource loader and initialize
267
274
  setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
268
- initTheme(this.settingsManager.getTheme(), true);
269
- }
270
- async detectThemeIfUnset() {
271
- if (this.settingsManager.getTheme()) {
272
- return;
273
- }
274
- const detection = await detectTerminalBackgroundTheme({ ui: this.ui, timeoutMs: 100 });
275
- const result = setTheme(detection.theme, true);
276
- if (!result.success) {
277
- return;
278
- }
279
- if (detection.confidence === "high") {
280
- this.settingsManager.setTheme(detection.theme);
281
- await this.settingsManager.flush();
282
- }
283
- this.updateEditorBorderColor();
284
- this.ui.requestRender();
275
+ this.themeController = new InteractiveThemeController(this.ui, this.settingsManager, (message) => this.showError(message), () => this.updateEditorBorderColor());
285
276
  }
286
277
  getAutocompleteSourceTag(sourceInfo) {
287
278
  if (!sourceInfo) {
@@ -356,10 +347,11 @@ export class InteractiveMode {
356
347
  const items = models.map((m) => ({
357
348
  id: m.id,
358
349
  provider: m.provider,
350
+ name: m.name,
359
351
  label: `${m.provider}/${m.id}`,
360
352
  }));
361
- // Fuzzy filter by model ID + provider (allows "opus anthropic" to match)
362
- const filtered = fuzzyFilter(items, prefix, (item) => `${item.id} ${item.provider}`);
353
+ // Fuzzy filter by model ID + provider in either order.
354
+ const filtered = fuzzyFilter(items, prefix, getModelSearchText);
363
355
  if (filtered.length === 0)
364
356
  return null;
365
357
  return filtered.map((item) => ({
@@ -450,7 +442,7 @@ export class InteractiveMode {
450
442
  this.registerSignalHandlers();
451
443
  // Load changelog (only show new entries, skip for resumed sessions)
452
444
  this.changelogMarkdown = this.getChangelogForDisplay();
453
- // Add header container as first child. Populate it after detectThemeIfUnset.
445
+ // Add header container as first child. Populate it after theme initialization.
454
446
  this.ui.addChild(this.headerContainer);
455
447
  this.ui.addChild(this.chatContainer);
456
448
  this.ui.addChild(this.pendingMessagesContainer);
@@ -478,7 +470,7 @@ export class InteractiveMode {
478
470
  this.ui.start();
479
471
  recordTimeSinceReset("time-to-first-frame");
480
472
  this.isInitialized = true;
481
- await this.detectThemeIfUnset();
473
+ await this.themeController.applyFromSettings();
482
474
  // Add the quiet startup identity (unless silenced). Resource details are
483
475
  // disclosed separately in the chat canvas via the tools/resources toggle.
484
476
  if (this.options.verbose || !this.settingsManager.getQuietStartup()) {
@@ -541,6 +533,9 @@ export class InteractiveMode {
541
533
  */
542
534
  async run() {
543
535
  await this.init();
536
+ // Load GitHub Copilot context-window tiers from CAPI early (gated on the Copilot provider) so
537
+ // the footer and /model picker reflect GitHub's real windows. Best-effort, never blocks startup.
538
+ void this.refreshCopilotModelCatalog();
544
539
  // Start version check asynchronously
545
540
  checkForNewPiVersion(this.version).then((newVersion) => {
546
541
  if (newVersion) {
@@ -1853,16 +1848,11 @@ export class InteractiveMode {
1853
1848
  getTheme: (name) => getThemeByName(name),
1854
1849
  setTheme: (themeOrName) => {
1855
1850
  if (themeOrName instanceof Theme) {
1856
- setThemeInstance(themeOrName);
1857
- this.ui.requestRender();
1858
- return { success: true };
1851
+ return this.themeController.setThemeInstance(themeOrName);
1859
1852
  }
1860
- const result = setTheme(themeOrName, true);
1861
- if (result.success) {
1862
- if (this.settingsManager.getTheme() !== themeOrName) {
1863
- this.settingsManager.setTheme(themeOrName);
1864
- }
1865
- this.ui.requestRender();
1853
+ const result = this.themeController.setThemeName(themeOrName);
1854
+ if (result.success && this.settingsManager.getThemeSetting() !== themeOrName) {
1855
+ this.settingsManager.setTheme(themeOrName);
1866
1856
  }
1867
1857
  return result;
1868
1858
  },
@@ -2619,6 +2609,11 @@ export class InteractiveMode {
2619
2609
  this.refreshBuiltInHeader();
2620
2610
  this.updateEditorBorderColor();
2621
2611
  break;
2612
+ case "context_window_changed":
2613
+ this.footer.invalidate();
2614
+ this.usageMeter.invalidate();
2615
+ this.ui.requestRender();
2616
+ break;
2622
2617
  case "message_start":
2623
2618
  if (event.message.role === "custom") {
2624
2619
  this.addMessageToChat(event.message);
@@ -3118,6 +3113,7 @@ export class InteractiveMode {
3118
3113
  // dispatch and re-sends the signal if only its own listeners remain.
3119
3114
  if (options?.fromSignal) {
3120
3115
  await this.runtimeHost.dispose();
3116
+ this.themeController.disableAutoSync();
3121
3117
  // Drain any in-flight Kitty key release events before stopping.
3122
3118
  // This prevents escape sequences from leaking to the parent shell over slow SSH.
3123
3119
  await this.ui.terminal.drainInput(1000);
@@ -3126,6 +3122,7 @@ export class InteractiveMode {
3126
3122
  }
3127
3123
  // Drain any in-flight Kitty key release events before stopping.
3128
3124
  // This prevents escape sequences from leaking to the parent shell over slow SSH.
3125
+ this.themeController.disableAutoSync();
3129
3126
  await this.ui.terminal.drainInput(1000);
3130
3127
  this.stop();
3131
3128
  await this.runtimeHost.dispose();
@@ -3413,7 +3410,7 @@ export class InteractiveMode {
3413
3410
  this.ui.requestRender();
3414
3411
  }
3415
3412
  showPackageUpdateNotification(packages) {
3416
- const action = theme.fg("accent", `${APP_NAME} update`);
3413
+ const action = theme.fg("accent", `${APP_NAME} update --extensions`);
3417
3414
  const updateInstruction = theme.fg("muted", "Package updates are available. Run ") + action;
3418
3415
  const packageLines = packages.map((pkg) => `- ${pkg}`).join("\n");
3419
3416
  this.chatContainer.addChild(new Spacer(1));
@@ -3655,7 +3652,8 @@ export class InteractiveMode {
3655
3652
  httpIdleTimeoutMs: this.settingsManager.getHttpIdleTimeoutMs(),
3656
3653
  thinkingLevel: this.session.thinkingLevel,
3657
3654
  availableThinkingLevels: this.session.getAvailableThinkingLevels(),
3658
- currentTheme: this.settingsManager.getTheme() || "dark",
3655
+ currentTheme: this.settingsManager.getThemeSetting() || "dark",
3656
+ terminalTheme: this.themeController.getTerminalTheme(),
3659
3657
  availableThemes: getAvailableThemes(),
3660
3658
  hideThinkingBlock: this.hideThinkingBlock,
3661
3659
  collapseChangelog: this.settingsManager.getCollapseChangelog(),
@@ -3720,21 +3718,11 @@ export class InteractiveMode {
3720
3718
  this.footer.invalidate();
3721
3719
  this.updateEditorBorderColor();
3722
3720
  },
3723
- onThemeChange: (themeName) => {
3724
- const result = setTheme(themeName, true);
3725
- this.settingsManager.setTheme(themeName);
3726
- this.ui.invalidate();
3727
- if (!result.success) {
3728
- this.showError(`Failed to load theme "${themeName}": ${result.error}\nFell back to dark theme.`);
3729
- }
3730
- },
3731
- onThemePreview: (themeName) => {
3732
- const result = setTheme(themeName, true);
3733
- if (result.success) {
3734
- this.ui.invalidate();
3735
- this.ui.requestRender();
3736
- }
3721
+ onThemeChange: (themeSetting) => {
3722
+ this.settingsManager.setTheme(themeSetting);
3723
+ void this.themeController.applyFromSettings();
3737
3724
  },
3725
+ onThemePreview: (themeName) => this.themeController.preview(themeName),
3738
3726
  onHideThinkingBlockChange: (hidden) => {
3739
3727
  this.hideThinkingBlock = hidden;
3740
3728
  this.settingsManager.setHideThinkingBlock(hidden);
@@ -3832,6 +3820,7 @@ export class InteractiveMode {
3832
3820
  if (this.session.scopedModels.length > 0) {
3833
3821
  return this.session.scopedModels.map((scoped) => scoped.model);
3834
3822
  }
3823
+ await this.refreshCopilotModelCatalog();
3835
3824
  this.session.modelRegistry.refresh();
3836
3825
  try {
3837
3826
  return await this.session.modelRegistry.getAvailable();
@@ -3840,6 +3829,51 @@ export class InteractiveMode {
3840
3829
  return [];
3841
3830
  }
3842
3831
  }
3832
+ /**
3833
+ * Load GitHub Copilot's context-window tiers from the live CAPI catalog, but only when the user
3834
+ * actually has the GitHub Copilot provider authenticated. Cache-first (30-min TTL); on a
3835
+ * successful load the model registry is refreshed so the /model context-window picker reflects
3836
+ * GitHub's real tiers (e.g. gpt-5.5 1.05m, Claude/Gemini 1m). Best-effort and gated: offline,
3837
+ * unauthenticated, or non-Copilot sessions silently keep no long-context options.
3838
+ */
3839
+ async refreshCopilotModelCatalog() {
3840
+ if (this.copilotCatalogApplied)
3841
+ return;
3842
+ if (!this.copilotCatalogInFlight) {
3843
+ this.copilotCatalogInFlight = this.loadCopilotModelCatalog();
3844
+ }
3845
+ try {
3846
+ await this.copilotCatalogInFlight;
3847
+ }
3848
+ finally {
3849
+ this.copilotCatalogInFlight = undefined;
3850
+ }
3851
+ }
3852
+ async loadCopilotModelCatalog() {
3853
+ const registry = this.session.modelRegistry;
3854
+ const cred = registry.authStorage.get("github-copilot");
3855
+ // Gate: do nothing unless the user has the GitHub Copilot provider.
3856
+ if (!cred || cred.type !== "oauth")
3857
+ return;
3858
+ try {
3859
+ const token = await registry.getApiKeyForProvider("github-copilot");
3860
+ if (!token)
3861
+ return;
3862
+ const baseUrl = copilotApiBaseUrlFromToken(token);
3863
+ const cachePath = copilotCatalogCachePath(getAgentDir());
3864
+ let catalog = readCopilotCatalogCache(cachePath, { host: copilotCatalogCacheHost(baseUrl) });
3865
+ if (!catalog) {
3866
+ catalog = await fetchCopilotModelCatalog({ token, baseUrl });
3867
+ writeCopilotCatalogCache(cachePath, baseUrl, catalog);
3868
+ }
3869
+ setActiveCopilotModelCatalog(catalog);
3870
+ registry.refresh();
3871
+ this.copilotCatalogApplied = true;
3872
+ }
3873
+ catch {
3874
+ // Best-effort: leave the active catalog as-is on any failure (offline, auth, parse).
3875
+ }
3876
+ }
3843
3877
  /** Update the footer's available provider count from current model candidates */
3844
3878
  async updateAvailableProviderCount() {
3845
3879
  const models = await this.getModelCandidates();
@@ -3882,9 +3916,14 @@ export class InteractiveMode {
3882
3916
  this.footer.invalidate();
3883
3917
  this.updateEditorBorderColor();
3884
3918
  done();
3885
- this.showStatus(`Model: ${model.id}`);
3886
3919
  void this.maybeWarnAboutAnthropicSubscriptionAuth(model);
3887
3920
  this.checkDaxnutsEasterEgg(model);
3921
+ if (this.session.supportsContextWindowSelection()) {
3922
+ this.showContextWindowSelector(model);
3923
+ }
3924
+ else {
3925
+ this.showStatus(`Model: ${model.id}`);
3926
+ }
3888
3927
  }
3889
3928
  catch (error) {
3890
3929
  done();
@@ -3897,6 +3936,32 @@ export class InteractiveMode {
3897
3936
  return { component: selector, focus: selector };
3898
3937
  });
3899
3938
  }
3939
+ showContextWindowSelector(model) {
3940
+ const availableContextWindows = this.session.getAvailableContextWindows();
3941
+ const currentContextWindow = this.session.model?.contextWindow ?? availableContextWindows[0] ?? 0;
3942
+ this.showSelector((done) => {
3943
+ const selector = new ContextWindowSelectorComponent(model.name ?? model.id, availableContextWindows, currentContextWindow, (contextWindow) => {
3944
+ try {
3945
+ this.session.setContextWindow(contextWindow, {
3946
+ persistDefault: true,
3947
+ });
3948
+ this.footer.invalidate();
3949
+ this.usageMeter.invalidate();
3950
+ this.updateEditorBorderColor();
3951
+ done();
3952
+ this.showStatus(`Model: ${model.id} \u00b7 ${formatContextWindow(contextWindow)} context`);
3953
+ }
3954
+ catch (error) {
3955
+ done();
3956
+ this.showError(error instanceof Error ? error.message : String(error));
3957
+ }
3958
+ }, () => {
3959
+ done();
3960
+ this.showStatus(`Model: ${model.id}`);
3961
+ });
3962
+ return { component: selector, focus: selector };
3963
+ });
3964
+ }
3900
3965
  async showModelsSelector() {
3901
3966
  // Get all available models
3902
3967
  this.session.modelRegistry.refresh();
@@ -4602,13 +4667,7 @@ export class InteractiveMode {
4602
4667
  }
4603
4668
  setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
4604
4669
  this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
4605
- const themeName = this.settingsManager.getTheme();
4606
- const themeResult = themeName
4607
- ? setTheme(themeName, true)
4608
- : { success: true };
4609
- if (!themeResult.success) {
4610
- this.showError(`Failed to load theme "${themeName}": ${themeResult.error}\nFell back to dark theme.`);
4611
- }
4670
+ await this.themeController.applyFromSettings();
4612
4671
  const editorPaddingX = this.settingsManager.getEditorPaddingX();
4613
4672
  const autocompleteMaxVisible = this.settingsManager.getAutocompleteMaxVisible();
4614
4673
  this.defaultEditor.setPaddingX(editorPaddingX);
@@ -5189,6 +5248,7 @@ export class InteractiveMode {
5189
5248
  this.loadingAnimation.stop();
5190
5249
  this.loadingAnimation = undefined;
5191
5250
  }
5251
+ this.themeController.disableAutoSync();
5192
5252
  this.clearExtensionTerminalInputListeners();
5193
5253
  this.footer.dispose();
5194
5254
  this.footerDataProvider.dispose();