Merge branch 'v0.9.0rc' into remove-sandbox-as-it-is-broken

This commit is contained in:
webdevcody
2026-01-07 15:01:31 -05:00
115 changed files with 8734 additions and 401 deletions

View File

@@ -0,0 +1,100 @@
/**
* Codex CLI Model IDs
* Based on OpenAI Codex CLI official models
* Reference: https://developers.openai.com/codex/models/
*/
export type CodexModelId =
| 'gpt-5.2-codex' // Most advanced agentic coding model for complex software engineering
| 'gpt-5-codex' // Purpose-built for Codex CLI with versatile tool use
| 'gpt-5-codex-mini' // Faster workflows optimized for low-latency code Q&A and editing
| 'codex-1' // Version of o3 optimized for software engineering
| 'codex-mini-latest' // Version of o4-mini for Codex, optimized for faster workflows
| 'gpt-5'; // GPT-5 base flagship model
/**
* Codex model metadata
*/
export interface CodexModelConfig {
id: CodexModelId;
label: string;
description: string;
hasThinking: boolean;
/** Whether the model supports vision/image inputs */
supportsVision: boolean;
}
/**
* Complete model map for Codex CLI
*/
export const CODEX_MODEL_CONFIG_MAP: Record<CodexModelId, CodexModelConfig> = {
'gpt-5.2-codex': {
id: 'gpt-5.2-codex',
label: 'GPT-5.2-Codex',
description: 'Most advanced agentic coding model for complex software engineering',
hasThinking: true,
supportsVision: true, // GPT-5 supports vision
},
'gpt-5-codex': {
id: 'gpt-5-codex',
label: 'GPT-5-Codex',
description: 'Purpose-built for Codex CLI with versatile tool use',
hasThinking: true,
supportsVision: true,
},
'gpt-5-codex-mini': {
id: 'gpt-5-codex-mini',
label: 'GPT-5-Codex-Mini',
description: 'Faster workflows optimized for low-latency code Q&A and editing',
hasThinking: false,
supportsVision: true,
},
'codex-1': {
id: 'codex-1',
label: 'Codex-1',
description: 'Version of o3 optimized for software engineering',
hasThinking: true,
supportsVision: true,
},
'codex-mini-latest': {
id: 'codex-mini-latest',
label: 'Codex-Mini-Latest',
description: 'Version of o4-mini for Codex, optimized for faster workflows',
hasThinking: false,
supportsVision: true,
},
'gpt-5': {
id: 'gpt-5',
label: 'GPT-5',
description: 'GPT-5 base flagship model',
hasThinking: true,
supportsVision: true,
},
};
/**
* Helper: Check if model has thinking capability
*/
export function codexModelHasThinking(modelId: CodexModelId): boolean {
return CODEX_MODEL_CONFIG_MAP[modelId]?.hasThinking ?? false;
}
/**
* Helper: Get display name for model
*/
export function getCodexModelLabel(modelId: CodexModelId): string {
return CODEX_MODEL_CONFIG_MAP[modelId]?.label ?? modelId;
}
/**
* Helper: Get all Codex model IDs
*/
export function getAllCodexModelIds(): CodexModelId[] {
return Object.keys(CODEX_MODEL_CONFIG_MAP) as CodexModelId[];
}
/**
* Helper: Check if Codex model supports vision
*/
export function codexModelSupportsVision(modelId: CodexModelId): boolean {
return CODEX_MODEL_CONFIG_MAP[modelId]?.supportsVision ?? true;
}

52
libs/types/src/codex.ts Normal file
View File

@@ -0,0 +1,52 @@
/** Sandbox modes for Codex CLI command execution */
export type CodexSandboxMode = 'read-only' | 'workspace-write' | 'danger-full-access';
/** Approval policies for Codex CLI tool execution */
export type CodexApprovalPolicy = 'untrusted' | 'on-failure' | 'on-request' | 'never';
/** Codex event types emitted by CLI */
export type CodexEventType =
| 'thread.started'
| 'turn.started'
| 'turn.completed'
| 'turn.failed'
| 'item.completed'
| 'error';
/** Codex item types in CLI events */
export type CodexItemType =
| 'agent_message'
| 'reasoning'
| 'command_execution'
| 'file_change'
| 'mcp_tool_call'
| 'web_search'
| 'plan_update';
/** Codex CLI event structure */
export interface CodexEvent {
type: CodexEventType;
thread_id?: string;
item?: {
type: CodexItemType;
content?: string;
[key: string]: unknown;
};
[key: string]: unknown;
}
/** Codex CLI configuration (stored in .automaker/codex-config.json) */
export interface CodexCliConfig {
/** Default model to use when not specified */
defaultModel?: string;
/** List of enabled models */
models?: string[];
}
/** Codex authentication status */
export interface CodexAuthStatus {
authenticated: boolean;
method: 'oauth' | 'api_key' | 'none';
hasCredentialsFile?: boolean;
error?: string;
}

View File

@@ -217,6 +217,7 @@ export interface CursorAuthStatus {
authenticated: boolean;
method: 'login' | 'api_key' | 'none';
hasCredentialsFile?: boolean;
error?: string;
}
/**

View File

@@ -17,8 +17,18 @@ export type {
McpStdioServerConfig,
McpSSEServerConfig,
McpHttpServerConfig,
ReasoningEffort,
} from './provider.js';
// Codex CLI types
export type {
CodexSandboxMode,
CodexApprovalPolicy,
CodexCliConfig,
CodexAuthStatus,
} from './codex.js';
export * from './codex-models.js';
// Feature types
export type {
Feature,
@@ -43,7 +53,18 @@ export type { ErrorType, ErrorInfo } from './error.js';
export type { ImageData, ImageContentBlock } from './image.js';
// Model types and constants
export { CLAUDE_MODEL_MAP, DEFAULT_MODELS, type ModelAlias } from './model.js';
export {
CLAUDE_MODEL_MAP,
CODEX_MODEL_MAP,
CODEX_MODEL_IDS,
REASONING_CAPABLE_MODELS,
supportsReasoningEffort,
getAllCodexModelIds,
DEFAULT_MODELS,
type ModelAlias,
type CodexModelId,
type AgentModel,
} from './model.js';
// Event types
export type { EventType, EventCallback } from './event.js';
@@ -109,11 +130,13 @@ export {
} from './settings.js';
// Model display constants
export type { ModelOption, ThinkingLevelOption } from './model-display.js';
export type { ModelOption, ThinkingLevelOption, ReasoningEffortOption } from './model-display.js';
export {
CLAUDE_MODELS,
THINKING_LEVELS,
THINKING_LEVEL_LABELS,
REASONING_EFFORT_LEVELS,
REASONING_EFFORT_LABELS,
getModelDisplayName,
} from './model-display.js';
@@ -156,6 +179,7 @@ export {
PROVIDER_PREFIXES,
isCursorModel,
isClaudeModel,
isCodexModel,
getModelProvider,
stripProviderPrefix,
addProviderPrefix,

View File

@@ -6,7 +6,10 @@
*/
import type { ModelAlias, ThinkingLevel, ModelProvider } from './settings.js';
import type { ReasoningEffort } from './provider.js';
import type { CursorModelId } from './cursor-models.js';
import type { AgentModel, CodexModelId } from './model.js';
import { CODEX_MODEL_MAP } from './model.js';
/**
* ModelOption - Display metadata for a model option in the UI
@@ -63,6 +66,61 @@ export const CLAUDE_MODELS: ModelOption[] = [
},
];
/**
* Codex model options with full metadata for UI display
* Official models from https://developers.openai.com/codex/models/
*/
export const CODEX_MODELS: (ModelOption & { hasReasoning?: boolean })[] = [
{
id: CODEX_MODEL_MAP.gpt52Codex,
label: 'GPT-5.2-Codex',
description: 'Most advanced agentic coding model (default for ChatGPT users).',
badge: 'Premium',
provider: 'codex',
hasReasoning: true,
},
{
id: CODEX_MODEL_MAP.gpt5Codex,
label: 'GPT-5-Codex',
description: 'Purpose-built for Codex CLI (default for CLI users).',
badge: 'Balanced',
provider: 'codex',
hasReasoning: true,
},
{
id: CODEX_MODEL_MAP.gpt5CodexMini,
label: 'GPT-5-Codex-Mini',
description: 'Faster workflows for code Q&A and editing.',
badge: 'Speed',
provider: 'codex',
hasReasoning: false,
},
{
id: CODEX_MODEL_MAP.codex1,
label: 'Codex-1',
description: 'o3-based model optimized for software engineering.',
badge: 'Premium',
provider: 'codex',
hasReasoning: true,
},
{
id: CODEX_MODEL_MAP.codexMiniLatest,
label: 'Codex-Mini-Latest',
description: 'o4-mini-based model for faster workflows.',
badge: 'Balanced',
provider: 'codex',
hasReasoning: false,
},
{
id: CODEX_MODEL_MAP.gpt5,
label: 'GPT-5',
description: 'GPT-5 base flagship model.',
badge: 'Balanced',
provider: 'codex',
hasReasoning: true,
},
];
/**
* Thinking level options with display labels
*
@@ -89,6 +147,43 @@ export const THINKING_LEVEL_LABELS: Record<ThinkingLevel, string> = {
ultrathink: 'Ultra',
};
/**
* ReasoningEffortOption - Display metadata for reasoning effort selection (Codex/OpenAI)
*/
export interface ReasoningEffortOption {
/** Reasoning effort identifier */
id: ReasoningEffort;
/** Display label */
label: string;
/** Description of what this level does */
description: string;
}
/**
* Reasoning effort options for Codex/OpenAI models
* All models support reasoning effort levels
*/
export const REASONING_EFFORT_LEVELS: ReasoningEffortOption[] = [
{ id: 'none', label: 'None', description: 'No reasoning tokens (GPT-5.1 models only)' },
{ id: 'minimal', label: 'Minimal', description: 'Very quick reasoning' },
{ id: 'low', label: 'Low', description: 'Quick responses for simpler queries' },
{ id: 'medium', label: 'Medium', description: 'Balance between depth and speed (default)' },
{ id: 'high', label: 'High', description: 'Maximizes reasoning depth for critical tasks' },
{ id: 'xhigh', label: 'XHigh', description: 'Highest level for gpt-5.1-codex-max and newer' },
];
/**
* Map of reasoning effort levels to short display labels
*/
export const REASONING_EFFORT_LABELS: Record<ReasoningEffort, string> = {
none: 'None',
minimal: 'Min',
low: 'Low',
medium: 'Med',
high: 'High',
xhigh: 'XHigh',
};
/**
* Get display name for a model
*
@@ -107,6 +202,12 @@ export function getModelDisplayName(model: ModelAlias | string): string {
haiku: 'Claude Haiku',
sonnet: 'Claude Sonnet',
opus: 'Claude Opus',
[CODEX_MODEL_MAP.gpt52Codex]: 'GPT-5.2-Codex',
[CODEX_MODEL_MAP.gpt5Codex]: 'GPT-5-Codex',
[CODEX_MODEL_MAP.gpt5CodexMini]: 'GPT-5-Codex-Mini',
[CODEX_MODEL_MAP.codex1]: 'Codex-1',
[CODEX_MODEL_MAP.codexMiniLatest]: 'Codex-Mini-Latest',
[CODEX_MODEL_MAP.gpt5]: 'GPT-5',
};
return displayNames[model] || model;
}

View File

@@ -7,12 +7,70 @@ export const CLAUDE_MODEL_MAP: Record<string, string> = {
opus: 'claude-opus-4-5-20251101',
} as const;
/**
* Codex/OpenAI model identifiers
* Based on OpenAI Codex CLI official models
* See: https://developers.openai.com/codex/models/
*/
export const CODEX_MODEL_MAP = {
// Codex-specific models
/** Most advanced agentic coding model for complex software engineering (default for ChatGPT users) */
gpt52Codex: 'gpt-5.2-codex',
/** Purpose-built for Codex CLI with versatile tool use (default for CLI users) */
gpt5Codex: 'gpt-5-codex',
/** Faster workflows optimized for low-latency code Q&A and editing */
gpt5CodexMini: 'gpt-5-codex-mini',
/** Version of o3 optimized for software engineering */
codex1: 'codex-1',
/** Version of o4-mini for Codex, optimized for faster workflows */
codexMiniLatest: 'codex-mini-latest',
// Base GPT-5 model (also available in Codex)
/** GPT-5 base flagship model */
gpt5: 'gpt-5',
} as const;
export const CODEX_MODEL_IDS = Object.values(CODEX_MODEL_MAP);
/**
* Models that support reasoning effort configuration
* These models can use reasoning.effort parameter
*/
export const REASONING_CAPABLE_MODELS = new Set([
CODEX_MODEL_MAP.gpt52Codex,
CODEX_MODEL_MAP.gpt5Codex,
CODEX_MODEL_MAP.gpt5,
CODEX_MODEL_MAP.codex1, // o3-based model
]);
/**
* Check if a model supports reasoning effort configuration
*/
export function supportsReasoningEffort(modelId: string): boolean {
return REASONING_CAPABLE_MODELS.has(modelId as any);
}
/**
* Get all Codex model IDs as an array
*/
export function getAllCodexModelIds(): CodexModelId[] {
return CODEX_MODEL_IDS as CodexModelId[];
}
/**
* Default models per provider
*/
export const DEFAULT_MODELS = {
claude: 'claude-opus-4-5-20251101',
cursor: 'auto', // Cursor's recommended default
codex: CODEX_MODEL_MAP.gpt52Codex, // GPT-5.2-Codex is the most advanced agentic coding model
} as const;
export type ModelAlias = keyof typeof CLAUDE_MODEL_MAP;
export type CodexModelId = (typeof CODEX_MODEL_MAP)[keyof typeof CODEX_MODEL_MAP];
/**
* AgentModel - Alias for ModelAlias for backward compatibility
* Represents available models across providers
*/
export type AgentModel = ModelAlias | CodexModelId;

View File

@@ -8,11 +8,12 @@
import type { ModelProvider } from './settings.js';
import { CURSOR_MODEL_MAP, type CursorModelId } from './cursor-models.js';
import { CLAUDE_MODEL_MAP } from './model.js';
import { CLAUDE_MODEL_MAP, CODEX_MODEL_MAP, type CodexModelId } from './model.js';
/** Provider prefix constants */
export const PROVIDER_PREFIXES = {
cursor: 'cursor-',
codex: 'codex-',
// Add new provider prefixes here
} as const;
@@ -52,6 +53,35 @@ export function isClaudeModel(model: string | undefined | null): boolean {
return model.includes('claude-');
}
/**
* Check if a model string represents a Codex/OpenAI model
*
* @param model - Model string to check (e.g., "gpt-5.2", "o1", "codex-gpt-5.2")
* @returns true if the model is a Codex model
*/
export function isCodexModel(model: string | undefined | null): boolean {
if (!model || typeof model !== 'string') return false;
// Check for explicit codex- prefix
if (model.startsWith(PROVIDER_PREFIXES.codex)) {
return true;
}
// Check if it's a gpt- model
if (model.startsWith('gpt-')) {
return true;
}
// Check if it's an o-series model (o1, o3, etc.)
if (/^o\d/.test(model)) {
return true;
}
// Check if it's in the CODEX_MODEL_MAP
const modelValues = Object.values(CODEX_MODEL_MAP);
return modelValues.includes(model as CodexModelId);
}
/**
* Get the provider for a model string
*
@@ -59,6 +89,11 @@ export function isClaudeModel(model: string | undefined | null): boolean {
* @returns The provider type, defaults to 'claude' for unknown models
*/
export function getModelProvider(model: string | undefined | null): ModelProvider {
// Check Codex first before Cursor, since Cursor also supports gpt models
// but bare gpt-* should route to Codex
if (isCodexModel(model)) {
return 'codex';
}
if (isCursorModel(model)) {
return 'cursor';
}
@@ -96,6 +131,7 @@ export function stripProviderPrefix(model: string): string {
* @example
* addProviderPrefix('composer-1', 'cursor') // 'cursor-composer-1'
* addProviderPrefix('cursor-composer-1', 'cursor') // 'cursor-composer-1' (no change)
* addProviderPrefix('gpt-5.2', 'codex') // 'codex-gpt-5.2'
* addProviderPrefix('sonnet', 'claude') // 'sonnet' (Claude doesn't use prefix)
*/
export function addProviderPrefix(model: string, provider: ModelProvider): string {
@@ -105,6 +141,10 @@ export function addProviderPrefix(model: string, provider: ModelProvider): strin
if (!model.startsWith(PROVIDER_PREFIXES.cursor)) {
return `${PROVIDER_PREFIXES.cursor}${model}`;
}
} else if (provider === 'codex') {
if (!model.startsWith(PROVIDER_PREFIXES.codex)) {
return `${PROVIDER_PREFIXES.codex}${model}`;
}
}
// Claude models don't use prefixes
return model;
@@ -123,6 +163,7 @@ export function getBareModelId(model: string): string {
/**
* Normalize a model string to its canonical form
* - For Cursor: adds cursor- prefix if missing
* - For Codex: can add codex- prefix (but bare gpt-* is also valid)
* - For Claude: returns as-is
*
* @param model - Model string to normalize
@@ -136,5 +177,19 @@ export function normalizeModelString(model: string | undefined | null): string {
return `${PROVIDER_PREFIXES.cursor}${model}`;
}
// For Codex, bare gpt-* and o-series models are valid canonical forms
// Only add prefix if it's in CODEX_MODEL_MAP but doesn't have gpt-/o prefix
const codexModelValues = Object.values(CODEX_MODEL_MAP);
if (codexModelValues.includes(model as CodexModelId)) {
// If it already starts with gpt- or o, it's canonical
if (model.startsWith('gpt-') || /^o\d/.test(model)) {
return model;
}
// Otherwise, it might need a prefix (though this is unlikely)
if (!model.startsWith(PROVIDER_PREFIXES.codex)) {
return `${PROVIDER_PREFIXES.codex}${model}`;
}
}
return model;
}

View File

@@ -3,6 +3,20 @@
*/
import type { ThinkingLevel } from './settings.js';
import type { CodexSandboxMode, CodexApprovalPolicy } from './codex.js';
/**
* Reasoning effort levels for Codex/OpenAI models
* Controls the computational intensity and reasoning tokens used.
* Based on OpenAI API documentation:
* - 'none': No reasoning (GPT-5.1 models only)
* - 'minimal': Very quick reasoning
* - 'low': Quick responses for simpler queries
* - 'medium': Balance between depth and speed (default)
* - 'high': Maximizes reasoning depth for critical tasks
* - 'xhigh': Highest level, supported by gpt-5.1-codex-max and newer
*/
export type ReasoningEffort = 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
/**
* Configuration for a provider instance
@@ -73,6 +87,10 @@ export interface ExecuteOptions {
maxTurns?: number;
allowedTools?: string[];
mcpServers?: Record<string, McpServerConfig>;
/** If true, allows all MCP tools unrestricted (no approval needed). Default: false */
mcpUnrestrictedTools?: boolean;
/** If true, automatically approves all MCP tool calls. Default: undefined (uses approval policy) */
mcpAutoApproveTools?: boolean;
abortController?: AbortController;
conversationHistory?: ConversationMessage[]; // Previous messages for context
sdkSessionId?: string; // Claude SDK session ID for resuming conversations
@@ -89,6 +107,31 @@ export interface ExecuteOptions {
* Only applies to Claude models; Cursor models handle thinking internally.
*/
thinkingLevel?: ThinkingLevel;
/**
* Reasoning effort for Codex/OpenAI models with reasoning capabilities.
* Controls how many reasoning tokens the model generates before responding.
* Supported values: 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh'
* - none: No reasoning tokens (fastest)
* - minimal/low: Quick reasoning for simple tasks
* - medium: Balanced reasoning (default)
* - high: Extended reasoning for complex tasks
* - xhigh: Maximum reasoning for quality-critical tasks
* Only applies to models that support reasoning (gpt-5.1-codex-max+, o3-mini, o4-mini)
*/
reasoningEffort?: ReasoningEffort;
codexSettings?: {
autoLoadAgents?: boolean;
sandboxMode?: CodexSandboxMode;
approvalPolicy?: CodexApprovalPolicy;
enableWebSearch?: boolean;
enableImages?: boolean;
additionalDirs?: string[];
threadId?: string;
};
outputFormat?: {
type: 'json_schema';
schema: Record<string, unknown>;
};
}
/**
@@ -165,4 +208,5 @@ export interface ModelDefinition {
supportsTools?: boolean;
tier?: 'basic' | 'standard' | 'premium';
default?: boolean;
hasReasoning?: boolean;
}

View File

@@ -6,10 +6,11 @@
* (for file I/O via SettingsService) and the UI (for state management and sync).
*/
import type { ModelAlias } from './model.js';
import type { ModelAlias, AgentModel, CodexModelId } from './model.js';
import type { CursorModelId } from './cursor-models.js';
import { CURSOR_MODEL_MAP, getAllCursorModelIds } from './cursor-models.js';
import type { PromptCustomization } from './prompts.js';
import type { CodexSandboxMode, CodexApprovalPolicy } from './codex.js';
// Re-export ModelAlias for convenience
export type { ModelAlias };
@@ -95,7 +96,14 @@ export function getThinkingTokenBudget(level: ThinkingLevel | undefined): number
}
/** ModelProvider - AI model provider for credentials and API key management */
export type ModelProvider = 'claude' | 'cursor';
export type ModelProvider = 'claude' | 'cursor' | 'codex';
const DEFAULT_CODEX_AUTO_LOAD_AGENTS = false;
const DEFAULT_CODEX_SANDBOX_MODE: CodexSandboxMode = 'workspace-write';
const DEFAULT_CODEX_APPROVAL_POLICY: CodexApprovalPolicy = 'on-request';
const DEFAULT_CODEX_ENABLE_WEB_SEARCH = false;
const DEFAULT_CODEX_ENABLE_IMAGES = true;
const DEFAULT_CODEX_ADDITIONAL_DIRS: string[] = [];
/**
* PhaseModelEntry - Configuration for a single phase model
@@ -227,7 +235,7 @@ export interface AIProfile {
name: string;
/** User-friendly description */
description: string;
/** Provider selection: 'claude' or 'cursor' */
/** Provider selection: 'claude', 'cursor', or 'codex' */
provider: ModelProvider;
/** Whether this is a built-in default profile */
isBuiltIn: boolean;
@@ -245,6 +253,10 @@ export interface AIProfile {
* Note: For Cursor, thinking is embedded in the model ID (e.g., 'claude-sonnet-4-thinking')
*/
cursorModel?: CursorModelId;
// Codex-specific settings
/** Which Codex/GPT model to use - only for Codex provider */
codexModel?: CodexModelId;
}
/**
@@ -262,6 +274,12 @@ export function profileHasThinking(profile: AIProfile): boolean {
return modelConfig?.hasThinking ?? false;
}
if (profile.provider === 'codex') {
// Codex models handle thinking internally (o-series models)
const model = profile.codexModel || 'gpt-5.2';
return model.startsWith('o');
}
return false;
}
@@ -273,6 +291,10 @@ export function getProfileModelString(profile: AIProfile): string {
return `cursor:${profile.cursorModel || 'auto'}`;
}
if (profile.provider === 'codex') {
return `codex:${profile.codexModel || 'gpt-5.2'}`;
}
// Claude
return profile.model || 'sonnet';
}
@@ -493,6 +515,22 @@ export interface GlobalSettings {
/** Skip the sandbox environment warning dialog on startup */
skipSandboxWarning?: boolean;
// Codex CLI Settings
/** Auto-load .codex/AGENTS.md instructions into Codex prompts */
codexAutoLoadAgents?: boolean;
/** Sandbox mode for Codex CLI command execution */
codexSandboxMode?: CodexSandboxMode;
/** Approval policy for Codex CLI tool execution */
codexApprovalPolicy?: CodexApprovalPolicy;
/** Enable web search capability for Codex CLI (--search flag) */
codexEnableWebSearch?: boolean;
/** Enable image attachment support for Codex CLI (-i flag) */
codexEnableImages?: boolean;
/** Additional directories with write access (--add-dir flags) */
codexAdditionalDirs?: string[];
/** Last thread ID for session resumption */
codexThreadId?: string;
// MCP Server Configuration
/** List of configured MCP servers for agent use */
mcpServers: MCPServerConfig[];
@@ -691,6 +729,14 @@ export const DEFAULT_GLOBAL_SETTINGS: GlobalSettings = {
worktreePanelCollapsed: false,
lastSelectedSessionByProject: {},
autoLoadClaudeMd: false,
skipSandboxWarning: false,
codexAutoLoadAgents: DEFAULT_CODEX_AUTO_LOAD_AGENTS,
codexSandboxMode: DEFAULT_CODEX_SANDBOX_MODE,
codexApprovalPolicy: DEFAULT_CODEX_APPROVAL_POLICY,
codexEnableWebSearch: DEFAULT_CODEX_ENABLE_WEB_SEARCH,
codexEnableImages: DEFAULT_CODEX_ENABLE_IMAGES,
codexAdditionalDirs: DEFAULT_CODEX_ADDITIONAL_DIRS,
codexThreadId: undefined,
mcpServers: [],
};