jeopi-cli 16.6.2 → 16.6.5
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.
- package/CHANGELOG.md +18 -0
- package/dist/cli.js +2736 -2736
- package/dist/types/config/model-registry.d.ts +1 -1
- package/package.json +12 -12
- package/src/config/model-registry.ts +36 -8
- package/src/main.ts +26 -8
- package/src/sdk.ts +64 -14
|
@@ -94,7 +94,7 @@ export declare class ModelRegistry {
|
|
|
94
94
|
* Drives the same SQLite model cache as built-in providers, so the default
|
|
95
95
|
* `online-if-uncached` strategy fetches at most once per cache TTL (24 h).
|
|
96
96
|
*/
|
|
97
|
-
refreshRuntimeProviders(strategy?: ModelRefreshStrategy): Promise<void>;
|
|
97
|
+
refreshRuntimeProviders(strategy?: ModelRefreshStrategy, excludedProviders?: ReadonlySet<string>): Promise<void>;
|
|
98
98
|
/**
|
|
99
99
|
* Get any error from loading custom models config (undefined if no error).
|
|
100
100
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "jeopi-cli",
|
|
4
|
-
"version": "16.6.
|
|
4
|
+
"version": "16.6.5",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://github.com/akillness/jeopi",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -57,17 +57,17 @@
|
|
|
57
57
|
"@agentclientprotocol/sdk": "0.25.0",
|
|
58
58
|
"@babel/parser": "^7.29.7",
|
|
59
59
|
"@mozilla/readability": "^0.6.0",
|
|
60
|
-
"jeopi-hashline": "16.6.
|
|
61
|
-
"jeopi-stats": "16.6.
|
|
62
|
-
"jeopi-agent-core": "16.6.
|
|
63
|
-
"jeopi-ai": "16.6.
|
|
64
|
-
"jeopi-catalog": "16.6.
|
|
65
|
-
"jeopi-mnemopi": "16.6.
|
|
66
|
-
"jeopi-natives": "16.6.
|
|
67
|
-
"jeopi-tui": "16.6.
|
|
68
|
-
"jeopi-utils": "16.6.
|
|
69
|
-
"jeopi-wire": "16.6.
|
|
70
|
-
"jeopi-snapcompact": "16.6.
|
|
60
|
+
"jeopi-hashline": "16.6.5",
|
|
61
|
+
"jeopi-stats": "16.6.5",
|
|
62
|
+
"jeopi-agent-core": "16.6.5",
|
|
63
|
+
"jeopi-ai": "16.6.5",
|
|
64
|
+
"jeopi-catalog": "16.6.5",
|
|
65
|
+
"jeopi-mnemopi": "16.6.5",
|
|
66
|
+
"jeopi-natives": "16.6.5",
|
|
67
|
+
"jeopi-tui": "16.6.5",
|
|
68
|
+
"jeopi-utils": "16.6.5",
|
|
69
|
+
"jeopi-wire": "16.6.5",
|
|
70
|
+
"jeopi-snapcompact": "16.6.5",
|
|
71
71
|
"@opentelemetry/api": "^1.9.1",
|
|
72
72
|
"@opentelemetry/context-async-hooks": "^2.7.1",
|
|
73
73
|
"@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
|
|
@@ -823,14 +823,30 @@ export class ModelRegistry {
|
|
|
823
823
|
this.#backgroundRefresh = refreshPromise;
|
|
824
824
|
}
|
|
825
825
|
|
|
826
|
+
#resolveProviderId(providerId: string): string | undefined {
|
|
827
|
+
const candidates = new Set<string>([
|
|
828
|
+
...STARTUP_MODEL_CACHE_PROVIDER_IDS,
|
|
829
|
+
...this.#models.map(model => model.provider),
|
|
830
|
+
...this.#discoverableProviders.map(provider => provider.provider),
|
|
831
|
+
...this.#providerOverrides.keys(),
|
|
832
|
+
...this.#customProviderApiKeys.keys(),
|
|
833
|
+
...this.#runtimeProviderApiKeys.keys(),
|
|
834
|
+
...this.#runtimeProviderOverrides.keys(),
|
|
835
|
+
...this.#runtimeModelManagers.keys(),
|
|
836
|
+
]);
|
|
837
|
+
return [...candidates].find(candidate => candidate.toLowerCase() === providerId.toLowerCase());
|
|
838
|
+
}
|
|
839
|
+
|
|
826
840
|
async refreshProvider(providerId: string, strategy: ModelRefreshStrategy = "online"): Promise<void> {
|
|
827
841
|
this.#reloadStaticModels();
|
|
842
|
+
const canonicalProviderId = this.#resolveProviderId(providerId);
|
|
843
|
+
if (!canonicalProviderId) return;
|
|
828
844
|
for (const selector of this.#suppressedSelectors.keys()) {
|
|
829
|
-
if (selector.startsWith(`${
|
|
845
|
+
if (selector.startsWith(`${canonicalProviderId}/`)) {
|
|
830
846
|
this.#suppressedSelectors.delete(selector);
|
|
831
847
|
}
|
|
832
848
|
}
|
|
833
|
-
await this.#refreshRuntimeDiscoveries(strategy, new Set([
|
|
849
|
+
await this.#refreshRuntimeDiscoveries(strategy, new Set([canonicalProviderId]));
|
|
834
850
|
}
|
|
835
851
|
|
|
836
852
|
/**
|
|
@@ -892,11 +908,18 @@ export class ModelRegistry {
|
|
|
892
908
|
* Drives the same SQLite model cache as built-in providers, so the default
|
|
893
909
|
* `online-if-uncached` strategy fetches at most once per cache TTL (24 h).
|
|
894
910
|
*/
|
|
895
|
-
async refreshRuntimeProviders(
|
|
911
|
+
async refreshRuntimeProviders(
|
|
912
|
+
strategy: ModelRefreshStrategy = "online-if-uncached",
|
|
913
|
+
excludedProviders?: ReadonlySet<string>,
|
|
914
|
+
): Promise<void> {
|
|
896
915
|
if (this.#runtimeModelManagers.size === 0) {
|
|
897
916
|
return;
|
|
898
917
|
}
|
|
899
|
-
|
|
918
|
+
const providers = new Set(
|
|
919
|
+
[...this.#runtimeModelManagers.keys()].filter(provider => !excludedProviders?.has(provider.toLowerCase())),
|
|
920
|
+
);
|
|
921
|
+
if (providers.size === 0) return;
|
|
922
|
+
await this.#refreshRuntimeDiscoveries(strategy, providers);
|
|
900
923
|
}
|
|
901
924
|
|
|
902
925
|
#reloadStaticModels(): void {
|
|
@@ -1620,7 +1643,7 @@ export class ModelRegistry {
|
|
|
1620
1643
|
): Promise<BuiltInDiscoveryResult> {
|
|
1621
1644
|
// Skip providers already handled by configured discovery (e.g. user-configured ollama with discovery.type)
|
|
1622
1645
|
const configuredDiscoveryProviders = new Set(this.#discoverableProviders.map(p => p.provider));
|
|
1623
|
-
const managerOptions = (await this.#collectBuiltInModelManagerOptions()).filter(opts => {
|
|
1646
|
+
const managerOptions = (await this.#collectBuiltInModelManagerOptions(providerFilter)).filter(opts => {
|
|
1624
1647
|
if (configuredDiscoveryProviders.has(opts.providerId)) {
|
|
1625
1648
|
return false;
|
|
1626
1649
|
}
|
|
@@ -1643,7 +1666,7 @@ export class ModelRegistry {
|
|
|
1643
1666
|
return { models, authoritativeProviders };
|
|
1644
1667
|
}
|
|
1645
1668
|
|
|
1646
|
-
async #collectBuiltInModelManagerOptions(): Promise<ModelManagerOptions<Api>[]> {
|
|
1669
|
+
async #collectBuiltInModelManagerOptions(providerFilter?: ReadonlySet<string>): Promise<ModelManagerOptions<Api>[]> {
|
|
1647
1670
|
const specialProviderDescriptors: Array<{
|
|
1648
1671
|
providerId: string;
|
|
1649
1672
|
resolveKey: (value: string | undefined) => string | undefined;
|
|
@@ -1712,10 +1735,14 @@ export class ModelRegistry {
|
|
|
1712
1735
|
];
|
|
1713
1736
|
const disabledProviders = getDisabledProviderIdsFromSettings();
|
|
1714
1737
|
const standardProviderDescriptors = PROVIDER_DESCRIPTORS.filter(
|
|
1715
|
-
descriptor =>
|
|
1738
|
+
descriptor =>
|
|
1739
|
+
!disabledProviders.has(descriptor.providerId) &&
|
|
1740
|
+
(providerFilter === undefined || providerFilter.has(descriptor.providerId)),
|
|
1716
1741
|
);
|
|
1717
1742
|
const enabledSpecialProviderDescriptors = specialProviderDescriptors.filter(
|
|
1718
|
-
descriptor =>
|
|
1743
|
+
descriptor =>
|
|
1744
|
+
!disabledProviders.has(descriptor.providerId) &&
|
|
1745
|
+
(providerFilter === undefined || providerFilter.has(descriptor.providerId)),
|
|
1719
1746
|
);
|
|
1720
1747
|
// Use peekApiKey to avoid OAuth token refresh during discovery.
|
|
1721
1748
|
// The token is only needed if the dynamic fetch fires (cache miss),
|
|
@@ -1764,6 +1791,7 @@ export class ModelRegistry {
|
|
|
1764
1791
|
}
|
|
1765
1792
|
// Append runtime model managers registered by extensions via fetchDynamicModels.
|
|
1766
1793
|
for (const { options: managerOpts } of this.#runtimeModelManagers.values()) {
|
|
1794
|
+
if (providerFilter !== undefined && !providerFilter.has(managerOpts.providerId)) continue;
|
|
1767
1795
|
options.push(managerOpts);
|
|
1768
1796
|
}
|
|
1769
1797
|
return options;
|
package/src/main.ts
CHANGED
|
@@ -34,6 +34,7 @@ import { findConfigFile } from "./config";
|
|
|
34
34
|
import { ModelRegistry } from "./config/model-registry";
|
|
35
35
|
import {
|
|
36
36
|
getModelMatchPreferences,
|
|
37
|
+
parseModelString,
|
|
37
38
|
resolveCliModel,
|
|
38
39
|
resolveModelRoleValue,
|
|
39
40
|
resolveModelScope,
|
|
@@ -861,18 +862,35 @@ async function buildSessionOptions(
|
|
|
861
862
|
modelRegistry,
|
|
862
863
|
preferences: modelMatchPreferences,
|
|
863
864
|
});
|
|
864
|
-
|
|
865
|
+
const requestedSelector =
|
|
866
|
+
parsed.provider && !parsed.model.toLowerCase().startsWith(`${parsed.provider.toLowerCase()}/`)
|
|
867
|
+
? `${parsed.provider}/${parsed.model}`
|
|
868
|
+
: parsed.model;
|
|
869
|
+
const parsedSelector = parseModelString(requestedSelector, {
|
|
870
|
+
allowMaxAlias: true,
|
|
871
|
+
allowAutoAlias: true,
|
|
872
|
+
isLiteralModelId: (provider, id) => modelRegistry.find(provider, id) !== undefined,
|
|
873
|
+
});
|
|
874
|
+
const exactSelector = parsedSelector && !/[?*[\]]/.test(parsedSelector.id);
|
|
875
|
+
const exactModel = exactSelector ? modelRegistry.find(parsedSelector.provider, parsedSelector.id) : undefined;
|
|
876
|
+
const resolvedExactSelector =
|
|
877
|
+
parsedSelector &&
|
|
878
|
+
resolved.selector?.toLowerCase() === `${parsedSelector.provider}/${parsedSelector.id}`.toLowerCase();
|
|
879
|
+
const deferSelection = resolved.error || (exactSelector && !exactModel && !resolvedExactSelector);
|
|
880
|
+
if (resolved.warning && !deferSelection) {
|
|
865
881
|
process.stderr.write(`${chalk.yellow(`Warning: ${resolved.warning}`)}\n`);
|
|
866
882
|
}
|
|
867
|
-
if (
|
|
868
|
-
if (
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
883
|
+
if (deferSelection) {
|
|
884
|
+
if (
|
|
885
|
+
resolved.error &&
|
|
886
|
+
parsedSelector?.provider.toLowerCase() !== "google-antigravity" &&
|
|
887
|
+
(parsed.provider || parsed.model.includes(":"))
|
|
888
|
+
) {
|
|
873
889
|
process.stderr.write(`${chalk.red(resolved.error)}\n`);
|
|
874
890
|
process.exit(1);
|
|
875
891
|
}
|
|
892
|
+
// Register extension overrides before discovering missing account models.
|
|
893
|
+
options.modelPattern = requestedSelector;
|
|
876
894
|
} else if (resolved.model) {
|
|
877
895
|
options.model = resolved.model;
|
|
878
896
|
activeSettings.overrideModelRoles({
|
|
@@ -1416,7 +1434,7 @@ export async function runRootCommand(
|
|
|
1416
1434
|
notifs.push({ kind: "error", message: modelRegistryError.message });
|
|
1417
1435
|
}
|
|
1418
1436
|
|
|
1419
|
-
if (!
|
|
1437
|
+
if (!session.model && (!isInteractive || sessionOptions.modelPattern !== undefined)) {
|
|
1420
1438
|
if (modelFallbackMessage) {
|
|
1421
1439
|
process.stderr.write(`${chalk.red(modelFallbackMessage)}\n`);
|
|
1422
1440
|
} else {
|
package/src/sdk.ts
CHANGED
|
@@ -1255,6 +1255,10 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1255
1255
|
|
|
1256
1256
|
const hasExplicitModel = options.model !== undefined || options.modelPattern !== undefined;
|
|
1257
1257
|
const modelMatchPreferences = getModelMatchPreferences(settings);
|
|
1258
|
+
const sessionModelStrings =
|
|
1259
|
+
!hasExplicitModel && hasExistingSession
|
|
1260
|
+
? getRestorableSessionModels(existingSession.models, sessionManager.getLastModelChangeRole())
|
|
1261
|
+
: [];
|
|
1258
1262
|
const allowedModels = await logger.time("resolveAllowedModels", () =>
|
|
1259
1263
|
resolveAllowedModels(modelRegistry, settings, modelMatchPreferences),
|
|
1260
1264
|
);
|
|
@@ -1271,10 +1275,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1271
1275
|
// host preconnect) can use the restored model; extension-registered
|
|
1272
1276
|
// providers aren't visible yet, so we retry the preferred candidates once
|
|
1273
1277
|
// extensions register below.
|
|
1274
|
-
const sessionModelStrings =
|
|
1275
|
-
!hasExplicitModel && hasExistingSession
|
|
1276
|
-
? getRestorableSessionModels(existingSession.models, sessionManager.getLastModelChangeRole())
|
|
1277
|
-
: [];
|
|
1278
1278
|
let restoredSessionModelIndex = -1;
|
|
1279
1279
|
let restoredSessionThinkingLevel: ConfiguredThinkingLevel | undefined;
|
|
1280
1280
|
if (!hasExplicitModel && !model && sessionModelStrings.length > 0) {
|
|
@@ -1319,6 +1319,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1319
1319
|
}
|
|
1320
1320
|
|
|
1321
1321
|
const taskDepth = options.taskDepth ?? 0;
|
|
1322
|
+
let deferredThinkingLevel: ConfiguredThinkingLevel | undefined;
|
|
1322
1323
|
|
|
1323
1324
|
// Resolves the session/agent thinking level using the same precedence we
|
|
1324
1325
|
// apply at startup: explicit option → persisted session entry → restored
|
|
@@ -1327,7 +1328,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1327
1328
|
// role reclaim so the final model's own defaults aren't masked by an earlier
|
|
1328
1329
|
// fallback model's.
|
|
1329
1330
|
const pickInitialThinkingLevel = (selectedModel: Model | undefined): ConfiguredThinkingLevel | undefined => {
|
|
1330
|
-
let level = options.thinkingLevel;
|
|
1331
|
+
let level = options.thinkingLevel ?? deferredThinkingLevel;
|
|
1331
1332
|
if (level === undefined && hasExistingSession && hasThinkingEntry) {
|
|
1332
1333
|
level =
|
|
1333
1334
|
parseConfiguredThinkingLevel(existingSession.configuredThinkingLevel) ??
|
|
@@ -1402,11 +1403,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1402
1403
|
},
|
|
1403
1404
|
);
|
|
1404
1405
|
|
|
1405
|
-
// Resolve contextFiles
|
|
1406
|
-
//
|
|
1407
|
-
// it. On timeout we forward `undefined` to ToolSession; buildSystemPromptInternal
|
|
1408
|
-
// will re-race the same promise through its own withDeadline path. Background
|
|
1409
|
-
// work continues so caches still warm.
|
|
1406
|
+
// Resolve contextFiles before tool creation. Bound optional startup discovery;
|
|
1407
|
+
// late work may still populate caches or system-prompt fallback data.
|
|
1410
1408
|
const raceWithDeadline = async <T>(name: string, work: Promise<T>): Promise<T | undefined> => {
|
|
1411
1409
|
let timedOut = false;
|
|
1412
1410
|
const result = await Promise.race([
|
|
@@ -1417,7 +1415,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1417
1415
|
}),
|
|
1418
1416
|
]);
|
|
1419
1417
|
if (timedOut) {
|
|
1420
|
-
logger.warn("Startup
|
|
1418
|
+
logger.warn("Startup work exceeded deadline; continuing with available data", {
|
|
1421
1419
|
name,
|
|
1422
1420
|
timeoutMs: STARTUP_SCAN_DEADLINE_MS,
|
|
1423
1421
|
cwd,
|
|
@@ -1914,12 +1912,50 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1914
1912
|
}
|
|
1915
1913
|
extensionsResult.runtime.pendingProviderRegistrations = [];
|
|
1916
1914
|
}
|
|
1915
|
+
// Resolve account-only models after extension overrides own their endpoints.
|
|
1916
|
+
const discoverySelectors = options.modelPattern
|
|
1917
|
+
? [options.modelPattern]
|
|
1918
|
+
: !hasExplicitModel
|
|
1919
|
+
? [...sessionModelStrings, settings.getModelRole("default")]
|
|
1920
|
+
: [];
|
|
1921
|
+
const refreshedProviders = new Set<string>();
|
|
1922
|
+
for (const selector of discoverySelectors) {
|
|
1923
|
+
if (!selector) continue;
|
|
1924
|
+
const parsed = parseModelString(selector, {
|
|
1925
|
+
allowMaxAlias: true,
|
|
1926
|
+
allowAutoAlias: true,
|
|
1927
|
+
isLiteralModelId: (provider, id) => modelRegistry.find(provider, id) !== undefined,
|
|
1928
|
+
});
|
|
1929
|
+
if (!parsed || /[?*[\]]/.test(parsed.id)) continue;
|
|
1930
|
+
const existing = modelRegistry.find(parsed.provider, parsed.id);
|
|
1931
|
+
if (existing && hasModelAuth(existing)) break;
|
|
1932
|
+
if (!existing && selector.includes("@")) {
|
|
1933
|
+
const routed = parseModelPattern(selector, modelRegistry.getAll(), modelMatchPreferences);
|
|
1934
|
+
if (routed.upstream && routed.model && hasModelAuth(routed.model)) break;
|
|
1935
|
+
}
|
|
1936
|
+
const providerKey = parsed.provider.toLowerCase();
|
|
1937
|
+
if (existing || refreshedProviders.has(providerKey)) continue;
|
|
1938
|
+
refreshedProviders.add(providerKey);
|
|
1939
|
+
try {
|
|
1940
|
+
await raceWithDeadline(
|
|
1941
|
+
`refreshProvider:${parsed.provider}`,
|
|
1942
|
+
modelRegistry.refreshProvider(parsed.provider, "online"),
|
|
1943
|
+
);
|
|
1944
|
+
} catch (error) {
|
|
1945
|
+
logger.debug("startup model discovery failed", {
|
|
1946
|
+
provider: parsed.provider,
|
|
1947
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1948
|
+
});
|
|
1949
|
+
}
|
|
1950
|
+
const discovered = modelRegistry.find(parsed.provider, parsed.id);
|
|
1951
|
+
if (discovered && hasModelAuth(discovered)) break;
|
|
1952
|
+
}
|
|
1917
1953
|
// Discover runtime (extension) provider catalogs now that they are
|
|
1918
1954
|
// registered. The startup refreshInBackground() ran before extensions
|
|
1919
1955
|
// loaded, so dynamic extension providers are only discovered here. Runs in
|
|
1920
1956
|
// the background (cache-aware) so startup is never blocked on the fetch; the
|
|
1921
1957
|
// model list re-renders when the catalog arrives, like other dynamic providers.
|
|
1922
|
-
void modelRegistry.refreshRuntimeProviders().catch(error => {
|
|
1958
|
+
void modelRegistry.refreshRuntimeProviders("online-if-uncached", refreshedProviders).catch(error => {
|
|
1923
1959
|
logger.warn("runtime provider discovery failed", {
|
|
1924
1960
|
error: error instanceof Error ? error.message : String(error),
|
|
1925
1961
|
});
|
|
@@ -1964,14 +2000,28 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1964
2000
|
}
|
|
1965
2001
|
}
|
|
1966
2002
|
}
|
|
2003
|
+
if (!hasExplicitModel && restoredSessionModelIndex < 0 && refreshedProviders.size > 0) {
|
|
2004
|
+
model = undefined;
|
|
2005
|
+
}
|
|
1967
2006
|
// Resolve deferred --model pattern now that extension models are registered.
|
|
1968
2007
|
if (!model && options.modelPattern) {
|
|
1969
2008
|
const availableModels = modelRegistry.getAll();
|
|
1970
2009
|
const matchPreferences = getModelMatchPreferences(settings);
|
|
1971
|
-
const { model: resolved } = parseModelPattern(
|
|
2010
|
+
const { model: resolved, thinkingLevel: selectorThinkingLevel } = parseModelPattern(
|
|
2011
|
+
options.modelPattern,
|
|
2012
|
+
availableModels,
|
|
2013
|
+
matchPreferences,
|
|
2014
|
+
{ allowInvalidThinkingSelectorFallback: false },
|
|
2015
|
+
);
|
|
1972
2016
|
if (resolved) {
|
|
1973
2017
|
model = resolved;
|
|
1974
2018
|
modelFallbackMessage = undefined;
|
|
2019
|
+
deferredThinkingLevel = selectorThinkingLevel;
|
|
2020
|
+
thinkingLevel = pickInitialThinkingLevel(resolved);
|
|
2021
|
+
autoThinking = thinkingLevel === AUTO_THINKING;
|
|
2022
|
+
effectiveThinkingLevel = autoThinking
|
|
2023
|
+
? resolveProvisionalAutoLevel(resolved)
|
|
2024
|
+
: resolveThinkingLevelForModel(resolved, concreteThinkingLevel(thinkingLevel));
|
|
1975
2025
|
} else {
|
|
1976
2026
|
modelFallbackMessage = `Model "${options.modelPattern}" not found`;
|
|
1977
2027
|
}
|
|
@@ -1994,7 +2044,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1994
2044
|
// default with a bundled provider's default whenever a stray
|
|
1995
2045
|
// `OPENAI_API_KEY`/`ANTHROPIC_API_KEY` is in the environment.
|
|
1996
2046
|
// (issue #3569)
|
|
1997
|
-
if (!hasExplicitModel && !defaultRoleSpec.model) {
|
|
2047
|
+
if (!hasExplicitModel && (!defaultRoleSpec.model || refreshedProviders.size > 0)) {
|
|
1998
2048
|
const reResolvedRoleSpec = resolveModelRoleValue(settings.getModelRole("default"), fallbackCandidates, {
|
|
1999
2049
|
settings,
|
|
2000
2050
|
matchPreferences: modelMatchPreferences,
|