@dexto/tui 1.6.11 → 1.6.13

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 (66) hide show
  1. package/dist/InkCLIRefactored.cjs +39 -29
  2. package/dist/InkCLIRefactored.d.ts.map +1 -1
  3. package/dist/InkCLIRefactored.js +39 -29
  4. package/dist/components/Footer.cjs +9 -2
  5. package/dist/components/Footer.d.ts +3 -2
  6. package/dist/components/Footer.d.ts.map +1 -1
  7. package/dist/components/Footer.js +17 -3
  8. package/dist/components/modes/AlternateBufferCLI.cjs +2 -1
  9. package/dist/components/modes/AlternateBufferCLI.d.ts.map +1 -1
  10. package/dist/components/modes/AlternateBufferCLI.js +2 -1
  11. package/dist/components/modes/StaticCLI.cjs +2 -1
  12. package/dist/components/modes/StaticCLI.d.ts.map +1 -1
  13. package/dist/components/modes/StaticCLI.js +2 -1
  14. package/dist/components/overlays/ChatGPTUsageCapOverlay.cjs +90 -0
  15. package/dist/components/overlays/ChatGPTUsageCapOverlay.d.ts +19 -0
  16. package/dist/components/overlays/ChatGPTUsageCapOverlay.d.ts.map +1 -0
  17. package/dist/components/overlays/ChatGPTUsageCapOverlay.js +70 -0
  18. package/dist/components/overlays/ModelSelectorRefactored.cjs +263 -38
  19. package/dist/components/overlays/ModelSelectorRefactored.d.ts.map +1 -1
  20. package/dist/components/overlays/ModelSelectorRefactored.js +267 -38
  21. package/dist/components/overlays/ReasoningOverlay.cjs +1 -1
  22. package/dist/components/overlays/ReasoningOverlay.js +1 -1
  23. package/dist/containers/OverlayContainer.cjs +104 -13
  24. package/dist/containers/OverlayContainer.d.ts.map +1 -1
  25. package/dist/containers/OverlayContainer.js +104 -13
  26. package/dist/hooks/useAgentEvents.cjs +33 -2
  27. package/dist/hooks/useAgentEvents.d.ts.map +1 -1
  28. package/dist/hooks/useAgentEvents.js +35 -3
  29. package/dist/hooks/useCLIState.cjs +1 -0
  30. package/dist/hooks/useCLIState.d.ts.map +1 -1
  31. package/dist/hooks/useCLIState.js +1 -0
  32. package/dist/interactive-commands/exit-stats.cjs +16 -0
  33. package/dist/interactive-commands/exit-stats.d.ts +4 -0
  34. package/dist/interactive-commands/exit-stats.d.ts.map +1 -1
  35. package/dist/interactive-commands/exit-stats.js +15 -0
  36. package/dist/interactive-commands/general-commands.cjs +13 -2
  37. package/dist/interactive-commands/general-commands.d.ts.map +1 -1
  38. package/dist/interactive-commands/general-commands.js +14 -3
  39. package/dist/interactive-commands/general-commands.test.cjs +152 -0
  40. package/dist/interactive-commands/general-commands.test.d.ts +2 -0
  41. package/dist/interactive-commands/general-commands.test.d.ts.map +1 -0
  42. package/dist/interactive-commands/general-commands.test.js +151 -0
  43. package/dist/services/processStream.test.cjs +1 -0
  44. package/dist/services/processStream.test.js +1 -0
  45. package/dist/state/initialState.cjs +1 -0
  46. package/dist/state/initialState.d.ts.map +1 -1
  47. package/dist/state/initialState.js +1 -0
  48. package/dist/state/types.d.ts +4 -2
  49. package/dist/state/types.d.ts.map +1 -1
  50. package/dist/utils/chatgpt-rate-limit.cjs +72 -0
  51. package/dist/utils/chatgpt-rate-limit.d.ts +11 -0
  52. package/dist/utils/chatgpt-rate-limit.d.ts.map +1 -0
  53. package/dist/utils/chatgpt-rate-limit.js +49 -0
  54. package/dist/utils/chatgpt-rate-limit.test.cjs +46 -0
  55. package/dist/utils/chatgpt-rate-limit.test.d.ts +2 -0
  56. package/dist/utils/chatgpt-rate-limit.test.d.ts.map +1 -0
  57. package/dist/utils/chatgpt-rate-limit.test.js +49 -0
  58. package/dist/utils/llm-provider-display.cjs +11 -1
  59. package/dist/utils/llm-provider-display.d.ts +2 -2
  60. package/dist/utils/llm-provider-display.d.ts.map +1 -1
  61. package/dist/utils/llm-provider-display.js +11 -1
  62. package/dist/utils/llm-provider-display.test.cjs +15 -0
  63. package/dist/utils/llm-provider-display.test.d.ts +2 -0
  64. package/dist/utils/llm-provider-display.test.d.ts.map +1 -0
  65. package/dist/utils/llm-provider-display.test.js +14 -0
  66. package/package.json +4 -4
@@ -42,9 +42,102 @@ const MODEL_SELECTOR_TABS = [
42
42
  ];
43
43
  const PROVIDER_COLLATOR = new Intl.Collator("en", { sensitivity: "base" });
44
44
  const PROVIDER_TOKEN_PATTERN = /[^a-z0-9]/g;
45
+ const CODEX_CHATGPT_BASE_URL = (0, import_core.createCodexBaseURL)("chatgpt");
46
+ const CHATGPT_CODEX_MODEL_LIST_TIMEOUT_MS = 5e3;
45
47
  function normalizeProviderToken(value) {
46
48
  return value.toLowerCase().replace(PROVIDER_TOKEN_PATTERN, "");
47
49
  }
50
+ function isChatGPTCodexBaseURL(baseURL) {
51
+ return (0, import_core.parseCodexBaseURL)(baseURL)?.authMode === "chatgpt";
52
+ }
53
+ function isChatGPTCodexConfig(provider, baseURL) {
54
+ return provider === "openai-compatible" && isChatGPTCodexBaseURL(baseURL);
55
+ }
56
+ function canonicalizeModelBaseURL(baseURL) {
57
+ const parsed = (0, import_core.parseCodexBaseURL)(baseURL);
58
+ return parsed ? (0, import_core.createCodexBaseURL)(parsed.authMode) : baseURL;
59
+ }
60
+ function toCanonicalModelPickerKey(input) {
61
+ const baseURL = canonicalizeModelBaseURL(input.baseURL);
62
+ return (0, import_agent_management.toModelPickerKey)({
63
+ provider: input.provider,
64
+ model: input.model,
65
+ ...baseURL ? { baseURL } : {}
66
+ });
67
+ }
68
+ function createModelIdentity(input) {
69
+ return {
70
+ provider: input.provider,
71
+ name: input.name,
72
+ ...input.baseURL ? { baseURL: input.baseURL } : {}
73
+ };
74
+ }
75
+ async function raceWithTimeout(promise, options) {
76
+ return await new Promise((resolve, reject) => {
77
+ const timer = setTimeout(() => {
78
+ options.onTimeout?.();
79
+ reject(new Error(options.errorMessage));
80
+ }, options.timeoutMs);
81
+ promise.then(
82
+ (value) => {
83
+ clearTimeout(timer);
84
+ resolve(value);
85
+ },
86
+ (error) => {
87
+ clearTimeout(timer);
88
+ reject(error);
89
+ }
90
+ );
91
+ });
92
+ }
93
+ function matchesConfiguredModel(candidate, configured) {
94
+ if (candidate.provider !== configured.provider || candidate.name !== configured.model) {
95
+ return false;
96
+ }
97
+ const candidateBaseURL = candidate.baseURL ?? "";
98
+ const configuredBaseURL = configured.baseURL ?? "";
99
+ if (candidateBaseURL === configuredBaseURL) {
100
+ return true;
101
+ }
102
+ const candidateCodex = (0, import_core.parseCodexBaseURL)(candidate.baseURL);
103
+ const configuredCodex = (0, import_core.parseCodexBaseURL)(configured.baseURL);
104
+ return candidateCodex?.authMode === configuredCodex?.authMode;
105
+ }
106
+ async function loadChatGPTCodexModels(agent) {
107
+ let client = null;
108
+ try {
109
+ const createPromise = import_core.CodexAppServerClient.create({
110
+ requestTimeoutMs: CHATGPT_CODEX_MODEL_LIST_TIMEOUT_MS
111
+ });
112
+ client = await raceWithTimeout(createPromise, {
113
+ timeoutMs: CHATGPT_CODEX_MODEL_LIST_TIMEOUT_MS,
114
+ errorMessage: `Timed out starting ChatGPT Codex model listing after ${CHATGPT_CODEX_MODEL_LIST_TIMEOUT_MS}ms`,
115
+ onTimeout: () => {
116
+ void createPromise.then((lateClient) => lateClient.close().catch(() => void 0)).catch(() => void 0);
117
+ }
118
+ });
119
+ const account = await raceWithTimeout(client.readAccount(false), {
120
+ timeoutMs: CHATGPT_CODEX_MODEL_LIST_TIMEOUT_MS,
121
+ errorMessage: `Timed out reading ChatGPT Codex account state after ${CHATGPT_CODEX_MODEL_LIST_TIMEOUT_MS}ms`
122
+ });
123
+ if (account.account?.type !== "chatgpt") {
124
+ return [];
125
+ }
126
+ return await raceWithTimeout(client.listModels(), {
127
+ timeoutMs: CHATGPT_CODEX_MODEL_LIST_TIMEOUT_MS,
128
+ errorMessage: `Timed out listing ChatGPT Codex models after ${CHATGPT_CODEX_MODEL_LIST_TIMEOUT_MS}ms`
129
+ });
130
+ } catch (error) {
131
+ agent.logger.debug(
132
+ `ChatGPT Codex model list unavailable: ${error instanceof Error ? error.message : String(error)}`
133
+ );
134
+ return [];
135
+ } finally {
136
+ if (client) {
137
+ await client.close().catch(() => void 0);
138
+ }
139
+ }
140
+ }
48
141
  function toReleaseDateLookupKey(provider, modelName) {
49
142
  return `${provider}::${modelName.toLowerCase()}`;
50
143
  }
@@ -161,7 +254,11 @@ function compareModelOptionsForDisplay(left, right) {
161
254
  return 0;
162
255
  }
163
256
  function toModelIdentityKey(model) {
164
- return (0, import_agent_management.toModelPickerKey)({ provider: model.provider, model: model.name });
257
+ return toCanonicalModelPickerKey({
258
+ provider: model.provider,
259
+ model: model.name,
260
+ ...model.baseURL ? { baseURL: model.baseURL } : {}
261
+ });
165
262
  }
166
263
  function normalizeLineText(value) {
167
264
  return (0, import_textUtils.stripUnsafeCharacters)(value).replace(/\r?\n/g, " ").replace(/\s+/g, " ").trim();
@@ -318,12 +415,20 @@ const ModelSelector = (0, import_react.forwardRef)(function ModelSelector2({
318
415
  `OpenRouter catalog refresh skipped: ${error instanceof Error ? error.message : String(error)}`
319
416
  );
320
417
  }
321
- const [allModels, providers, currentConfig, loadedCustomModels, preferences] = await Promise.all([
418
+ const [
419
+ allModels,
420
+ providers,
421
+ currentConfig,
422
+ loadedCustomModels,
423
+ preferences,
424
+ codexModels
425
+ ] = await Promise.all([
322
426
  Promise.resolve(agent.getSupportedModels()),
323
427
  Promise.resolve(agent.getSupportedProviders()),
324
428
  Promise.resolve(agent.getCurrentLLMConfig()),
325
429
  (0, import_agent_management.loadCustomModels)(),
326
- (0, import_agent_management.loadGlobalPreferences)().catch(() => null)
430
+ (0, import_agent_management.loadGlobalPreferences)().catch(() => null),
431
+ loadChatGPTCodexModels(agent)
327
432
  ]);
328
433
  const pickerState = await (0, import_agent_management.loadModelPickerState)().catch(() => null);
329
434
  const modelList = [];
@@ -332,6 +437,51 @@ const ModelSelector = (0, import_react.forwardRef)(function ModelSelector2({
332
437
  const defaultBaseURL = preferences?.llm.baseURL;
333
438
  const defaultReasoningVariant = preferences?.llm.reasoning?.variant;
334
439
  const resolveReleaseDate = createReleaseDateResolver({ allModels, providers });
440
+ const defaultConfig = defaultProvider && defaultModel ? {
441
+ provider: defaultProvider,
442
+ model: defaultModel,
443
+ ...defaultBaseURL ? { baseURL: defaultBaseURL } : {}
444
+ } : null;
445
+ const currentModelConfig = {
446
+ provider: currentConfig.provider,
447
+ model: currentConfig.model,
448
+ ...currentConfig.baseURL ? { baseURL: currentConfig.baseURL } : {}
449
+ };
450
+ const getMatchState = (candidate) => ({
451
+ isDefault: defaultConfig ? matchesConfiguredModel(candidate, defaultConfig) : false,
452
+ isCurrent: matchesConfiguredModel(candidate, currentModelConfig)
453
+ });
454
+ const addChatGPTCodexModel = (input) => {
455
+ const existing = modelList.find(
456
+ (candidate) => candidate.provider === "openai-compatible" && candidate.name === input.model && candidate.baseURL === CODEX_CHATGPT_BASE_URL
457
+ );
458
+ if (existing) {
459
+ existing.isDefault = existing.isDefault || input.isDefault;
460
+ existing.isCurrent = existing.isCurrent || input.isCurrent;
461
+ if (existing.displayName === void 0 && input.displayName !== void 0) {
462
+ existing.displayName = input.displayName;
463
+ }
464
+ if (existing.releaseDate === void 0 && input.releaseDate !== void 0) {
465
+ existing.releaseDate = input.releaseDate;
466
+ }
467
+ if (existing.reasoningVariant === void 0 && input.reasoningVariant !== void 0) {
468
+ existing.reasoningVariant = input.reasoningVariant;
469
+ }
470
+ return;
471
+ }
472
+ modelList.push({
473
+ provider: "openai-compatible",
474
+ name: input.model,
475
+ displayName: input.displayName,
476
+ maxInputTokens: 128e3,
477
+ isDefault: input.isDefault,
478
+ isCurrent: input.isCurrent,
479
+ isCustom: false,
480
+ baseURL: CODEX_CHATGPT_BASE_URL,
481
+ ...input.releaseDate !== void 0 ? { releaseDate: input.releaseDate } : {},
482
+ ...input.reasoningVariant !== void 0 ? { reasoningVariant: input.reasoningVariant } : {}
483
+ });
484
+ };
335
485
  let ollamaModels = [];
336
486
  let localModels = [];
337
487
  try {
@@ -346,13 +496,19 @@ const ModelSelector = (0, import_react.forwardRef)(function ModelSelector2({
346
496
  }
347
497
  for (const custom of loadedCustomModels) {
348
498
  const customProvider = custom.provider;
499
+ const candidate = createModelIdentity({
500
+ provider: customProvider,
501
+ name: custom.name,
502
+ baseURL: custom.baseURL
503
+ });
504
+ const { isDefault, isCurrent } = getMatchState(candidate);
349
505
  const modelOption = {
350
506
  provider: customProvider,
351
507
  name: custom.name,
352
508
  displayName: custom.displayName || custom.name,
353
509
  maxInputTokens: custom.maxInputTokens ?? 128e3,
354
- isDefault: customProvider === defaultProvider && custom.name === defaultModel,
355
- isCurrent: currentConfig.provider === customProvider && currentConfig.model === custom.name,
510
+ isDefault,
511
+ isCurrent,
356
512
  isCustom: true
357
513
  };
358
514
  if (custom.baseURL) {
@@ -383,32 +539,95 @@ const ModelSelector = (0, import_react.forwardRef)(function ModelSelector2({
383
539
  model.releaseDate
384
540
  );
385
541
  const originalProvider = "originalProvider" in model ? model.originalProvider : void 0;
542
+ const candidate = createModelIdentity({
543
+ provider,
544
+ name: model.name
545
+ });
546
+ const { isDefault, isCurrent } = getMatchState(candidate);
386
547
  modelList.push({
387
548
  provider,
388
549
  name: model.name,
389
550
  displayName: model.displayName,
390
551
  maxInputTokens: model.maxInputTokens,
391
- isDefault: provider === defaultProvider && model.name === defaultModel,
392
- isCurrent: provider === currentConfig.provider && model.name === currentConfig.model,
552
+ isDefault,
553
+ isCurrent,
393
554
  isCustom: false,
394
555
  ...releaseDate !== void 0 ? { releaseDate } : {},
395
556
  ...model.status !== void 0 ? { status: model.status } : {},
396
- ...defaultReasoningVariant && provider === defaultProvider && model.name === defaultModel ? { reasoningVariant: defaultReasoningVariant } : {},
397
- ...defaultBaseURL && provider === defaultProvider && model.name === defaultModel ? { baseURL: defaultBaseURL } : {},
557
+ ...defaultReasoningVariant && isDefault ? { reasoningVariant: defaultReasoningVariant } : {},
398
558
  // Store original provider for display purposes
399
559
  ...originalProvider && { originalProvider }
400
560
  });
401
561
  }
402
562
  }
563
+ for (const codexModel of codexModels) {
564
+ const candidate = {
565
+ provider: "openai-compatible",
566
+ name: codexModel.model,
567
+ baseURL: CODEX_CHATGPT_BASE_URL
568
+ };
569
+ const isDefault = defaultConfig ? matchesConfiguredModel(candidate, defaultConfig) : false;
570
+ const releaseDate = resolveReleaseDate("openai", codexModel.model) ?? resolveReleaseDate("openrouter", codexModel.model);
571
+ addChatGPTCodexModel({
572
+ model: codexModel.model,
573
+ displayName: codexModel.displayName,
574
+ isDefault,
575
+ isCurrent: matchesConfiguredModel(candidate, currentModelConfig),
576
+ ...releaseDate !== void 0 ? { releaseDate } : {},
577
+ ...isDefault && defaultReasoningVariant ? { reasoningVariant: defaultReasoningVariant } : {}
578
+ });
579
+ }
580
+ const addMissingConfiguredCodexModel = (configured) => {
581
+ if (!configured || !isChatGPTCodexConfig(configured.provider, configured.baseURL)) {
582
+ return;
583
+ }
584
+ const releaseDate = resolveReleaseDate("openai", configured.model) ?? resolveReleaseDate("openrouter", configured.model);
585
+ addChatGPTCodexModel({
586
+ model: configured.model,
587
+ displayName: (0, import_core.getModelDisplayName)(configured.model, "openai"),
588
+ isDefault: defaultConfig ? matchesConfiguredModel(
589
+ {
590
+ provider: "openai-compatible",
591
+ name: configured.model,
592
+ baseURL: CODEX_CHATGPT_BASE_URL
593
+ },
594
+ defaultConfig
595
+ ) : false,
596
+ isCurrent: matchesConfiguredModel(
597
+ {
598
+ provider: "openai-compatible",
599
+ name: configured.model,
600
+ baseURL: CODEX_CHATGPT_BASE_URL
601
+ },
602
+ currentModelConfig
603
+ ),
604
+ ...releaseDate !== void 0 ? { releaseDate } : {},
605
+ ...defaultConfig && defaultReasoningVariant && matchesConfiguredModel(
606
+ {
607
+ provider: "openai-compatible",
608
+ name: configured.model,
609
+ baseURL: CODEX_CHATGPT_BASE_URL
610
+ },
611
+ defaultConfig
612
+ ) ? { reasoningVariant: defaultReasoningVariant } : {}
613
+ });
614
+ };
615
+ addMissingConfiguredCodexModel(defaultConfig);
616
+ addMissingConfiguredCodexModel(currentModelConfig);
403
617
  for (const ollamaModel of ollamaModels) {
618
+ const candidate = createModelIdentity({
619
+ provider: "ollama",
620
+ name: ollamaModel.name
621
+ });
622
+ const { isDefault, isCurrent } = getMatchState(candidate);
404
623
  modelList.push({
405
624
  provider: "ollama",
406
625
  name: ollamaModel.name,
407
626
  displayName: ollamaModel.name,
408
627
  maxInputTokens: 128e3,
409
628
  // Default, actual varies by model
410
- isDefault: defaultProvider === "ollama" && defaultModel === ollamaModel.name,
411
- isCurrent: currentConfig.provider === "ollama" && currentConfig.model === ollamaModel.name,
629
+ isDefault,
630
+ isCurrent,
412
631
  isCustom: false
413
632
  });
414
633
  }
@@ -416,13 +635,18 @@ const ModelSelector = (0, import_react.forwardRef)(function ModelSelector2({
416
635
  const modelInfo = (0, import_core.getLocalModelById)(localModel.id);
417
636
  const displayName = modelInfo?.name || localModel.id;
418
637
  const maxInputTokens = modelInfo?.contextLength || 128e3;
638
+ const candidate = createModelIdentity({
639
+ provider: "local",
640
+ name: localModel.id
641
+ });
642
+ const { isDefault, isCurrent } = getMatchState(candidate);
419
643
  modelList.push({
420
644
  provider: "local",
421
645
  name: localModel.id,
422
646
  displayName,
423
647
  maxInputTokens,
424
- isDefault: defaultProvider === "local" && defaultModel === localModel.id,
425
- isCurrent: currentConfig.provider === "local" && currentConfig.model === localModel.id,
648
+ isDefault,
649
+ isCurrent,
426
650
  isCustom: false
427
651
  });
428
652
  }
@@ -437,17 +661,22 @@ const ModelSelector = (0, import_react.forwardRef)(function ModelSelector2({
437
661
  model.name,
438
662
  model.releaseDate
439
663
  );
664
+ const candidate = createModelIdentity({
665
+ provider: "vertex",
666
+ name: model.name
667
+ });
668
+ const { isDefault, isCurrent } = getMatchState(candidate);
440
669
  modelList.push({
441
670
  provider: "vertex",
442
671
  name: model.name,
443
672
  displayName: model.displayName,
444
673
  maxInputTokens: model.maxInputTokens,
445
- isDefault: defaultProvider === "vertex" && defaultModel === model.name,
446
- isCurrent: currentConfig.provider === "vertex" && currentConfig.model === model.name,
674
+ isDefault,
675
+ isCurrent,
447
676
  isCustom: false,
448
677
  ...releaseDate !== void 0 ? { releaseDate } : {},
449
678
  ...model.status !== void 0 ? { status: model.status } : {},
450
- ...defaultReasoningVariant && defaultProvider === "vertex" && defaultModel === model.name ? { reasoningVariant: defaultReasoningVariant } : {}
679
+ ...defaultReasoningVariant && isDefault ? { reasoningVariant: defaultReasoningVariant } : {}
451
680
  });
452
681
  }
453
682
  }
@@ -535,12 +764,7 @@ const ModelSelector = (0, import_react.forwardRef)(function ModelSelector2({
535
764
  }, [isVisible, agent, refreshVersion]);
536
765
  const favoriteKeySet = (0, import_react.useMemo)(
537
766
  () => new Set(
538
- (modelPickerState?.favorites ?? []).map(
539
- (entry) => (0, import_agent_management.toModelPickerKey)({
540
- provider: entry.provider,
541
- model: entry.model
542
- })
543
- )
767
+ (modelPickerState?.favorites ?? []).map((entry) => toCanonicalModelPickerKey(entry))
544
768
  ),
545
769
  [modelPickerState]
546
770
  );
@@ -553,7 +777,8 @@ const ModelSelector = (0, import_react.forwardRef)(function ModelSelector2({
553
777
  const name = model.name.toLowerCase().replace(/[\s-]+/g, "");
554
778
  const displayName = (model.displayName || "").toLowerCase().replace(/[\s-]+/g, "");
555
779
  const provider = model.provider.toLowerCase().replace(/[\s-]+/g, "");
556
- return name.includes(query) || displayName.includes(query) || provider.includes(query);
780
+ const providerDisplay = (0, import_llm_provider_display.getLLMProviderDisplayName)(model.provider, model.baseURL).toLowerCase().replace(/[\s-]+/g, "");
781
+ return name.includes(query) || displayName.includes(query) || provider.includes(query) || providerDisplay.includes(query);
557
782
  },
558
783
  [searchQuery]
559
784
  );
@@ -562,10 +787,7 @@ const ModelSelector = (0, import_react.forwardRef)(function ModelSelector2({
562
787
  const hasSearchQuery = searchQuery.trim().length > 0;
563
788
  const allCandidates = [...models].sort(compareModelOptionsForDisplay);
564
789
  const modelsByKey = new Map(
565
- allCandidates.map((model) => [
566
- (0, import_agent_management.toModelPickerKey)({ provider: model.provider, model: model.name }),
567
- model
568
- ])
790
+ allCandidates.map((model) => [toModelIdentityKey(model), model])
569
791
  );
570
792
  const toUniqueMatchingModels = (candidates, limit) => {
571
793
  const deduped = [];
@@ -574,10 +796,7 @@ const ModelSelector = (0, import_react.forwardRef)(function ModelSelector2({
574
796
  if (!candidate || !matchesSearch(candidate)) {
575
797
  continue;
576
798
  }
577
- const key = (0, import_agent_management.toModelPickerKey)({
578
- provider: candidate.provider,
579
- model: candidate.name
580
- });
799
+ const key = toModelIdentityKey(candidate);
581
800
  if (seen.has(key)) {
582
801
  continue;
583
802
  }
@@ -595,12 +814,12 @@ const ModelSelector = (0, import_react.forwardRef)(function ModelSelector2({
595
814
  const featuredCandidates = (0, import_core.getCuratedModelRefsForProviders)({
596
815
  providers: providersInModels,
597
816
  max: FEATURED_SECTION_LIMIT
598
- }).map((ref2) => modelsByKey.get((0, import_agent_management.toModelPickerKey)(ref2)));
817
+ }).map((ref2) => modelsByKey.get(toCanonicalModelPickerKey(ref2)));
599
818
  const recentsFromState = (modelPickerState?.recents ?? []).map(
600
- (entry) => modelsByKey.get((0, import_agent_management.toModelPickerKey)({ provider: entry.provider, model: entry.model }))
819
+ (entry) => modelsByKey.get(toCanonicalModelPickerKey(entry))
601
820
  );
602
821
  const favoritesFromState = (modelPickerState?.favorites ?? []).map(
603
- (entry) => modelsByKey.get((0, import_agent_management.toModelPickerKey)({ provider: entry.provider, model: entry.model }))
822
+ (entry) => modelsByKey.get(toCanonicalModelPickerKey(entry))
604
823
  );
605
824
  const customCandidates = allCandidates.filter((model) => model.isCustom);
606
825
  const tabModels = hasSearchQuery ? toUniqueMatchingModels(allCandidates) : activeTab === "all-models" ? toUniqueMatchingModels(allCandidates) : activeTab === "featured" ? toUniqueMatchingModels(featuredCandidates) : activeTab === "recents" ? toUniqueMatchingModels(recentsFromState) : activeTab === "favorites" ? toUniqueMatchingModels(favoritesFromState) : toUniqueMatchingModels(customCandidates);
@@ -653,9 +872,11 @@ const ModelSelector = (0, import_react.forwardRef)(function ModelSelector2({
653
872
  const handleToggleFavoriteModel = (0, import_react.useCallback)(
654
873
  async (model) => {
655
874
  try {
875
+ const baseURL = canonicalizeModelBaseURL(model.baseURL);
656
876
  await (0, import_agent_management.toggleFavoriteModel)({
657
877
  provider: model.provider,
658
- model: model.name
878
+ model: model.name,
879
+ ...baseURL ? { baseURL } : {}
659
880
  });
660
881
  const nextState = await (0, import_agent_management.loadModelPickerState)();
661
882
  setModelPickerState(nextState);
@@ -1143,12 +1364,16 @@ const ModelSelector = (0, import_react.forwardRef)(function ModelSelector2({
1143
1364
  }
1144
1365
  const actualIndex = modelStartIndex + scrollOffset + rowIndex;
1145
1366
  const isSelected = actualIndex === selectedIndex;
1146
- const providerDisplay = (0, import_llm_provider_display.getLLMProviderDisplayName)(item.provider);
1367
+ const providerDisplay = (0, import_llm_provider_display.getLLMProviderDisplayName)(
1368
+ item.provider,
1369
+ item.baseURL
1370
+ );
1147
1371
  const name = item.displayName || item.name;
1148
1372
  const isFavorite = favoriteKeySet.has(
1149
- (0, import_agent_management.toModelPickerKey)({
1373
+ toCanonicalModelPickerKey({
1150
1374
  provider: item.provider,
1151
- model: item.name
1375
+ model: item.name,
1376
+ ...item.baseURL ? { baseURL: item.baseURL } : {}
1152
1377
  })
1153
1378
  );
1154
1379
  const prefix = getRowPrefix({
@@ -1 +1 @@
1
- {"version":3,"file":"ModelSelectorRefactored.d.ts","sourceRoot":"","sources":["../../../src/components/overlays/ModelSelectorRefactored.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AAE/D,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAU7E,OAAO,EASH,KAAK,WAAW,EAEnB,MAAM,yBAAyB,CAAC;AAoLjC,UAAU,kBAAkB;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,CACX,QAAQ,EAAE,WAAW,EACrB,KAAK,EAAE,MAAM,EACb,WAAW,CAAC,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,MAAM,EAChB,gBAAgB,CAAC,EAAE,gBAAgB,KAClC,IAAI,CAAC;IACV,iBAAiB,EAAE,CACf,QAAQ,EAAE,WAAW,EACrB,KAAK,EAAE,MAAM,EACb,WAAW,CAAC,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,MAAM,EAChB,gBAAgB,CAAC,EAAE,gBAAgB,KAClC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,iBAAiB,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IAChD,KAAK,EAAE,UAAU,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAChC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AA4ID;;GAEG;AACH,QAAA,MAAM,aAAa,oHA8rCjB,CAAC;AAEH,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"ModelSelectorRefactored.d.ts","sourceRoot":"","sources":["../../../src/components/overlays/ModelSelectorRefactored.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qCAAqC,CAAC;AAE/D,OAAO,KAAK,EAAkB,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAc7F,OAAO,EASH,KAAK,WAAW,EAEnB,MAAM,yBAAyB,CAAC;AAoTjC,UAAU,kBAAkB;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,CACX,QAAQ,EAAE,WAAW,EACrB,KAAK,EAAE,MAAM,EACb,WAAW,CAAC,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,MAAM,EAChB,gBAAgB,CAAC,EAAE,gBAAgB,KAClC,IAAI,CAAC;IACV,iBAAiB,EAAE,CACf,QAAQ,EAAE,WAAW,EACrB,KAAK,EAAE,MAAM,EACb,WAAW,CAAC,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,MAAM,EAChB,gBAAgB,CAAC,EAAE,gBAAgB,KAClC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,iBAAiB,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IAChD,KAAK,EAAE,UAAU,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAChC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAgJD;;GAEG;AACH,QAAA,MAAM,aAAa,oHAy2CjB,CAAC;AAEH,eAAe,aAAa,CAAC"}