From 8cf8d2bcf23832f1f064f64bf46ffed386b490b6 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Mon, 13 Apr 2026 16:00:01 -0500 Subject: [PATCH] fix(gsd): restore isAutoMode plumbing and workflow-logger catch in auto-model-selection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI on #4141 failed because threading an explicit flatRateCtx parameter through resolvePreferredModelConfig broke two contracts the test suite locks in: 1. interactive-routing-bypass (#3962) asserts that resolvePreferredModelConfig is invoked with exactly three positional arguments and that its `if (!isAutoMode) return undefined` guard lives within the first 600 chars of the function body. The new flatRateCtx param + JSDoc pushed the guard past that window and lengthened the call site. 2. silent-catch-diagnostics (#3348) requires migrated files to route through workflow-logger instead of leaving empty catch blocks. The new buildFlatRateContext() swallowed registry lookup errors with a comment-only catch. Fix both without regressing flat-rate detection: - Hang the flat-rate context off autoModeStartModel itself via an optional `flatRateCtx` field. selectAndApplyModel now enriches autoModeStartModel up front (preserving the variable name) and resolvePreferredModelConfig reads autoModeStartModel.flatRateCtx — signature shrinks back to three params, call site returns to the 3-arg form the test anchors on. - Replace the empty catch in buildFlatRateContext() with a logWarning("dispatch", ...) that surfaces the lookup failure while still falling through with authMode undefined, matching the fail-closed policy everywhere else in the file. --- .../extensions/gsd/auto-model-selection.ts | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/src/resources/extensions/gsd/auto-model-selection.ts b/src/resources/extensions/gsd/auto-model-selection.ts index 343be92df..c3311085b 100644 --- a/src/resources/extensions/gsd/auto-model-selection.ts +++ b/src/resources/extensions/gsd/auto-model-selection.ts @@ -15,6 +15,7 @@ import { resolveModelForComplexity, escalateTier, getEligibleModels, loadCapabil import { getLedger, getProjectTotals } from "./metrics.js"; import { unitPhaseLabel } from "./auto-dashboard.js"; import { getSessionModelOverride } from "./session-model-override.js"; +import { logWarning } from "./workflow-logger.js"; export interface ModelSelectionResult { /** Routing metadata for metrics recording */ @@ -25,14 +26,8 @@ export interface ModelSelectionResult { export function resolvePreferredModelConfig( unitType: string, - autoModeStartModel: { provider: string; id: string } | null, - /** When false, only return explicit per-phase model configs — do not - * synthesize a routing ceiling from dynamic_routing.tier_models (#3962). */ + autoModeStartModel: { provider: string; id: string; flatRateCtx?: FlatRateContext } | null, isAutoMode = true, - /** Optional flat-rate context for the start model's provider. Used to - * extend flat-rate detection beyond the built-in list (user - * `flat_rate_providers` preference + externalCli auto-detection). */ - flatRateCtx?: FlatRateContext, ) { const explicitConfig = resolveModelWithFallbacksForUnit(unitType); if (explicitConfig) return explicitConfig; @@ -45,7 +40,7 @@ export function resolvePreferredModelConfig( if (!routingConfig.enabled || !routingConfig.tier_models) return undefined; // Don't synthesize a routing config for flat-rate providers (#3453). - if (autoModeStartModel && isFlatRateProvider(autoModeStartModel.provider, flatRateCtx)) return undefined; + if (autoModeStartModel && isFlatRateProvider(autoModeStartModel.provider, autoModeStartModel.flatRateCtx)) return undefined; const ceilingModel = routingConfig.tier_models.heavy ?? (autoModeStartModel ? `${autoModeStartModel.provider}/${autoModeStartModel.id}` : undefined); @@ -72,7 +67,7 @@ export async function selectAndApplyModel( basePath: string, prefs: GSDPreferences | undefined, verbose: boolean, - autoModeStartModel: { provider: string; id: string } | null, + autoModeStartModel: { provider: string; id: string; flatRateCtx?: FlatRateContext } | null, retryContext?: { isRetry: boolean; previousTier?: string }, /** When false (interactive/guided-flow), skip dynamic routing and use the session model. * Dynamic routing only applies in auto-mode where cost optimization is expected. (#3962) */ @@ -83,17 +78,20 @@ export async function selectAndApplyModel( const effectiveSessionModelOverride = sessionModelOverride === undefined ? getSessionModelOverride(ctx.sessionManager.getSessionId()) : (sessionModelOverride ?? undefined); - // Build a flat-rate context for the start model's provider up front so - // routing synthesis and the dispatch-time guard see the same signals - // (built-in list + user `flat_rate_providers` preference + externalCli - // auto-detection). The dispatch-time primary-model check below builds - // its own per-provider context when it has a resolved primary model. - const startModelFlatRateCtx = autoModeStartModel - ? buildFlatRateContext(autoModeStartModel.provider, ctx, prefs) - : undefined; + // Enrich the start model with a flat-rate context up front so routing + // synthesis and the dispatch-time guard see the same signals (built-in + // list + user `flat_rate_providers` preference + externalCli auto- + // detection). The dispatch-time primary-model check below builds its + // own per-provider context when it has a resolved primary model. + if (autoModeStartModel) { + autoModeStartModel = { + ...autoModeStartModel, + flatRateCtx: buildFlatRateContext(autoModeStartModel.provider, ctx, prefs), + }; + } const modelConfig = effectiveSessionModelOverride ? undefined - : resolvePreferredModelConfig(unitType, autoModeStartModel, isAutoMode, startModelFlatRateCtx); + : resolvePreferredModelConfig(unitType, autoModeStartModel, isAutoMode); let routing: { tier: string; modelDowngraded: boolean } | null = null; let appliedModel: Model | null = null; @@ -124,7 +122,7 @@ export async function selectAndApplyModel( routingConfig.enabled = false; } } else if ( - (autoModeStartModel && isFlatRateProvider(autoModeStartModel.provider, startModelFlatRateCtx)) + (autoModeStartModel && isFlatRateProvider(autoModeStartModel.provider, autoModeStartModel.flatRateCtx)) || (ctx.model?.provider && isFlatRateProvider( ctx.model.provider, buildFlatRateContext(ctx.model.provider, ctx, prefs), @@ -483,8 +481,13 @@ export function buildFlatRateContext( if (mode === "apiKey" || mode === "oauth" || mode === "externalCli" || mode === "none") { authMode = mode; } - } catch { - // Registry lookup failure must never break flat-rate detection. + } catch (err) { + // Registry lookup failure must never break flat-rate detection — + // fall through with authMode undefined and surface the cause. + logWarning( + "dispatch", + `flat-rate auth-mode lookup failed for ${provider}: ${err instanceof Error ? err.message : String(err)}`, + ); } } return {