acdev 1.0.7 → 1.0.8
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/package.json +1 -1
- package/public/app.js +109 -42
- package/public/styles.css +34 -0
- package/src/models.js +39 -9
package/package.json
CHANGED
package/public/app.js
CHANGED
|
@@ -129,6 +129,73 @@ let settingsSaving = false;
|
|
|
129
129
|
let settingsTab = 'ticket';
|
|
130
130
|
/** Sentinel: no model selected (jobs blocked until user picks one). */
|
|
131
131
|
const NO_MODEL = '-';
|
|
132
|
+
const OPENROUTER_AGENT_MODEL_PREFIX = 'anthropic/';
|
|
133
|
+
/** @type {string} */
|
|
134
|
+
let modelSourceHintBase = '';
|
|
135
|
+
/**
|
|
136
|
+
* @param {string} modelId
|
|
137
|
+
* @returns {boolean}
|
|
138
|
+
*/
|
|
139
|
+
function isOpenRouterAgentCompatibleModel(modelId) {
|
|
140
|
+
if (!modelId || !String(modelId).includes('/')) return false;
|
|
141
|
+
return String(modelId).trim().toLowerCase().startsWith(OPENROUTER_AGENT_MODEL_PREFIX);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @param {HTMLElement} btn
|
|
146
|
+
* @param {{ id: string, agentCompatible?: boolean }} model
|
|
147
|
+
*/
|
|
148
|
+
function appendModelCompatibilityBadge(btn, model) {
|
|
149
|
+
if (currentLlmProviderSelection() !== 'openrouter') return;
|
|
150
|
+
const compatible =
|
|
151
|
+
model.agentCompatible ?? isOpenRouterAgentCompatibleModel(model.id);
|
|
152
|
+
const badge = document.createElement('span');
|
|
153
|
+
badge.className = compatible
|
|
154
|
+
? 'model-picker-badge model-picker-badge--agent'
|
|
155
|
+
: 'model-picker-badge model-picker-badge--reference';
|
|
156
|
+
badge.textContent = compatible ? 'Agent' : 'May not work';
|
|
157
|
+
badge.title = compatible
|
|
158
|
+
? 'Supported for acdev agent jobs (anthropic/* via Claude Agent SDK)'
|
|
159
|
+
: 'Listed for reference; agent jobs require anthropic/* models';
|
|
160
|
+
btn.appendChild(badge);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @param {HTMLElement} btn
|
|
165
|
+
* @param {{ id: string, label?: string, name?: string, agentCompatible?: boolean }} model
|
|
166
|
+
*/
|
|
167
|
+
function populateModelPickerOption(btn, model) {
|
|
168
|
+
const titleRow = document.createElement('span');
|
|
169
|
+
titleRow.className = 'model-picker-option-title-row';
|
|
170
|
+
|
|
171
|
+
const title = document.createElement('span');
|
|
172
|
+
title.className = 'model-picker-option-title';
|
|
173
|
+
title.textContent = modelPickerLabel(model);
|
|
174
|
+
titleRow.appendChild(title);
|
|
175
|
+
appendModelCompatibilityBadge(titleRow, model);
|
|
176
|
+
btn.appendChild(titleRow);
|
|
177
|
+
|
|
178
|
+
if (model.id !== modelPickerLabel(model)) {
|
|
179
|
+
const sub = document.createElement('span');
|
|
180
|
+
sub.className = 'model-picker-option-id';
|
|
181
|
+
sub.textContent = model.id;
|
|
182
|
+
btn.appendChild(sub);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function refreshModelSourceHint() {
|
|
187
|
+
if (!els.settingsModelHint) return;
|
|
188
|
+
const provider = currentLlmProviderSelection();
|
|
189
|
+
let text = modelSourceHintBase;
|
|
190
|
+
if (
|
|
191
|
+
provider === 'openrouter' &&
|
|
192
|
+
!isNoModelSelection(currentModel) &&
|
|
193
|
+
!isOpenRouterAgentCompatibleModel(currentModel)
|
|
194
|
+
) {
|
|
195
|
+
text += ` Selected model "${currentModel}" may not work for agent jobs — choose an anthropic/* model to run jobs.`;
|
|
196
|
+
}
|
|
197
|
+
els.settingsModelHint.textContent = text;
|
|
198
|
+
}
|
|
132
199
|
/** @type {string} */
|
|
133
200
|
let currentModel = 'claude-sonnet-5';
|
|
134
201
|
/** @type {Array<{id:string,label?:string,name?:string}>} */
|
|
@@ -3012,6 +3079,7 @@ function updateModelLabel() {
|
|
|
3012
3079
|
els.modelCombined.title = combined;
|
|
3013
3080
|
}
|
|
3014
3081
|
syncAgentPickerDisplay();
|
|
3082
|
+
refreshModelSourceHint();
|
|
3015
3083
|
}
|
|
3016
3084
|
|
|
3017
3085
|
/**
|
|
@@ -3019,8 +3087,10 @@ function updateModelLabel() {
|
|
|
3019
3087
|
* @returns {Array<{id:string,label?:string,name?:string}>}
|
|
3020
3088
|
*/
|
|
3021
3089
|
function getAvailableModels(provider = currentLlmProviderSelection()) {
|
|
3022
|
-
if (availableModelsProvider === provider
|
|
3023
|
-
|
|
3090
|
+
if (availableModelsProvider === provider && availableModels.length) {
|
|
3091
|
+
return availableModels;
|
|
3092
|
+
}
|
|
3093
|
+
return defaultModelsForProvider(provider);
|
|
3024
3094
|
}
|
|
3025
3095
|
|
|
3026
3096
|
/**
|
|
@@ -3281,16 +3351,7 @@ function renderModelComboboxList(instance, selectedId = currentModel) {
|
|
|
3281
3351
|
btn.classList.add('is-active');
|
|
3282
3352
|
}
|
|
3283
3353
|
|
|
3284
|
-
|
|
3285
|
-
title.textContent = modelPickerLabel(m);
|
|
3286
|
-
btn.appendChild(title);
|
|
3287
|
-
|
|
3288
|
-
if (m.id !== modelPickerLabel(m)) {
|
|
3289
|
-
const sub = document.createElement('span');
|
|
3290
|
-
sub.className = 'model-picker-option-id';
|
|
3291
|
-
sub.textContent = m.id;
|
|
3292
|
-
btn.appendChild(sub);
|
|
3293
|
-
}
|
|
3354
|
+
populateModelPickerOption(btn, m);
|
|
3294
3355
|
|
|
3295
3356
|
btn.addEventListener('click', () => {
|
|
3296
3357
|
instance.onSelect(m.id);
|
|
@@ -3477,16 +3538,7 @@ function renderAgentPickerModelList(instance, selectedId = currentModel) {
|
|
|
3477
3538
|
btn.classList.add('is-active');
|
|
3478
3539
|
}
|
|
3479
3540
|
|
|
3480
|
-
|
|
3481
|
-
title.textContent = modelPickerLabel(m);
|
|
3482
|
-
btn.appendChild(title);
|
|
3483
|
-
|
|
3484
|
-
if (m.id !== modelPickerLabel(m)) {
|
|
3485
|
-
const sub = document.createElement('span');
|
|
3486
|
-
sub.className = 'model-picker-option-id';
|
|
3487
|
-
sub.textContent = m.id;
|
|
3488
|
-
btn.appendChild(sub);
|
|
3489
|
-
}
|
|
3541
|
+
populateModelPickerOption(btn, m);
|
|
3490
3542
|
|
|
3491
3543
|
btn.addEventListener('click', () => {
|
|
3492
3544
|
instance.onSelect(m.id);
|
|
@@ -3637,9 +3689,16 @@ function lastModelForProvider(provider) {
|
|
|
3637
3689
|
function defaultModelsForProvider(provider = 'claude') {
|
|
3638
3690
|
if (provider === 'openrouter') {
|
|
3639
3691
|
return [
|
|
3640
|
-
{ id: 'anthropic/claude-sonnet-4', label: 'Claude Sonnet 4 (Anthropic)' },
|
|
3641
|
-
{ id: 'anthropic/claude-opus-4', label: 'Claude Opus 4 (Anthropic)' },
|
|
3642
|
-
{ id: 'anthropic/claude-3.5-sonnet', label: 'Claude 3.5 Sonnet (Anthropic)' },
|
|
3692
|
+
{ id: 'anthropic/claude-sonnet-4', label: 'Claude Sonnet 4 (Anthropic)', agentCompatible: true },
|
|
3693
|
+
{ id: 'anthropic/claude-opus-4', label: 'Claude Opus 4 (Anthropic)', agentCompatible: true },
|
|
3694
|
+
{ id: 'anthropic/claude-3.5-sonnet', label: 'Claude 3.5 Sonnet (Anthropic)', agentCompatible: true },
|
|
3695
|
+
{ id: 'anthropic/claude-3.7-sonnet', label: 'Claude 3.7 Sonnet (Anthropic)', agentCompatible: true },
|
|
3696
|
+
{ id: 'openai/gpt-4o', label: 'GPT-4o (OpenAI)', agentCompatible: false },
|
|
3697
|
+
{ id: 'openai/gpt-4o-mini', label: 'GPT-4o Mini (OpenAI)', agentCompatible: false },
|
|
3698
|
+
{ id: 'google/gemini-2.5-pro-preview', label: 'Gemini 2.5 Pro Preview (Google)', agentCompatible: false },
|
|
3699
|
+
{ id: 'qwen/qwen3-235b-a22b', label: 'Qwen3 235B (Qwen)', agentCompatible: false },
|
|
3700
|
+
{ id: 'deepseek/deepseek-chat-v3-0324', label: 'DeepSeek Chat V3 (DeepSeek)', agentCompatible: false },
|
|
3701
|
+
{ id: 'meta-llama/llama-3.3-70b-instruct', label: 'Llama 3.3 70B Instruct (Meta)', agentCompatible: false },
|
|
3643
3702
|
];
|
|
3644
3703
|
}
|
|
3645
3704
|
return [
|
|
@@ -3660,22 +3719,23 @@ function updateModelSourceHint(source, provider = 'claude') {
|
|
|
3660
3719
|
if (!els.settingsModelHint) return;
|
|
3661
3720
|
const name = provider === 'openrouter' ? 'OpenRouter' : 'Claude';
|
|
3662
3721
|
if (source === 'anthropic') {
|
|
3663
|
-
|
|
3722
|
+
modelSourceHintBase =
|
|
3664
3723
|
`${name} model for agent runs. List loaded from Anthropic Models API.`;
|
|
3665
3724
|
} else if (source === 'openrouter') {
|
|
3666
|
-
|
|
3667
|
-
'OpenRouter
|
|
3725
|
+
modelSourceHintBase =
|
|
3726
|
+
'OpenRouter models for agent runs. Full catalog from OpenRouter Models API. Agent jobs require anthropic/* models (marked “Agent” in the list).';
|
|
3668
3727
|
} else if (source === 'fallback') {
|
|
3669
3728
|
if (provider === 'openrouter') {
|
|
3670
|
-
|
|
3671
|
-
'OpenRouter
|
|
3729
|
+
modelSourceHintBase =
|
|
3730
|
+
'OpenRouter models for agent runs. Showing curated catalog (live list unavailable — set OPENROUTER_API_KEY in Authentication). Agent jobs require anthropic/* models.';
|
|
3672
3731
|
} else {
|
|
3673
|
-
|
|
3732
|
+
modelSourceHintBase =
|
|
3674
3733
|
'Claude model for agent runs. Showing curated Claude Code models (live list unavailable — set an API key or use claude auth login).';
|
|
3675
3734
|
}
|
|
3676
3735
|
} else {
|
|
3677
|
-
|
|
3736
|
+
modelSourceHintBase = `${name} model for agent runs.`;
|
|
3678
3737
|
}
|
|
3738
|
+
refreshModelSourceHint();
|
|
3679
3739
|
}
|
|
3680
3740
|
|
|
3681
3741
|
/**
|
|
@@ -3721,7 +3781,7 @@ function syncLlmProviderSelects(provider) {
|
|
|
3721
3781
|
if (els.settingsLlmProviderHint) {
|
|
3722
3782
|
els.settingsLlmProviderHint.textContent =
|
|
3723
3783
|
value === 'openrouter'
|
|
3724
|
-
? 'Agent runs route through OpenRouter (requires OPENROUTER_API_KEY).
|
|
3784
|
+
? 'Agent runs route through OpenRouter (requires OPENROUTER_API_KEY). All models are listed; agent jobs need anthropic/* models.'
|
|
3725
3785
|
: 'Agent runs use Claude via Anthropic API, OAuth token, or claude auth login.';
|
|
3726
3786
|
}
|
|
3727
3787
|
}
|
|
@@ -3732,13 +3792,16 @@ function syncLlmProviderSelects(provider) {
|
|
|
3732
3792
|
function populateSettingsModels(data) {
|
|
3733
3793
|
const provider = data?.provider || currentLlmProviderSelection();
|
|
3734
3794
|
const cached = getAvailableModels(provider);
|
|
3735
|
-
|
|
3795
|
+
let models = Array.isArray(data?.models)
|
|
3736
3796
|
? data.models
|
|
3737
3797
|
: data?.reconcileProvider
|
|
3738
3798
|
? []
|
|
3739
3799
|
: cached.length
|
|
3740
3800
|
? cached
|
|
3741
3801
|
: defaultModelsForProvider(provider);
|
|
3802
|
+
if (!models.length && data?.source) {
|
|
3803
|
+
models = defaultModelsForProvider(provider);
|
|
3804
|
+
}
|
|
3742
3805
|
setAvailableModels(provider, models);
|
|
3743
3806
|
|
|
3744
3807
|
if (data?.reconcileProvider) {
|
|
@@ -3758,10 +3821,9 @@ function populateSettingsModels(data) {
|
|
|
3758
3821
|
updateModelLabel();
|
|
3759
3822
|
}
|
|
3760
3823
|
|
|
3761
|
-
function modelsForFetchFallback(provider
|
|
3762
|
-
if (reconcileProvider) return [];
|
|
3824
|
+
function modelsForFetchFallback(provider) {
|
|
3763
3825
|
const cached = getAvailableModels(provider);
|
|
3764
|
-
if (cached.length) return cached;
|
|
3826
|
+
if (availableModelsProvider === provider && cached.length) return cached;
|
|
3765
3827
|
return defaultModelsForProvider(provider);
|
|
3766
3828
|
}
|
|
3767
3829
|
|
|
@@ -3779,7 +3841,7 @@ async function fetchModels(opts = {}) {
|
|
|
3779
3841
|
const res = await fetch(`/api/models?${query.toString()}`);
|
|
3780
3842
|
if (!res.ok) {
|
|
3781
3843
|
populateSettingsModels({
|
|
3782
|
-
models: modelsForFetchFallback(provider
|
|
3844
|
+
models: modelsForFetchFallback(provider),
|
|
3783
3845
|
selected: currentModel,
|
|
3784
3846
|
provider,
|
|
3785
3847
|
reconcileProvider,
|
|
@@ -3797,7 +3859,7 @@ async function fetchModels(opts = {}) {
|
|
|
3797
3859
|
} catch (err) {
|
|
3798
3860
|
console.error('Failed to fetch models:', err);
|
|
3799
3861
|
populateSettingsModels({
|
|
3800
|
-
models: modelsForFetchFallback(provider
|
|
3862
|
+
models: modelsForFetchFallback(provider),
|
|
3801
3863
|
selected: currentModel,
|
|
3802
3864
|
provider,
|
|
3803
3865
|
reconcileProvider,
|
|
@@ -4379,11 +4441,13 @@ function applyConfigSnapshot(data) {
|
|
|
4379
4441
|
async function fetchConfig() {
|
|
4380
4442
|
try {
|
|
4381
4443
|
const res = await fetch('/api/config');
|
|
4382
|
-
if (!res.ok) return;
|
|
4444
|
+
if (!res.ok) return null;
|
|
4383
4445
|
const data = await readJson(res);
|
|
4384
4446
|
applyConfigSnapshot(data);
|
|
4447
|
+
return data;
|
|
4385
4448
|
} catch (err) {
|
|
4386
4449
|
console.error('Failed to fetch config:', err);
|
|
4450
|
+
return null;
|
|
4387
4451
|
}
|
|
4388
4452
|
}
|
|
4389
4453
|
|
|
@@ -5031,7 +5095,10 @@ els.logJobFilter.addEventListener('change', () => {
|
|
|
5031
5095
|
});
|
|
5032
5096
|
|
|
5033
5097
|
applyTheme(loadTheme());
|
|
5034
|
-
|
|
5035
|
-
|
|
5098
|
+
void (async () => {
|
|
5099
|
+
const cfg = await fetchConfig();
|
|
5100
|
+
const provider = cfg?.llmProvider === 'openrouter' ? 'openrouter' : 'claude';
|
|
5101
|
+
await fetchModels({ provider });
|
|
5102
|
+
})();
|
|
5036
5103
|
fetchJobs();
|
|
5037
5104
|
setInterval(fetchJobs, 3000);
|
package/public/styles.css
CHANGED
|
@@ -2298,6 +2298,40 @@ body.diff-fs-open {
|
|
|
2298
2298
|
cursor: pointer;
|
|
2299
2299
|
}
|
|
2300
2300
|
|
|
2301
|
+
.model-picker-option-title-row {
|
|
2302
|
+
display: flex;
|
|
2303
|
+
align-items: center;
|
|
2304
|
+
justify-content: space-between;
|
|
2305
|
+
gap: 8px;
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2308
|
+
.model-picker-option-title {
|
|
2309
|
+
min-width: 0;
|
|
2310
|
+
overflow: hidden;
|
|
2311
|
+
text-overflow: ellipsis;
|
|
2312
|
+
white-space: nowrap;
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
.model-picker-badge {
|
|
2316
|
+
flex-shrink: 0;
|
|
2317
|
+
padding: 1px 6px;
|
|
2318
|
+
border-radius: 999px;
|
|
2319
|
+
font-size: 10px;
|
|
2320
|
+
font-weight: 600;
|
|
2321
|
+
letter-spacing: 0.02em;
|
|
2322
|
+
text-transform: uppercase;
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2325
|
+
.model-picker-badge--agent {
|
|
2326
|
+
color: var(--primary);
|
|
2327
|
+
background: color-mix(in srgb, var(--primary) 14%, transparent);
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
.model-picker-badge--reference {
|
|
2331
|
+
color: var(--text-muted);
|
|
2332
|
+
background: color-mix(in srgb, var(--text-muted) 12%, transparent);
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2301
2335
|
.model-picker-option:hover,
|
|
2302
2336
|
.model-picker-option.is-active {
|
|
2303
2337
|
background: var(--surface-2);
|
package/src/models.js
CHANGED
|
@@ -45,18 +45,23 @@ export const MODEL_OPTIONS = CLAUDE_MODEL_OPTIONS;
|
|
|
45
45
|
export const DEFAULT_MODEL = 'claude-sonnet-5';
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
|
-
* OpenRouter slugs
|
|
49
|
-
*
|
|
50
|
-
* only anthropic/* models are supported on that path.
|
|
48
|
+
* OpenRouter slugs commonly used with acdev. Curated list is merged with the
|
|
49
|
+
* live OpenRouter catalog; anthropic/* entries are agent-compatible.
|
|
51
50
|
*/
|
|
52
51
|
export const OPENROUTER_AGENT_MODEL_PREFIX = 'anthropic/';
|
|
53
52
|
|
|
54
|
-
/** Curated OpenRouter model ids used as defaults + fallback
|
|
53
|
+
/** Curated OpenRouter model ids used as defaults + fallback when the live API is unavailable. */
|
|
55
54
|
export const OPENROUTER_MODEL_OPTIONS = [
|
|
56
55
|
{ id: 'anthropic/claude-sonnet-4', label: 'Claude Sonnet 4 (Anthropic)' },
|
|
57
56
|
{ id: 'anthropic/claude-opus-4', label: 'Claude Opus 4 (Anthropic)' },
|
|
58
57
|
{ id: 'anthropic/claude-3.5-sonnet', label: 'Claude 3.5 Sonnet (Anthropic)' },
|
|
59
58
|
{ id: 'anthropic/claude-3.7-sonnet', label: 'Claude 3.7 Sonnet (Anthropic)' },
|
|
59
|
+
{ id: 'openai/gpt-4o', label: 'GPT-4o (OpenAI)' },
|
|
60
|
+
{ id: 'openai/gpt-4o-mini', label: 'GPT-4o Mini (OpenAI)' },
|
|
61
|
+
{ id: 'google/gemini-2.5-pro-preview', label: 'Gemini 2.5 Pro Preview (Google)' },
|
|
62
|
+
{ id: 'qwen/qwen3-235b-a22b', label: 'Qwen3 235B (Qwen)' },
|
|
63
|
+
{ id: 'deepseek/deepseek-chat-v3-0324', label: 'DeepSeek Chat V3 (DeepSeek)' },
|
|
64
|
+
{ id: 'meta-llama/llama-3.3-70b-instruct', label: 'Llama 3.3 70B Instruct (Meta)' },
|
|
60
65
|
];
|
|
61
66
|
|
|
62
67
|
export const DEFAULT_OPENROUTER_MODEL = 'anthropic/claude-sonnet-4';
|
|
@@ -76,7 +81,7 @@ const ANTHROPIC_VERSION = '2023-06-01';
|
|
|
76
81
|
const CACHE_TTL_MS = 5 * 60_000;
|
|
77
82
|
const KEYCHAIN_SERVICE = 'Claude Code-credentials';
|
|
78
83
|
|
|
79
|
-
/** @typedef {{ id: string, name?: string, label?: string }} ModelOption */
|
|
84
|
+
/** @typedef {{ id: string, name?: string, label?: string, agentCompatible?: boolean }} ModelOption */
|
|
80
85
|
/** @typedef {'anthropic' | 'openrouter' | 'fallback'} ModelsSource */
|
|
81
86
|
/** @typedef {{ models: ModelOption[], selected: string, source: ModelsSource, provider?: LlmProvider }} ModelsListResult */
|
|
82
87
|
|
|
@@ -206,11 +211,36 @@ export function isOpenRouterAgentCompatibleModel(modelId) {
|
|
|
206
211
|
}
|
|
207
212
|
|
|
208
213
|
/**
|
|
214
|
+
* Mark whether each OpenRouter slug is supported for acdev agent jobs.
|
|
215
|
+
* @param {ModelOption} model
|
|
216
|
+
* @returns {ModelOption}
|
|
217
|
+
*/
|
|
218
|
+
export function annotateOpenRouterModelCompatibility(model) {
|
|
219
|
+
if (!model?.id) return model;
|
|
220
|
+
return {
|
|
221
|
+
...model,
|
|
222
|
+
agentCompatible: isOpenRouterAgentCompatibleModel(model.id),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* @param {ModelOption[]} models
|
|
228
|
+
* @returns {ModelOption[]}
|
|
229
|
+
*/
|
|
230
|
+
function annotateOpenRouterModelsCompatibility(models) {
|
|
231
|
+
return models.map(annotateOpenRouterModelCompatibility);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* If the live list is empty, fall back to curated options. Listing is not
|
|
236
|
+
* filtered to anthropic/* — runtime enqueue still validates agent compatibility.
|
|
209
237
|
* @param {ModelOption[]} models
|
|
238
|
+
* @param {ModelOption[]} curated
|
|
210
239
|
* @returns {ModelOption[]}
|
|
211
240
|
*/
|
|
212
|
-
function
|
|
213
|
-
|
|
241
|
+
function ensureOpenRouterModels(models, curated) {
|
|
242
|
+
const list = models.length > 0 ? models : curated;
|
|
243
|
+
return annotateOpenRouterModelsCompatibility(list);
|
|
214
244
|
}
|
|
215
245
|
|
|
216
246
|
/**
|
|
@@ -593,7 +623,7 @@ async function listModelsForProvider(provider, opts = {}) {
|
|
|
593
623
|
const live = filterModelsForProvider(liveRaw, provider);
|
|
594
624
|
let models = filterModelsForProvider(mergeModelLists(curated, live), provider);
|
|
595
625
|
if (provider === 'openrouter') {
|
|
596
|
-
models =
|
|
626
|
+
models = ensureOpenRouterModels(models, curated);
|
|
597
627
|
}
|
|
598
628
|
const selected = reconcileModelForProvider(models, selectedRaw);
|
|
599
629
|
const source = /** @type {ModelsSource} */ (provider === 'openrouter' ? 'openrouter' : 'anthropic');
|
|
@@ -603,7 +633,7 @@ async function listModelsForProvider(provider, opts = {}) {
|
|
|
603
633
|
} catch {
|
|
604
634
|
let models = curated;
|
|
605
635
|
if (provider === 'openrouter') {
|
|
606
|
-
models =
|
|
636
|
+
models = ensureOpenRouterModels(models, curated);
|
|
607
637
|
}
|
|
608
638
|
const selected = reconcileModelForProvider(models, selectedRaw);
|
|
609
639
|
const result = { models, source: /** @type {ModelsSource} */ ('fallback') };
|