@gajae-code/coding-agent 0.16.1 → 0.16.4

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 (146) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/dist/types/cli/args.d.ts +2 -2
  3. package/dist/types/cli/root-flags.d.ts +4 -2
  4. package/dist/types/cli.d.ts +1 -1
  5. package/dist/types/commands/launch.d.ts +1 -1
  6. package/dist/types/config/autorouting-tier-map.d.ts +3 -0
  7. package/dist/types/config/model-registry.d.ts +4 -0
  8. package/dist/types/config/model-resolver.d.ts +9 -0
  9. package/dist/types/config/settings-schema.d.ts +29 -1
  10. package/dist/types/discovery/claude-plugins.d.ts +3 -1
  11. package/dist/types/discovery/helpers.d.ts +2 -0
  12. package/dist/types/extensibility/gjc-plugins/mcp-policy.d.ts +16 -0
  13. package/dist/types/extensibility/gjc-plugins/paths.d.ts +2 -0
  14. package/dist/types/extensibility/runtime-skill-discovery.d.ts +1 -1
  15. package/dist/types/extensibility/shared-events.d.ts +1 -1
  16. package/dist/types/gjc-runtime/session-state-lock.d.ts +2 -0
  17. package/dist/types/modes/components/assistant-message.d.ts +1 -2
  18. package/dist/types/modes/components/settings-selector.d.ts +6 -1
  19. package/dist/types/modes/components/status-line/command.d.ts +35 -0
  20. package/dist/types/modes/components/status-line/index.d.ts +1 -0
  21. package/dist/types/modes/components/status-line/types.d.ts +5 -0
  22. package/dist/types/modes/components/tool-status-header.d.ts +14 -0
  23. package/dist/types/modes/controllers/event-controller.d.ts +6 -0
  24. package/dist/types/modes/interactive-mode.d.ts +1 -0
  25. package/dist/types/modes/types.d.ts +1 -0
  26. package/dist/types/runtime-mcp/client.d.ts +9 -1
  27. package/dist/types/runtime-mcp/manager.d.ts +7 -1
  28. package/dist/types/runtime-mcp/pool.d.ts +22 -3
  29. package/dist/types/runtime-mcp/transports/stdio.d.ts +0 -4
  30. package/dist/types/runtime-mcp/types.d.ts +22 -0
  31. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +12 -0
  32. package/dist/types/sdk/bus/slack-live-provider.d.ts +6 -0
  33. package/dist/types/sdk/bus/slack-provider.d.ts +12 -0
  34. package/dist/types/sdk/cli/rows.d.ts +7 -0
  35. package/dist/types/sdk/host/events.d.ts +1 -0
  36. package/dist/types/sdk/host/host.d.ts +2 -0
  37. package/dist/types/sdk/host/session-runtime.d.ts +23 -0
  38. package/dist/types/sdk/session-api-key.d.ts +26 -0
  39. package/dist/types/sdk/transport/serve-cli.d.ts +1 -1
  40. package/dist/types/session/agent-session.d.ts +9 -0
  41. package/dist/types/setup/paseo/announce.d.ts +111 -0
  42. package/dist/types/setup/paseo/setup-deps.d.ts +2 -1
  43. package/dist/types/system-prompt.d.ts +2 -0
  44. package/dist/types/task/executor.d.ts +2 -1
  45. package/dist/types/tools/fetch.d.ts +7 -0
  46. package/dist/types/tools/index.d.ts +8 -2
  47. package/dist/types/tools/path-utils.d.ts +2 -0
  48. package/dist/types/tools/search.d.ts +6 -0
  49. package/dist/types/utils/external-editor.d.ts +5 -0
  50. package/package.json +7 -7
  51. package/scripts/capture-status-line-custom-editor-showcase.ts +151 -0
  52. package/scripts/generate-sdk-operation-inventory.ts +9 -0
  53. package/scripts/verify-status-line-custom-editor-showcase.ts +309 -0
  54. package/src/cli/args.ts +20 -9
  55. package/src/cli/root-flags.ts +5 -3
  56. package/src/config/autorouting-tier-map.ts +1 -0
  57. package/src/config/file-lock.ts +181 -17
  58. package/src/config/model-preset-registry.ts +5 -3
  59. package/src/config/model-profiles.ts +40 -0
  60. package/src/config/model-registry.ts +57 -11
  61. package/src/config/model-resolver.ts +48 -0
  62. package/src/config/settings-schema.ts +33 -1
  63. package/src/config/settings.ts +1 -0
  64. package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +11 -11
  65. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +13 -2
  66. package/src/discovery/builtin.ts +13 -4
  67. package/src/discovery/claude-plugins.ts +12 -4
  68. package/src/discovery/helpers.ts +10 -2
  69. package/src/extensibility/gjc-plugins/compiler.ts +208 -58
  70. package/src/extensibility/gjc-plugins/installer.ts +13 -12
  71. package/src/extensibility/gjc-plugins/mcp-policy.ts +81 -52
  72. package/src/extensibility/gjc-plugins/paths.ts +13 -0
  73. package/src/extensibility/gjc-plugins/runtime-adapters.ts +758 -16
  74. package/src/extensibility/runtime-skill-discovery.ts +77 -151
  75. package/src/extensibility/shared-events.ts +1 -1
  76. package/src/extensibility/skills.ts +44 -16
  77. package/src/gjc-runtime/repository-binding.ts +1 -1
  78. package/src/gjc-runtime/session-state-lock.ts +94 -6
  79. package/src/internal-urls/docs-index.generated.ts +10 -10
  80. package/src/main.ts +72 -34
  81. package/src/modes/DESIGN.md +54 -0
  82. package/src/modes/acp/acp-agent.ts +29 -14
  83. package/src/modes/components/assistant-message.ts +22 -9
  84. package/src/modes/components/model-selector.ts +166 -33
  85. package/src/modes/components/settings-selector.ts +1075 -235
  86. package/src/modes/components/status-line/command.ts +190 -0
  87. package/src/modes/components/status-line/index.ts +1 -0
  88. package/src/modes/components/status-line/segments.ts +32 -5
  89. package/src/modes/components/status-line/types.ts +5 -0
  90. package/src/modes/components/tool-status-header.ts +393 -21
  91. package/src/modes/controllers/command-controller.ts +2 -0
  92. package/src/modes/controllers/event-controller.ts +148 -5
  93. package/src/modes/controllers/selector-controller.ts +21 -0
  94. package/src/modes/interactive-mode.ts +47 -1
  95. package/src/modes/types.ts +1 -0
  96. package/src/modes/utils/ui-helpers.ts +1 -0
  97. package/src/prompts/system/system-prompt.md +8 -0
  98. package/src/prompts/tools/bash.md +1 -1
  99. package/src/prompts/tools/job.md +1 -1
  100. package/src/prompts/tools/search.md +2 -0
  101. package/src/runtime-mcp/client.ts +60 -17
  102. package/src/runtime-mcp/manager.ts +171 -14
  103. package/src/runtime-mcp/pool.test.ts +209 -0
  104. package/src/runtime-mcp/pool.ts +224 -23
  105. package/src/runtime-mcp/transports/stdio.ts +158 -24
  106. package/src/runtime-mcp/types.ts +24 -0
  107. package/src/sdk/acp/adapter.ts +1 -1
  108. package/src/sdk/broker/broker.ts +5 -4
  109. package/src/sdk/bus/chat-daemon-control.ts +1 -1
  110. package/src/sdk/bus/chat-daemon-runtime.ts +44 -1
  111. package/src/sdk/bus/slack-daemon.ts +92 -2
  112. package/src/sdk/bus/slack-live-provider.ts +12 -0
  113. package/src/sdk/bus/slack-provider.ts +5 -0
  114. package/src/sdk/cli/rows.ts +47 -2
  115. package/src/sdk/cli/session-cli.ts +47 -23
  116. package/src/sdk/host/events.ts +25 -2
  117. package/src/sdk/host/host.ts +4 -1
  118. package/src/sdk/host/session-runtime.test.ts +36 -25
  119. package/src/sdk/host/session-runtime.ts +279 -43
  120. package/src/sdk/protocol/operation-inventory.generated.json +66 -0
  121. package/src/sdk/router/session-router.ts +60 -37
  122. package/src/sdk/session-api-key.ts +94 -0
  123. package/src/sdk/session.ts +73 -30
  124. package/src/sdk/transport/serve-cli.ts +3 -3
  125. package/src/session/agent-session.ts +123 -38
  126. package/src/session/contribution-prep.ts +214 -21
  127. package/src/setup/paseo/announce.ts +450 -0
  128. package/src/setup/paseo/setup-deps.ts +15 -3
  129. package/src/slash-commands/builtin-registry.ts +1 -0
  130. package/src/system-prompt.ts +4 -0
  131. package/src/task/executor.ts +2 -1
  132. package/src/task/index.ts +17 -4
  133. package/src/tools/bash.ts +50 -21
  134. package/src/tools/cron.ts +1 -1
  135. package/src/tools/fetch.ts +29 -1
  136. package/src/tools/index.ts +8 -2
  137. package/src/tools/job.ts +8 -2
  138. package/src/tools/monitor.ts +8 -2
  139. package/src/tools/path-utils.ts +7 -0
  140. package/src/tools/search.ts +116 -58
  141. package/src/tools/skill.ts +22 -2
  142. package/src/tools/subagent.ts +3 -1
  143. package/src/tools/tool-catalog.generated.ts +9 -2
  144. package/src/utils/external-editor.ts +16 -2
  145. package/src/web/insane/bridge.ts +113 -23
  146. package/src/web/search/providers/perplexity.ts +102 -41
@@ -410,6 +410,10 @@ export class ModelSelectorComponent extends Container {
410
410
  #unsubscribeProviderOrderChanged: () => void = () => {};
411
411
  #unsubscribeAuthGeneration: () => void = () => {};
412
412
  #disposed = false;
413
+ #catalogLoaded = false;
414
+ #catalogLoadPromise?: Promise<void>;
415
+ #presetCatalogRefreshPending = false;
416
+ #openSmartRoutingAfterPresetRefresh = false;
413
417
  /** Standalone smart-routing entry: cancel closes the selector instead of falling back to the preset landing. */
414
418
  #smartRoutingOnly = false;
415
419
 
@@ -422,6 +426,8 @@ export class ModelSelectorComponent extends Container {
422
426
  #presetScopeIndex: number = 0;
423
427
  #providerAuthById = new Map<string, boolean>();
424
428
  #bareProfileAuthByName = new Map<string, boolean>();
429
+ // Rebuilt with the provider and bare-selector snapshots on every catalog or credential refresh.
430
+ #profileAuthByName = new Map<string, boolean>();
425
431
  #providerAuthPending: boolean = false;
426
432
  #providerAuthRefreshGeneration = 0;
427
433
  #presetLoginHint?: string;
@@ -545,21 +551,39 @@ export class ModelSelectorComponent extends Container {
545
551
  this.#unsubscribeCatalogChanged = this.#modelRegistry.onCatalogChanged(() => {
546
552
  if (this.#disposed) return;
547
553
  if (this.#viewMode === "presets") {
548
- this.#refreshCatalogView();
554
+ if (this.#presetCatalogRefreshPending) return;
555
+ this.#rebuildRoleModels();
549
556
  this.#clampPresetCursor();
550
557
  void this.#refreshProviderAuth();
551
558
  this.#renderPresetLanding();
552
559
  this.#tui.requestRender();
553
560
  return;
554
561
  }
562
+ if (this.#viewMode === "smart-routing" && this.#smartRoutingPanel) return;
563
+ // The in-flight load already consumes the latest registry snapshot. Avoid
564
+ // materializing the same catalog again from its own change notification.
565
+ if (this.#catalogLoadPromise) return;
566
+ if (!this.#catalogLoaded) {
567
+ void this.#initializeCatalogView();
568
+ return;
569
+ }
555
570
  if (this.#refreshCatalogView()) this.#tui.requestRender();
556
571
  });
557
572
  }
558
573
 
559
- // Advisory drift only, and only while the smart-routing panel is mounted. This
560
- // must not call refreshState: that would discard the user's unsaved draft.
574
+ // Proxy and provider-policy changes invalidate preset availability. Provider-order changes are
575
+ // advisory only while smart routing is mounted and must not discard its draft.
561
576
  this.#unsubscribeProviderOrderChanged = this.#settings.onChanged?.(path => {
562
- if (this.#disposed || path !== "modelProviderOrder") return;
577
+ if (this.#disposed) return;
578
+ if (
579
+ path === "modelProfile.proxyProvider" ||
580
+ path === "modelProfile.proxyMode" ||
581
+ path === "disabledProviders"
582
+ ) {
583
+ if (this.#viewMode === "presets") void this.#refreshProviderAuth();
584
+ return;
585
+ }
586
+ if (path !== "modelProviderOrder") return;
563
587
  if (this.#viewMode !== "smart-routing") return;
564
588
  const panel = this.#smartRoutingPanel;
565
589
  if (!panel) return;
@@ -572,34 +596,21 @@ export class ModelSelectorComponent extends Container {
572
596
  void this.#refreshProviderAuth();
573
597
  }) ?? (() => {});
574
598
 
575
- // Load models and do initial render
576
- this.#loadModels().then(() => {
577
- this.#buildProviderTabs();
578
- if (this.#smartRoutingOnly) {
579
- this.#enterSmartRoutingMode();
580
- this.#tui.requestRender();
581
- return;
582
- }
583
- if (this.#viewMode === "presets" && (this.#modelRegistry.getModelProfiles?.().size ?? 0) === 0) {
584
- this.#viewMode = "models";
585
- }
586
- if (this.#viewMode === "presets") {
587
- void this.#refreshProviderAuth();
599
+ if (this.#viewMode === "presets") {
600
+ // The landing only needs profile definitions and provider authentication.
601
+ // Refresh static configuration without enumerating and canonicalizing the
602
+ // full browser catalog, then resolve preset authentication once.
603
+ this.#presetCatalogRefreshPending = true;
604
+ if ((this.#modelRegistry.getModelProfiles?.().size ?? 0) > 0) {
588
605
  this.#renderPresetLanding();
589
606
  } else {
590
- this.#updateTabBar();
591
- // Always apply the current search query — the user may have typed
592
- // while models were loading asynchronously.
593
- const currentQuery = this.#searchInput.getValue();
594
- if (currentQuery) {
595
- this.#filterModels(currentQuery);
596
- } else {
597
- this.#updateList();
598
- }
607
+ this.#listContainer.addChild(new Text(theme.fg("muted", "Loading model presets..."), 0, 0));
599
608
  }
600
- // Request re-render after models are loaded
601
609
  this.#tui.requestRender();
602
- });
610
+ void this.#initializePresetLanding();
611
+ } else {
612
+ void this.#initializeCatalogView();
613
+ }
603
614
  }
604
615
 
605
616
  override dispose(): void {
@@ -1332,6 +1343,10 @@ export class ModelSelectorComponent extends Container {
1332
1343
  return this.#modelRegistry.getModelProfile?.(name) ?? this.#modelRegistry.getModelProfiles?.().get(name);
1333
1344
  }
1334
1345
 
1346
+ #isPresetAvailabilityPending(): boolean {
1347
+ return this.#presetCatalogRefreshPending || this.#providerAuthPending;
1348
+ }
1349
+
1335
1350
  #isProviderAuthenticated(providerId: string): boolean | undefined {
1336
1351
  return this.#providerAuthById.get(providerId);
1337
1352
  }
@@ -1405,9 +1420,13 @@ export class ModelSelectorComponent extends Container {
1405
1420
  return [...missing].sort((a, b) => a.localeCompare(b));
1406
1421
  }
1407
1422
 
1408
- #isPresetAuthenticated(profileOrProfiles: ModelProfileDefinition | ModelProfileDefinition[]): boolean {
1423
+ #isPresetAuthenticated(
1424
+ profileOrProfiles: ModelProfileDefinition | ModelProfileDefinition[],
1425
+ computeCurrent = false,
1426
+ ): boolean {
1409
1427
  const profiles = Array.isArray(profileOrProfiles) ? profileOrProfiles : [profileOrProfiles];
1410
1428
  return profiles.every(profile => {
1429
+ if (!computeCurrent) return this.#profileAuthByName.get(profile.name) === true;
1411
1430
  if (profile.source !== "user") {
1412
1431
  if (inspectProxyProviderId(this.#settings).status === "invalid") return false;
1413
1432
  const proxyProvider = tryResolveProxyProviderId(this.#settings);
@@ -1503,6 +1522,7 @@ export class ModelSelectorComponent extends Container {
1503
1522
  const refreshGeneration = ++this.#providerAuthRefreshGeneration;
1504
1523
  this.#providerAuthById = new Map();
1505
1524
  this.#bareProfileAuthByName = new Map();
1525
+ this.#profileAuthByName = new Map();
1506
1526
  const availableModels = this.#getProfileAvailableModels();
1507
1527
  const resolutionRegistry = this.#createProfileResolutionRegistry(availableModels);
1508
1528
  const providers = new Set<string>();
@@ -1612,6 +1632,11 @@ export class ModelSelectorComponent extends Container {
1612
1632
  )
1613
1633
  return;
1614
1634
  this.#bareProfileAuthByName = new Map(profileAuthEntries);
1635
+ this.#profileAuthByName = new Map(
1636
+ [...this.#getPresetGroups().values()]
1637
+ .flat()
1638
+ .map(profile => [profile.name, this.#isPresetAuthenticated(profile, true)]),
1639
+ );
1615
1640
  } finally {
1616
1641
  if (
1617
1642
  !this.#disposed &&
@@ -1684,6 +1709,39 @@ export class ModelSelectorComponent extends Container {
1684
1709
  this.#searchInput.setValue(value);
1685
1710
  }
1686
1711
 
1712
+ async #initializePresetLanding(): Promise<void> {
1713
+ this.#presetCatalogRefreshPending = true;
1714
+ let refreshFailed = false;
1715
+ try {
1716
+ const refreshStatic = this.#modelRegistry.refreshStatic;
1717
+ if (typeof refreshStatic === "function") await refreshStatic.call(this.#modelRegistry);
1718
+ this.#errorMessage = this.#modelRegistry.getError();
1719
+ } catch (error) {
1720
+ refreshFailed = true;
1721
+ this.#errorMessage = error instanceof Error ? error.message : String(error);
1722
+ } finally {
1723
+ this.#presetCatalogRefreshPending = false;
1724
+ }
1725
+ if (this.#disposed || this.#viewMode !== "presets") return;
1726
+ if ((this.#modelRegistry.getModelProfiles?.().size ?? 0) === 0) {
1727
+ this.#viewMode = "models";
1728
+ void this.#initializeCatalogView();
1729
+ return;
1730
+ }
1731
+ this.#rebuildRoleModels();
1732
+ this.#clampPresetCursor();
1733
+ if (this.#openSmartRoutingAfterPresetRefresh) {
1734
+ this.#openSmartRoutingAfterPresetRefresh = false;
1735
+ if (!refreshFailed) {
1736
+ this.#enterSmartRoutingMode();
1737
+ return;
1738
+ }
1739
+ }
1740
+ void this.#refreshProviderAuth();
1741
+ this.#renderPresetLanding();
1742
+ this.#tui.requestRender();
1743
+ }
1744
+
1687
1745
  #switchToModelMode(seed?: string, options?: { imageRoleFilter?: boolean }): void {
1688
1746
  this.#viewMode = "models";
1689
1747
  this.#expandedPresetProviderId = undefined;
@@ -1695,8 +1753,52 @@ export class ModelSelectorComponent extends Container {
1695
1753
  this.#selectedIndex = 0;
1696
1754
  this.#imageRoleFilter = options?.imageRoleFilter ?? false;
1697
1755
  this.#setSearchInputValue(seed ?? this.#searchInput.getValue());
1756
+ void this.#initializeCatalogView();
1757
+ }
1758
+
1759
+ async #initializeCatalogView(): Promise<void> {
1760
+ if (this.#catalogLoaded) {
1761
+ this.#presentCatalogView();
1762
+ return;
1763
+ }
1764
+ if (this.#catalogLoadPromise) return this.#catalogLoadPromise;
1765
+
1766
+ this.#headerContainer.clear();
1767
+ this.#listContainer.clear();
1768
+ this.#listContainer.addChild(new Text(theme.fg("muted", "Loading models..."), 0, 0));
1769
+ this.#tui.requestRender();
1770
+
1771
+ const load = this.#loadModels().then(() => {
1772
+ this.#catalogLoaded = true;
1773
+ if (this.#disposed) return;
1774
+ if (this.#smartRoutingOnly) {
1775
+ this.#enterSmartRoutingMode();
1776
+ return;
1777
+ }
1778
+ if (this.#viewMode === "models") this.#presentCatalogView();
1779
+ });
1780
+ this.#catalogLoadPromise = load;
1781
+ try {
1782
+ await load;
1783
+ } catch (error) {
1784
+ if (!this.#disposed && (this.#viewMode === "models" || this.#viewMode === "smart-routing")) {
1785
+ this.#errorMessage = error instanceof Error ? error.message : String(error);
1786
+ this.#buildProviderTabs();
1787
+ this.#updateTabBar();
1788
+ this.#updateList();
1789
+ this.#tui.requestRender();
1790
+ }
1791
+ } finally {
1792
+ if (this.#catalogLoadPromise === load) this.#catalogLoadPromise = undefined;
1793
+ }
1794
+ }
1795
+
1796
+ #presentCatalogView(): void {
1797
+ this.#buildProviderTabs();
1698
1798
  this.#updateTabBar();
1799
+ // Apply the latest query: input remains responsive while the catalog loads.
1699
1800
  this.#filterModels(this.#searchInput.getValue());
1801
+ this.#tui.requestRender();
1700
1802
  }
1701
1803
 
1702
1804
  /**
@@ -1880,6 +1982,12 @@ export class ModelSelectorComponent extends Container {
1880
1982
  for (const line of this.#formatCurrentSessionLines()) {
1881
1983
  this.#headerContainer.addChild(new Text(line, 0, 0));
1882
1984
  }
1985
+ if (this.#errorMessage) {
1986
+ for (const line of String(this.#errorMessage).split("\n")) {
1987
+ this.#listContainer.addChild(new Text(theme.fg("error", line), 0, 0));
1988
+ }
1989
+ this.#listContainer.addChild(new Spacer(1));
1990
+ }
1883
1991
  const rows = this.#getPresetRows();
1884
1992
  for (let i = 0; i < rows.length; i++) {
1885
1993
  const row = rows[i];
@@ -1926,7 +2034,7 @@ export class ModelSelectorComponent extends Container {
1926
2034
  }
1927
2035
  if (row.kind === "group") {
1928
2036
  const authenticated = this.#isPresetGroupUsable(row.profiles);
1929
- const mark = this.#providerAuthPending ? "…" : authenticated ? "✓" : "✗";
2037
+ const mark = this.#isPresetAvailabilityPending() ? "…" : authenticated ? "✓" : "✗";
1930
2038
  const containsActive =
1931
2039
  this.#activeModelProfile !== undefined &&
1932
2040
  row.profiles.some(profile => profile.name === this.#activeModelProfile);
@@ -1938,7 +2046,7 @@ export class ModelSelectorComponent extends Container {
1938
2046
  }
1939
2047
  const presentation = getModelProfilePresentation(row.profile);
1940
2048
  const authenticated = this.#isPresetAuthenticated(row.profile);
1941
- const mark = this.#providerAuthPending ? "…" : authenticated ? "✓" : "✗";
2049
+ const mark = this.#isPresetAvailabilityPending() ? "…" : authenticated ? "✓" : "✗";
1942
2050
  const isActive = row.profile.name === this.#activeModelProfile;
1943
2051
  const label = ` ${mark} ${presentation.displayName}`;
1944
2052
  const renderedLabel = selected ? theme.fg("accent", label) : authenticated ? label : theme.fg("dim", label);
@@ -2305,7 +2413,11 @@ export class ModelSelectorComponent extends Container {
2305
2413
 
2306
2414
  handleInput(keyData: string): void {
2307
2415
  if (this.#viewMode === "smart-routing") {
2308
- this.#smartRoutingPanel?.handleInput(keyData);
2416
+ if (this.#smartRoutingPanel) {
2417
+ this.#smartRoutingPanel.handleInput(keyData);
2418
+ } else if (getKeybindings().matches(keyData, "tui.select.cancel")) {
2419
+ this.#onCancelCallback();
2420
+ }
2309
2421
  return;
2310
2422
  }
2311
2423
  if (this.#assignmentState === "assigning") {
@@ -2477,6 +2589,11 @@ export class ModelSelectorComponent extends Container {
2477
2589
  this.#onSelectCallback({ kind: "deleteProfile", profileName: this.#previewProfileName });
2478
2590
  return;
2479
2591
  }
2592
+ if (this.#isPresetAvailabilityPending()) {
2593
+ this.#presetLoginHint = "Refreshing preset availability...";
2594
+ this.#renderPresetLanding();
2595
+ return;
2596
+ }
2480
2597
  const missing = this.#getMissingProviders(profile);
2481
2598
  if (missing.length > 0) {
2482
2599
  this.#presetLoginHint = `Run ${missing.map(provider => `/login ${provider}`).join(", ")}`;
@@ -2504,6 +2621,12 @@ export class ModelSelectorComponent extends Container {
2504
2621
  const row = this.#getSelectedPresetRow();
2505
2622
  if (!row) return;
2506
2623
  if (row.kind === "smartRouting") {
2624
+ if (this.#presetCatalogRefreshPending) {
2625
+ this.#openSmartRoutingAfterPresetRefresh = true;
2626
+ this.#presetLoginHint = "Refreshing model configuration...";
2627
+ this.#renderPresetLanding();
2628
+ return;
2629
+ }
2507
2630
  this.#enterSmartRoutingMode();
2508
2631
  return;
2509
2632
  }
@@ -2523,6 +2646,11 @@ export class ModelSelectorComponent extends Container {
2523
2646
  return;
2524
2647
  }
2525
2648
  if (row.kind === "group") {
2649
+ if (this.#isPresetAvailabilityPending()) {
2650
+ this.#presetLoginHint = "Refreshing preset availability...";
2651
+ this.#renderPresetLanding();
2652
+ return;
2653
+ }
2526
2654
  // A group is a list of alternative presets; only surface a login hint
2527
2655
  // when none of its members are usable. A partially-usable group stays
2528
2656
  // navigable so the user can drill in and pick a usable member.
@@ -2546,6 +2674,11 @@ export class ModelSelectorComponent extends Container {
2546
2674
  this.#renderPresetLanding();
2547
2675
  return;
2548
2676
  }
2677
+ if (this.#isPresetAvailabilityPending() && !isCustomUserProfile(row.profile)) {
2678
+ this.#presetLoginHint = "Refreshing preset availability...";
2679
+ this.#renderPresetLanding();
2680
+ return;
2681
+ }
2549
2682
  const missing = this.#getMissingProviders(row.profile);
2550
2683
  if (missing.length > 0 && !isCustomUserProfile(row.profile)) {
2551
2684
  this.#presetLoginHint = `Run ${missing.map(provider => `/login ${provider}`).join(", ")}`;
@@ -2583,7 +2716,7 @@ export class ModelSelectorComponent extends Container {
2583
2716
  this.#presetLoginHint = undefined;
2584
2717
  this.#clampPresetCursor();
2585
2718
  }
2586
- this.#renderPresetLanding();
2719
+ void this.#refreshProviderAuth();
2587
2720
  this.#tui.requestRender();
2588
2721
  }
2589
2722