Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 28 additions & 89 deletions extensions/thronekeeper/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ async function fetchAnthropicDefaults(secrets?: SecretsService): Promise<{ opus:
const data: any = await response.body.json();
const models = data.data || [];

// DEBUG: Log all raw model IDs before filtering
console.log(`[fetchAnthropicDefaults] All ${models.length} models from API:`, models.map((m: any) => m.id));

Comment on lines +126 to +128
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Gate raw model-ID dump behind debug mode.

Line 127 logs the full model-id list unconditionally; that’s noisy in normal runs and can flood extension logs like leaving a faucet open. Please guard it behind the debug setting.

💡 Suggested patch
-    // DEBUG: Log all raw model IDs before filtering
-    console.log(`[fetchAnthropicDefaults] All ${models.length} models from API:`, models.map((m: any) => m.id));
+    const debug = vscode.workspace.getConfiguration('claudeThrone').get<boolean>('proxy.debug', false)
+    if (debug) {
+      console.log(
+        `[fetchAnthropicDefaults] All ${models.length} models from API:`,
+        models.map((m: any) => m.id)
+      )
+    }

As per coding guidelines "Logging: use Fastify's logger; gate verbose output behind DEBUG."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@extensions/thronekeeper/src/extension.ts` around lines 126 - 128, The
unconditional console.log in fetchAnthropicDefaults printing models.map((m: any)
=> m.id) is noisy; wrap this raw model-ID dump behind the debug flag and use the
Fastify logger instead of console. Replace the direct console.log with a
conditional that checks the extension debug setting (e.g. process.env.DEBUG or
the extension's debug config) and call fastify.log.debug(...) (or the injected
logger) to emit the message only when debug is enabled, referencing the same
models variable and the existing fetchAnthropicDefaults function.

// Helper to select best model: prefer -latest alias, exclude -preview and other unstable suffixes
const selectBestModel = (filtered: any[], fallback: string): string => {
if (filtered.length === 0) return fallback;
Expand Down Expand Up @@ -187,15 +190,15 @@ async function fetchAnthropicDefaults(secrets?: SecretsService): Promise<{ opus:

// Find latest Opus model
const opusModels = models.filter((m: any) => m.id.includes('opus'));
const opus = selectBestModel(opusModels, 'claude-opus-4-1-20250805');
const opus = selectBestModel(opusModels, 'claude-opus-4-6-latest');

// Find latest Sonnet model
const sonnetModels = models.filter((m: any) => m.id.includes('sonnet'));
const sonnet = selectBestModel(sonnetModels, 'claude-sonnet-4-5-20250929');
const sonnet = selectBestModel(sonnetModels, 'claude-sonnet-4-6-latest');

// Find latest Haiku model
const haikuModels = models.filter((m: any) => m.id.includes('haiku'));
const haiku = selectBestModel(haikuModels, 'claude-3-5-haiku-latest');
const haiku = selectBestModel(haikuModels, 'claude-haiku-4-5-latest');

// Add detailed logging
console.log(`[fetchAnthropicDefaults] Model selection summary:`);
Expand All @@ -217,7 +220,7 @@ async function fetchAnthropicDefaults(secrets?: SecretsService): Promise<{ opus:
const authStatus = secrets ? (await secrets.getAnthropicKey() ? 'authenticated' : 'unauthenticated') : 'no secrets service';
console.error(`[fetchAnthropicDefaults] Failed to fetch (${authStatus}):`, error);
// Return hardcoded fallbacks if fetch fails
return { opus: 'claude-opus-4-1-20250805', sonnet: 'claude-sonnet-4-5-20250929', haiku: 'claude-3-5-haiku-latest' };
return { opus: 'claude-opus-4-6-latest', sonnet: 'claude-sonnet-4-6-latest', haiku: 'claude-haiku-4-5-latest' };
}
}

Expand Down Expand Up @@ -351,89 +354,26 @@ export function activate(context: vscode.ExtensionContext) {
}
}

// Check cache age before fetching fresh defaults
const cachedTimestamp = cfg.get<number>('anthropicDefaultsTimestamp', 0);
const cacheAge = Date.now() - cachedTimestamp;
const stalenessThreshold = 7 * 24 * 60 * 60 * 1000; // 7 days in milliseconds

if (cachedTimestamp > 0 && cacheAge > stalenessThreshold) {
const daysOld = Math.floor(cacheAge / (24 * 60 * 60 * 1000));
log.appendLine(`[revertApply] ⚠️ Cache is ${daysOld} days old (threshold: 7 days), fetching fresh defaults...`);
} else if (cachedTimestamp > 0) {
const daysOld = Math.floor(cacheAge / (24 * 60 * 60 * 1000));
log.appendLine(`[revertApply] Cache is ${daysOld} days old, fetching fresh defaults...`);
} else {
log.appendLine('[revertApply] No cache timestamp found, fetching fresh defaults...');
}

// Try to fetch fresh defaults from API (if key is available)
log.appendLine('[revertApply] Attempting to fetch fresh Anthropic model defaults...');
let defaults = await fetchAnthropicDefaults(secrets);
let usedFreshDefaults = true;

if (defaults.opus && defaults.sonnet && defaults.haiku) {
log.appendLine(`[revertApply] ✅ Fetched fresh defaults: opus=${defaults.opus}, sonnet=${defaults.sonnet}, haiku=${defaults.haiku}`);

// Update cache with fresh values using safe configuration updates
const defaultsSuccess = await safeConfigUpdate(
cfg,
'anthropicDefaults',
defaults,
vscode.ConfigurationTarget.Global
);

const timestampSuccess = await safeConfigUpdate(
cfg,
'anthropicDefaultsTimestamp',
Date.now(),
vscode.ConfigurationTarget.Global
);

if (!defaultsSuccess || !timestampSuccess) {
log.appendLine('[revertApply] ⚠️ Failed to cache fresh defaults, but will continue with revert operation');
}
} else {
// Should not happen since fetchAnthropicDefaults always returns defaults, but keep fallback logic
log.appendLine('[revertApply] ⚠️ Unexpected: defaults missing fields, falling back to cached values');
usedFreshDefaults = false;
const cached = cfg.get<any>('anthropicDefaults', null);
if (cached) defaults = cached;
}

// Single atomic operation: remove Thronekeeper overrides and restore Anthropic defaults
// Remove ALL ThroneKeeper overrides — let Claude Code use its built-in defaults.
// No need to fetch from Anthropic API; Claude Code resolves its own defaults
// when these keys are absent from settings.
if (settingsDir) {
const restoreEnv: Record<string, any> = {
// Always set base URL to Anthropic
ANTHROPIC_BASE_URL: 'https://api.anthropic.com',
ANTHROPIC_BASE_URL: null,
ANTHROPIC_MODEL: null,
ANTHROPIC_DEFAULT_OPUS_MODEL: null,
ANTHROPIC_DEFAULT_SONNET_MODEL: null,
ANTHROPIC_DEFAULT_HAIKU_MODEL: null,
API_TIMEOUT_MS: null,
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: null,
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: null,
};

// If we have cached defaults, restore model settings
if (defaults?.opus && defaults?.sonnet && defaults?.haiku) {
restoreEnv.ANTHROPIC_MODEL = defaults.sonnet;
restoreEnv.ANTHROPIC_DEFAULT_OPUS_MODEL = defaults.opus;
restoreEnv.ANTHROPIC_DEFAULT_SONNET_MODEL = defaults.sonnet;
restoreEnv.ANTHROPIC_DEFAULT_HAIKU_MODEL = defaults.haiku;
} else {
// No cached defaults: explicitly remove model env vars
// (Claude Code will use its own defaults)
restoreEnv.ANTHROPIC_MODEL = null;
restoreEnv.ANTHROPIC_DEFAULT_OPUS_MODEL = null;
restoreEnv.ANTHROPIC_DEFAULT_SONNET_MODEL = null;
restoreEnv.ANTHROPIC_DEFAULT_HAIKU_MODEL = null;
}

// Remove Claude Code environment variables when reverting
restoreEnv.API_TIMEOUT_MS = null;
restoreEnv.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = null;
restoreEnv.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = null;

// Single call: atomic update
await updateClaudeSettings(settingsDir, restoreEnv, /*revert*/ false);
await updateClaudeSettings(settingsDir, restoreEnv, /*revert=*/ false);
}

// NOTE: We do NOT clear reasoningModel/completionModel here anymore
// User's saved model preferences should persist across proxy stop/start cycles
// Only .claude/settings.json is reverted to Anthropic defaults for Claude Code CLI
// Only .claude/settings.json is reverted to clean state for Claude Code CLI

// Restore base URL to Anthropic defaults in extension settings
const candidates: { section: string; key: string }[] = [
Expand Down Expand Up @@ -513,14 +453,11 @@ export function activate(context: vscode.ExtensionContext) {
const parts: string[] = []
if (restored.length) parts.push(`extensions: ${restored.join(', ')}`)
if (termTouched) parts.push(`terminal env: ${termTouched} target(s)`)
if (defaults && defaults.opus && defaults.sonnet && defaults.haiku) {
const source = usedFreshDefaults ? 'fetched latest from API' : 'cached';
parts.push(`models (${source}): ${defaults.opus.split('-').slice(-1)[0]} (Opus), ${defaults.sonnet.split('-').slice(-1)[0]} (Sonnet), ${defaults.haiku.split('-').slice(-1)[0]} (Haiku)`)
}
if (settingsDir) parts.push('.claude/settings.json cleaned')
if (parts.length) {
vscode.window.showInformationMessage(`Restored Anthropic defaults (${parts.join(' | ')}). Open a new terminal for env changes to take effect.`)
vscode.window.showInformationMessage(`Reverted ThroneKeeper overrides (${parts.join(', ')}). Claude Code will use its built-in defaults.`)
} else {
vscode.window.showInformationMessage('No Claude overrides were found to restore.')
vscode.window.showInformationMessage('No ThroneKeeper overrides were found to revert.')
}
} catch (err: any) {
log.appendLine(`[revertApply] Unexpected error during revert: ${err?.stack || err}`);
Expand Down Expand Up @@ -846,7 +783,7 @@ export function activate(context: vscode.ExtensionContext) {
const applied: string[] = [];

if (applyToExtensions) {
log.appendLine('[applyToClaudeCode] applyToExtensions=true, writing to other extension settings...');
log.appendLine('[applyToClaudeCode] applyToExtensions=true, writing to other extension settings...')
const candidates: { section: string; key: string }[] = [
{ section: 'anthropic', key: 'baseUrl' },
{ section: 'claude', key: 'baseUrl' },
Expand Down Expand Up @@ -1211,6 +1148,8 @@ export function activate(context: vscode.ExtensionContext) {
storeTogetherKey,
storeDeepseekKey,
storeGlmKey,
storeKimiKey,
storeMinimaxKey,
storeCustomKey,
storeAnyKey,
storeAnthropicKey,
Expand Down Expand Up @@ -1362,4 +1301,4 @@ async function storeKey(provider: Provider, secrets: SecretsService) {

export function deactivate() {
try { proxy?.stop() } catch {}
}
}
Loading