diff --git a/chat/public/admin.html b/chat/public/admin.html
index c4ff33c..0a98f97 100644
--- a/chat/public/admin.html
+++ b/chat/public/admin.html
@@ -243,44 +243,6 @@
-
diff --git a/chat/public/admin.js b/chat/public/admin.js
index 9cc872c..deddd8d 100644
--- a/chat/public/admin.js
+++ b/chat/public/admin.js
@@ -12,10 +12,9 @@
accounts: [],
affiliates: [],
withdrawals: [],
- planSettings: { provider: 'openrouter', freePlanModel: '', planningChain: [] },
+ planSettings: { provider: 'openrouter', planningChain: [] },
providerLimits: {},
providerUsage: [],
- opencodeBackupModel: '',
providerOptions: [],
providerModels: {},
tokenRates: {},
@@ -69,12 +68,8 @@
orBackup2: document.getElementById('or-backup2'),
orBackup3: document.getElementById('or-backup3'),
orStatus: document.getElementById('or-status'),
- autoModelForm: document.getElementById('auto-model-form'),
- autoModelSelect: document.getElementById('auto-model-select'),
- autoModelStatus: document.getElementById('auto-model-status'),
planProviderForm: document.getElementById('plan-provider-form'),
planProvider: document.getElementById('plan-provider'),
- freePlanModel: document.getElementById('free-plan-model'),
planProviderStatus: document.getElementById('plan-provider-status'),
planPriorityList: document.getElementById('plan-priority-list'),
addPlanRow: document.getElementById('add-plan-row'),
@@ -120,9 +115,6 @@
// Cancel messages UI
cancelAllMessages: document.getElementById('cancel-all-messages'),
cancelMessagesStatus: document.getElementById('cancel-messages-status'),
- opencodeBackupForm: document.getElementById('opencode-backup-form'),
- opencodeBackup: document.getElementById('opencode-backup'),
- opencodeBackupStatus: document.getElementById('opencode-backup-status'),
externalTestingRun: document.getElementById('external-testing-run'),
externalTestingStatus: document.getElementById('external-testing-status'),
externalTestingOutput: document.getElementById('external-testing-output'),
@@ -131,9 +123,6 @@
ollamaTestStatus: document.getElementById('ollama-test-status'),
ollamaTestOutput: document.getElementById('ollama-test-output'),
};
- console.log('Element check - opencodeBackupForm:', el.opencodeBackupForm);
- console.log('Element check - opencodeBackup:', el.opencodeBackup);
- console.log('Element check - opencodeBackupStatus:', el.opencodeBackupStatus);
function ensureAvailableDatalist() {
if (el.availableModelDatalist) return el.availableModelDatalist;
@@ -205,12 +194,6 @@
el.providerLimitStatus.style.color = isError ? 'var(--danger)' : 'inherit';
}
- function setAutoModelStatus(msg, isError = false) {
- if (!el.autoModelStatus) return;
- el.autoModelStatus.textContent = msg || '';
- el.autoModelStatus.style.color = isError ? 'var(--danger)' : 'inherit';
- }
-
function setPublicModelStatus(msg, isError = false) {
if (!el.publicModelStatus) return;
el.publicModelStatus.textContent = msg || '';
@@ -229,12 +212,6 @@
el.providerChainStatus.style.color = isError ? 'var(--danger)' : 'inherit';
}
- function setOpencodeBackupStatus(msg, isError = false) {
- if (!el.opencodeBackupStatus) return;
- el.opencodeBackupStatus.textContent = msg || '';
- el.opencodeBackupStatus.style.color = isError ? 'var(--danger)' : 'inherit';
- }
-
function setExternalTestingStatus(msg, isError = false) {
if (!el.externalTestingStatus) return;
el.externalTestingStatus.textContent = msg || '';
@@ -1566,11 +1543,6 @@
renderAvailable();
syncAvailableModelDatalist();
- const selectedAutoModel = state.planSettings && typeof state.planSettings.freePlanModel === 'string'
- ? state.planSettings.freePlanModel
- : (el.autoModelSelect ? el.autoModelSelect.value : '');
- populateAutoModelOptions(selectedAutoModel);
-
if (el.limitProvider) renderLimitModelOptions(el.limitProvider.value || 'openrouter');
}
@@ -1588,11 +1560,6 @@
state.configured = data.models || []; // Legacy support
renderOpencodeModels();
renderPublicModels();
- const selectedAutoModel = state.planSettings && typeof state.planSettings.freePlanModel === 'string'
- ? state.planSettings.freePlanModel
- : (el.autoModelSelect ? el.autoModelSelect.value : '');
- populateAutoModelOptions(selectedAutoModel);
- populateFreePlanModelOptions(selectedAutoModel);
syncAvailableModelDatalist();
if (el.limitProvider) renderLimitModelOptions(el.limitProvider.value || 'openrouter');
}
@@ -1678,7 +1645,6 @@
if (el.limitRpm) el.limitRpm.value = target.requestsPerMinute ?? '';
if (el.limitRph) el.limitRph.value = target.requestsPerHour ?? '';
if (el.limitRpd) el.limitRpd.value = target.requestsPerDay ?? '';
- if (el.limitBackup && state.opencodeBackupModel !== undefined) el.limitBackup.value = state.opencodeBackupModel || '';
}
async function loadProviderLimits() {
@@ -1695,12 +1661,9 @@
if (!state.providerLimits[p]) state.providerLimits[p] = {};
});
state.providerUsage = data.usage || [];
- state.opencodeBackupModel = data.opencodeBackupModel || '';
renderProviderOptions();
populateLimitForm(el.limitProvider ? el.limitProvider.value : 'openrouter', el.limitScope ? el.limitScope.value : 'provider');
renderProviderUsage();
- if (el.limitBackup && state.opencodeBackupModel !== undefined) el.limitBackup.value = state.opencodeBackupModel || '';
- populateOpencodeBackupOptions(state.opencodeBackupModel);
// refresh datalist with provider-specific models
syncAvailableModelDatalist();
renderPlanPriority();
@@ -1836,187 +1799,22 @@
}
async function loadPlanProviderSettings() {
- if (!el.planProviderForm && !el.autoModelForm && !el.planPriorityList) return;
+ if (!el.planProviderForm && !el.planPriorityList) return;
try {
const data = await api('/api/admin/plan-settings');
state.planSettings = {
provider: 'openrouter',
- freePlanModel: '',
planningChain: [],
...(data || {}),
};
if (el.planProvider) el.planProvider.value = state.planSettings.provider || 'openrouter';
- populateAutoModelOptions(state.planSettings.freePlanModel || '');
- populateFreePlanModelOptions(state.planSettings.freePlanModel || '');
renderPlanPriority();
} catch (err) {
if (el.planProviderForm) setPlanProviderStatus(err.message, true);
- if (el.autoModelForm) setAutoModelStatus(err.message, true);
if (el.planPriorityList) setPlanChainStatus(err.message, true);
}
}
- function populateAutoModelOptions(selectedValue) {
- if (!el.autoModelSelect) return;
-
- const normalizeTier = (tier) => {
- const normalized = String(tier || 'free').trim().toLowerCase();
- return ['free', 'plus', 'pro'].includes(normalized) ? normalized : 'free';
- };
-
- // Use publicModels if available, fallback to configured for legacy support
- const configured = state.publicModels.length > 0 ? state.publicModels : (Array.isArray(state.configured) ? state.configured : []);
- const configuredByName = new Map();
- configured.forEach((m) => {
- const name = (m && (m.name || m.id)) ? String(m.name || m.id).trim() : '';
- if (name) configuredByName.set(name, m);
- });
-
- const current = typeof selectedValue === 'string' ? selectedValue : el.autoModelSelect.value;
-
- el.autoModelSelect.innerHTML = '';
- const auto = document.createElement('option');
- auto.value = '';
- auto.textContent = 'Auto (first free model)';
- el.autoModelSelect.appendChild(auto);
-
- const freeModels = configured
- .filter((m) => normalizeTier(m.tier) === 'free')
- .map((m) => ({
- name: (m && (m.name || m.id)) ? String(m.name || m.id).trim() : '',
- label: (m && (m.label || m.name || m.id)) ? String(m.label || m.name || m.id).trim() : '',
- }))
- .filter((m) => m.name);
-
- const freeGroup = document.createElement('optgroup');
- freeGroup.label = 'Free-tier models';
-
- const freeNames = new Set();
- freeModels
- .sort((a, b) => a.label.localeCompare(b.label))
- .forEach((m) => {
- freeNames.add(m.name);
- const opt = document.createElement('option');
- opt.value = m.name;
- opt.textContent = m.label || m.name;
- freeGroup.appendChild(opt);
- });
-
- const discoveredNames = getAvailableModelNames()
- .map((name) => String(name || '').trim())
- .filter(Boolean)
- .filter((name, idx, arr) => arr.indexOf(name) === idx)
- .filter((name) => !freeNames.has(name));
-
- const discoveredGroup = document.createElement('optgroup');
- discoveredGroup.label = 'Other discovered models';
-
- discoveredNames
- .sort((a, b) => a.localeCompare(b))
- .forEach((name) => {
- const configuredModel = configuredByName.get(name);
- const tier = configuredModel ? normalizeTier(configuredModel.tier) : null;
- const opt = document.createElement('option');
- opt.value = name;
- if (!configuredModel) {
- opt.textContent = `${name} (unpublished)`;
- } else {
- opt.textContent = `${name} (${tier.toUpperCase()})`;
- if (tier !== 'free') opt.disabled = true;
- }
- discoveredGroup.appendChild(opt);
- });
-
- const hasFree = freeGroup.children.length > 0;
- const hasDiscovered = discoveredGroup.children.length > 0;
-
- if (hasFree) {
- el.autoModelSelect.appendChild(freeGroup);
- }
-
- if (hasDiscovered) {
- el.autoModelSelect.appendChild(discoveredGroup);
- }
-
- if (!hasFree && !hasDiscovered) {
- const note = document.createElement('option');
- note.value = '__none__';
- note.textContent = '(No models discovered yet)';
- note.disabled = true;
- el.autoModelSelect.appendChild(note);
- }
-
- const currentName = (current || '').trim();
- if (currentName && !Array.from(el.autoModelSelect.options).some((opt) => opt.value === currentName)) {
- const orphan = document.createElement('option');
- orphan.value = currentName;
- orphan.textContent = `${currentName} (current selection)`;
- el.autoModelSelect.appendChild(orphan);
- }
-
- el.autoModelSelect.value = currentName;
- }
-
- function populateFreePlanModelOptions(selectedValue) {
- if (!el.freePlanModel) return;
- const current = selectedValue || el.freePlanModel.value;
- el.freePlanModel.innerHTML = '';
- const auto = document.createElement('option');
- auto.value = '';
- auto.textContent = 'Auto (use default)';
- el.freePlanModel.appendChild(auto);
- (state.configured || []).forEach((m) => {
- const opt = document.createElement('option');
- opt.value = m.name || m.id || '';
- opt.textContent = m.label || m.name || m.id || '';
- el.freePlanModel.appendChild(opt);
- });
- if (current !== undefined && current !== null) {
- el.freePlanModel.value = current;
- }
- }
-
- function populateOpencodeBackupOptions(selectedValue) {
- console.log('populateOpencodeBackupOptions called with:', selectedValue);
- if (!el.opencodeBackup) {
- console.log('el.opencodeBackup is null, returning early');
- return;
- }
- console.log('el.opencodeBackup found, populating...');
- const current = selectedValue || el.opencodeBackup.value;
- el.opencodeBackup.innerHTML = '';
-
- const allModels = new Set();
- (state.available || []).forEach((m) => {
- const name = m.name || m.id || m;
- if (name) allModels.add(name);
- });
- (state.configured || []).forEach((m) => {
- if (m.name) allModels.add(m.name);
- });
- Object.values(state.providerModels || {}).forEach((arr) => {
- (arr || []).forEach((name) => { if (name) allModels.add(name); });
- });
-
- console.log('Found models:', Array.from(allModels));
- const sorted = Array.from(allModels).filter(Boolean).sort((a, b) => a.localeCompare(b));
-
- const none = document.createElement('option');
- none.value = '';
- none.textContent = 'None (no backup)';
- el.opencodeBackup.appendChild(none);
-
- sorted.forEach((name) => {
- const opt = document.createElement('option');
- opt.value = name;
- opt.textContent = name;
- el.opencodeBackup.appendChild(opt);
- });
-
- if (current) el.opencodeBackup.value = current;
- console.log('Dropdown populated with', sorted.length + 1, 'options');
- }
-
function formatDisplayDate(value) {
if (!value) return '—';
const date = new Date(value);
@@ -2750,47 +2548,6 @@
});
}
- if (el.opencodeBackupForm) {
- el.opencodeBackupForm.addEventListener('submit', async (e) => {
- e.preventDefault();
- const opencodeBackupModel = el.opencodeBackup ? el.opencodeBackup.value.trim() : '';
-
- setOpencodeBackupStatus('Saving...');
- try {
- const res = await api('/api/admin/provider-limits', {
- method: 'POST',
- body: JSON.stringify({ provider: 'opencode', scope: 'provider', model: '', tokensPerMinute: '', tokensPerDay: '', requestsPerMinute: '', requestsPerDay: '', opencodeBackupModel }),
- });
- // update local state and refresh dropdowns
- state.opencodeBackupModel = res.opencodeBackupModel || opencodeBackupModel || '';
- populateOpencodeBackupOptions(state.opencodeBackupModel);
- setOpencodeBackupStatus('Saved');
- setTimeout(() => setOpencodeBackupStatus(''), 3000);
- } catch (err) {
- setOpencodeBackupStatus(err.message, true);
- }
- });
- }
-
- if (el.autoModelForm) {
- el.autoModelForm.addEventListener('submit', async (e) => {
- e.preventDefault();
- const freePlanModel = el.autoModelSelect ? el.autoModelSelect.value.trim() : '';
-
- setAutoModelStatus('Saving...');
- try {
- await api('/api/admin/plan-settings', {
- method: 'POST',
- body: JSON.stringify({ freePlanModel }),
- });
- setAutoModelStatus('Saved! Free plan users will use this model.');
- setTimeout(() => setAutoModelStatus(''), 3000);
- } catch (err) {
- setAutoModelStatus(err.message, true);
- }
- });
- }
-
if (el.planProviderForm) {
el.planProviderForm.addEventListener('submit', async (e) => {
e.preventDefault();
@@ -2838,7 +2595,6 @@
requestsPerMinute: Number(el.limitRpm.value || 0),
requestsPerHour: Number(el.limitRph.value || 0),
requestsPerDay: Number(el.limitRpd.value || 0),
- opencodeBackupModel: el.limitBackup ? el.limitBackup.value.trim() : '',
};
setProviderLimitStatus('Saving...');
try {
diff --git a/chat/public/builder.js b/chat/public/builder.js
index 8c9400c..b7c9081 100644
--- a/chat/public/builder.js
+++ b/chat/public/builder.js
@@ -3650,14 +3650,6 @@ function getBackupModel(currentModel) {
const configuredModels = (state.models || []).map(normalize).filter(Boolean);
const current = (currentModel || '').trim();
- const preferredBackup = normalize(window.providerLimits?.opencodeBackupModel || '');
-
- // If we have a preferred backup model, use it if it's different from current
- // Don't require it to be in configured models since OpenCode CLI can access models directly
- if (preferredBackup && preferredBackup !== current) {
- return preferredBackup;
- }
-
if (!configuredModels.length) return null;
// If current model is auto/default or not in list, pick the first configured model
@@ -4829,15 +4821,8 @@ window.addEventListener('focus', () => {
async function loadProviderLimits() {
try {
- const data = await api('/api/provider-limits');
- if (data?.opencodeBackupModel) {
- window.providerLimits = {
- opencodeBackupModel: data.opencodeBackupModel
- };
- console.log('[PROVIDER-LIMITS] Loaded backup model:', window.providerLimits.opencodeBackupModel);
- }
+ await api('/api/provider-limits');
} catch (err) {
console.warn('[PROVIDER-LIMITS] Failed to load provider limits:', err);
- window.providerLimits = window.providerLimits || {};
}
}
diff --git a/chat/server.js b/chat/server.js
index 76d11d8..93539dd 100644
--- a/chat/server.js
+++ b/chat/server.js
@@ -1542,13 +1542,11 @@ let mistralSettings = {
const PLANNING_PROVIDERS = ['openrouter', 'mistral', 'google', 'groq', 'nvidia', 'ollama', 'cohere', 'kilo'];
let planSettings = {
provider: 'openrouter', // legacy field, retained for backwards compatibility
- freePlanModel: '',
planningChain: [], // [{ provider, model }]
};
let providerLimits = {
limits: {},
modelProviders: {},
- opencodeBackupModel: '',
};
let pendingProviderPersistTimer = null;
let providerUsage = {};
@@ -4927,10 +4925,8 @@ function resolvePlanModel(plan, requestedModel) {
const candidateList = getConfiguredModels();
- // For hobby/free plan users, use the admin-configured auto model
+ // For hobby/free plan users, use the first model from the fallback chain
if (normalized === 'hobby') {
- const adminDefault = (planSettings.freePlanModel || '').trim();
- if (adminDefault) return adminDefault;
const firstModel = candidateList[0];
if (firstModel) return firstModel.name;
return resolveFallbackModel();
@@ -5890,9 +5886,6 @@ async function loadPlanSettings() {
if (typeof parsed.provider === 'string' && PLANNING_PROVIDERS.includes(normalizeProviderName(parsed.provider))) {
planSettings.provider = normalizeProviderName(parsed.provider);
}
- if (typeof parsed.freePlanModel === 'string') {
- planSettings.freePlanModel = parsed.freePlanModel.trim();
- }
if (Array.isArray(parsed.planningChain)) {
planSettings.planningChain = normalizePlanningChain(parsed.planningChain);
}
@@ -6178,7 +6171,6 @@ async function loadProviderLimits() {
}
collectProviderSeeds().forEach((p) => ensureProviderLimitDefaults(p));
- if (!providerLimits.opencodeBackupModel) providerLimits.opencodeBackupModel = '';
}
async function persistProviderLimits() {
@@ -8764,16 +8756,6 @@ function buildPlanModelChain() {
const chain = normalizePlanningChain(planSettings.planningChain);
if (chain.length) return chain;
- // Check if freePlanModel has a provider prefix (e.g., "groq/compound-mini")
- const freePlanModel = (planSettings.freePlanModel || '').trim();
- if (freePlanModel) {
- const parsed = parseModelString(freePlanModel);
- if (parsed.provider) {
- // User specified a provider prefix, use it directly
- return [{ provider: parsed.provider, model: parsed.model }];
- }
- }
-
return defaultPlanningChainFromSettings(planSettings.provider);
}
@@ -10893,16 +10875,6 @@ async function sendToOpencodeWithFallback({ session, model, content, message, cl
if (result) return result;
}
- const backupModel = (providerLimits.opencodeBackupModel || '').trim();
- if (backupModel) {
- const backupChain = buildOpencodeAttemptChain(cliName, backupModel);
- for (const option of backupChain) {
- const result = await tryOption(option, true);
- if (result instanceof Error) break;
- if (result) return result;
- }
- }
-
const MAX_EARLY_TERMINATIONS = 2;
const recentEarlyTerminations = attempts.filter(
a => a.earlyTermination &&
@@ -16054,9 +16026,6 @@ async function handleAdminPlanSettingsPost(req, res) {
if (Array.isArray(body.planningChain)) {
planSettings.planningChain = normalizePlanningChain(body.planningChain);
}
- if (typeof body.freePlanModel === 'string') {
- planSettings.freePlanModel = body.freePlanModel.trim();
- }
if (!planSettings.planningChain.length) {
planSettings.planningChain = defaultPlanningChainFromSettings(planSettings.provider);
}
@@ -16142,7 +16111,6 @@ async function handleAdminProviderLimitsGet(req, res) {
sendJson(res, 200, {
limits: providerLimits.limits,
usage: snapshot,
- opencodeBackupModel: providerLimits.opencodeBackupModel || '',
providers: discovery.providers,
providerModels: discovery.providerModels,
});
@@ -16168,17 +16136,12 @@ async function handleAdminProviderLimitsPost(req, res) {
if (body[field] !== undefined) target[field] = sanitizeLimitNumber(body[field]);
});
- if (typeof body.opencodeBackupModel === 'string') {
- providerLimits.opencodeBackupModel = body.opencodeBackupModel.trim();
- }
-
await persistProviderLimits();
const discovery = await discoverProviderModels();
sendJson(res, 200, {
ok: true,
limits: providerLimits.limits,
usage: getProviderUsageSnapshot(discovery.providers),
- opencodeBackupModel: providerLimits.opencodeBackupModel || '',
providers: discovery.providers,
providerModels: discovery.providerModels,
});
@@ -19362,7 +19325,6 @@ async function bootstrap() {
});
console.log('[CONFIG] Planning Settings:', {
provider: planSettings.provider,
- freePlanModel: planSettings.freePlanModel || 'not set',
planningChainLength: planSettings.planningChain?.length || 0,
planningChain: planSettings.planningChain
});