fix: centralize model prefix handling to prevent provider errors

Moves prefix stripping from individual providers to AgentService/IdeationService
and adds validation to ensure providers receive bare model IDs. This prevents
bugs like the Codex CLI receiving "codex-gpt-5.1-codex-max" instead of the
expected "gpt-5.1-codex-max".

- Add validateBareModelId() helper with fail-fast validation
- Add originalModel field to ExecuteOptions for logging
- Update all providers to validate model has no prefix
- Centralize prefix stripping in service layer
- Remove redundant prefix stripping from individual providers

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
DhanushSantosh
2026-01-09 01:40:29 +05:30
parent a815be6a20
commit b2e5ff1460
9 changed files with 83 additions and 10 deletions

View File

@@ -10,7 +10,7 @@ import { BaseProvider } from './base-provider.js';
import { classifyError, getUserFriendlyErrorMessage, createLogger } from '@automaker/utils';
const logger = createLogger('ClaudeProvider');
import { getThinkingTokenBudget } from '@automaker/types';
import { getThinkingTokenBudget, validateBareModelId } from '@automaker/types';
import type {
ExecuteOptions,
ProviderMessage,
@@ -53,6 +53,10 @@ export class ClaudeProvider extends BaseProvider {
* Execute a query using Claude Agent SDK
*/
async *executeQuery(options: ExecuteOptions): AsyncGenerator<ProviderMessage> {
// Validate that model doesn't have a provider prefix
// AgentService should strip prefixes before passing to providers
validateBareModelId(options.model, 'ClaudeProvider');
const {
prompt,
model,

View File

@@ -31,6 +31,7 @@ import type {
import {
CODEX_MODEL_MAP,
supportsReasoningEffort,
validateBareModelId,
type CodexApprovalPolicy,
type CodexSandboxMode,
type CodexAuthStatus,
@@ -663,6 +664,10 @@ export class CodexProvider extends BaseProvider {
}
async *executeQuery(options: ExecuteOptions): AsyncGenerator<ProviderMessage> {
// Validate that model doesn't have a provider prefix
// AgentService should strip prefixes before passing to providers
validateBareModelId(options.model, 'CodexProvider');
try {
const mcpServers = options.mcpServers ?? {};
const hasMcpServers = Object.keys(mcpServers).length > 0;
@@ -760,6 +765,7 @@ export class CodexProvider extends BaseProvider {
}
}
// Model is already bare (no prefix) - validated by executeQuery
const args = [
CODEX_EXEC_SUBCOMMAND,
CODEX_YOLO_FLAG,

View File

@@ -28,7 +28,7 @@ import type {
ModelDefinition,
ContentBlock,
} from './types.js';
import { stripProviderPrefix } from '@automaker/types';
import { validateBareModelId } from '@automaker/types';
import { validateApiKey } from '../lib/auth-utils.js';
import { getEffectivePermissions } from '../services/cursor-config-service.js';
import {
@@ -317,8 +317,8 @@ export class CursorProvider extends CliProvider {
}
buildCliArgs(options: ExecuteOptions): string[] {
// Extract model (strip 'cursor-' prefix if present)
const model = stripProviderPrefix(options.model || 'auto');
// Model is already bare (no prefix) - validated by executeQuery
const model = options.model || 'auto';
// Build CLI arguments for cursor-agent
// NOTE: Prompt is NOT included here - it's passed via stdin to avoid
@@ -649,6 +649,10 @@ export class CursorProvider extends CliProvider {
async *executeQuery(options: ExecuteOptions): AsyncGenerator<ProviderMessage> {
this.ensureCliDetected();
// Validate that model doesn't have a provider prefix
// AgentService should strip prefixes before passing to providers
validateBareModelId(options.model, 'CursorProvider');
if (!this.cliPath) {
throw this.createError(
CursorErrorCode.NOT_INSTALLED,