Merge remote-tracking branch 'upstream/v0.15.0rc' into refactor/auto-mode-service-gsxdsm

This commit is contained in:
gsxdsm
2026-02-15 10:20:53 -08:00
42 changed files with 1363 additions and 153 deletions

View File

@@ -69,6 +69,29 @@ export function SandboxRiskDialog({ open, onConfirm, onDeny }: SandboxRiskDialog
For safer operation, consider running Automaker in Docker. See the README for
instructions.
</p>
<div className="bg-muted/50 border border-border rounded-lg p-4 space-y-2">
<p className="text-sm font-medium text-foreground">
Already running in Docker? Try these troubleshooting steps:
</p>
<ul className="text-sm text-muted-foreground list-disc list-inside space-y-1">
<li>
Ensure <code className="bg-muted px-1 rounded">IS_CONTAINERIZED=true</code> is
set in your docker-compose environment
</li>
<li>
Verify the server container has the environment variable:{' '}
<code className="bg-muted px-1 rounded">
docker exec automaker-server printenv IS_CONTAINERIZED
</code>
</li>
<li>Rebuild and restart containers if you recently changed the configuration</li>
<li>
Check the server logs for startup messages:{' '}
<code className="bg-muted px-1 rounded">docker-compose logs server</code>
</li>
</ul>
</div>
</div>
</DialogDescription>
</DialogHeader>

View File

@@ -28,7 +28,7 @@ import { cn } from '@/lib/utils';
import { modelSupportsThinking } from '@/lib/utils';
import { useAppStore, ThinkingLevel, FeatureImage, PlanningMode, Feature } from '@/store/app-store';
import type { ReasoningEffort, PhaseModelEntry, AgentModel } from '@automaker/types';
import { supportsReasoningEffort } from '@automaker/types';
import { supportsReasoningEffort, isAdaptiveThinkingModel } from '@automaker/types';
import {
PrioritySelector,
WorkModeSelector,
@@ -264,7 +264,20 @@ export function AddFeatureDialog({
}, [planningMode]);
const handleModelChange = (entry: PhaseModelEntry) => {
setModelEntry(entry);
// Normalize thinking level when switching between adaptive and non-adaptive models
const isNewModelAdaptive =
typeof entry.model === 'string' && isAdaptiveThinkingModel(entry.model);
const currentLevel = entry.thinkingLevel || 'none';
if (isNewModelAdaptive && currentLevel !== 'none' && currentLevel !== 'adaptive') {
// Switching TO Opus 4.6 with a manual level -> auto-switch to 'adaptive'
setModelEntry({ ...entry, thinkingLevel: 'adaptive' });
} else if (!isNewModelAdaptive && currentLevel === 'adaptive') {
// Switching FROM Opus 4.6 with adaptive -> auto-switch to 'high'
setModelEntry({ ...entry, thinkingLevel: 'high' });
} else {
setModelEntry(entry);
}
};
const buildFeatureData = (): FeatureData | null => {

View File

@@ -167,7 +167,14 @@ export const ALL_MODELS: ModelOption[] = [
...COPILOT_MODELS,
];
export const THINKING_LEVELS: ThinkingLevel[] = ['none', 'low', 'medium', 'high', 'ultrathink'];
export const THINKING_LEVELS: ThinkingLevel[] = [
'none',
'low',
'medium',
'high',
'ultrathink',
'adaptive',
];
export const THINKING_LEVEL_LABELS: Record<ThinkingLevel, string> = {
none: 'None',
@@ -175,6 +182,7 @@ export const THINKING_LEVEL_LABELS: Record<ThinkingLevel, string> = {
medium: 'Med',
high: 'High',
ultrathink: 'Ultra',
adaptive: 'Adaptive',
};
/**

View File

@@ -2,19 +2,26 @@ import { Label } from '@/components/ui/label';
import { Brain } from 'lucide-react';
import { cn } from '@/lib/utils';
import { ThinkingLevel } from '@/store/app-store';
import { THINKING_LEVELS, THINKING_LEVEL_LABELS } from './model-constants';
import { THINKING_LEVEL_LABELS } from './model-constants';
import { getThinkingLevelsForModel } from '@automaker/types';
interface ThinkingLevelSelectorProps {
selectedLevel: ThinkingLevel;
onLevelSelect: (level: ThinkingLevel) => void;
testIdPrefix?: string;
/** Model ID is required for correct thinking level filtering.
* Without it, adaptive thinking won't be available for Opus 4.6. */
model?: string;
}
export function ThinkingLevelSelector({
selectedLevel,
onLevelSelect,
testIdPrefix = 'thinking-level',
model,
}: ThinkingLevelSelectorProps) {
const levels = getThinkingLevelsForModel(model || '');
return (
<div className="space-y-2 pt-2 border-t border-border">
<Label className="flex items-center gap-2 text-sm">
@@ -22,7 +29,7 @@ export function ThinkingLevelSelector({
Thinking Level
</Label>
<div className="flex gap-2 flex-wrap">
{THINKING_LEVELS.map((level) => (
{levels.map((level) => (
<button
key={level}
type="button"
@@ -40,7 +47,9 @@ export function ThinkingLevelSelector({
))}
</div>
<p className="text-xs text-muted-foreground">
Higher levels give more time to reason through complex problems.
{levels.includes('adaptive')
? 'Adaptive thinking lets the model decide how much reasoning to use.'
: 'Higher levels give more time to reason through complex problems.'}
</p>
</div>
);

View File

@@ -21,6 +21,7 @@ import {
isGroupSelected,
getSelectedVariant,
codexModelHasThinking,
getThinkingLevelsForModel,
} from '@automaker/types';
import {
CLAUDE_MODELS,
@@ -28,7 +29,6 @@ import {
OPENCODE_MODELS,
GEMINI_MODELS,
COPILOT_MODELS,
THINKING_LEVELS,
THINKING_LEVEL_LABELS,
REASONING_EFFORT_LEVELS,
REASONING_EFFORT_LABELS,
@@ -1296,7 +1296,9 @@ export function PhaseModelSelector({
<div className="px-2 py-1 text-xs font-medium text-muted-foreground">
Thinking Level
</div>
{THINKING_LEVELS.map((level) => (
{getThinkingLevelsForModel(
model.mapsToClaudeModel === 'opus' ? 'claude-opus' : model.id || ''
).map((level) => (
<button
key={level}
onClick={() => {
@@ -1322,6 +1324,7 @@ export function PhaseModelSelector({
{level === 'medium' && 'Moderate reasoning (10k tokens)'}
{level === 'high' && 'Deep reasoning (16k tokens)'}
{level === 'ultrathink' && 'Maximum reasoning (32k tokens)'}
{level === 'adaptive' && 'Model decides reasoning depth'}
</span>
</div>
{isSelected && currentThinking === level && (
@@ -1402,7 +1405,9 @@ export function PhaseModelSelector({
<div className="px-2 py-1.5 text-xs font-medium text-muted-foreground border-b border-border/50 mb-1">
Thinking Level
</div>
{THINKING_LEVELS.map((level) => (
{getThinkingLevelsForModel(
model.mapsToClaudeModel === 'opus' ? 'claude-opus' : model.id || ''
).map((level) => (
<button
key={level}
onClick={() => {
@@ -1428,6 +1433,7 @@ export function PhaseModelSelector({
{level === 'medium' && 'Moderate reasoning (10k tokens)'}
{level === 'high' && 'Deep reasoning (16k tokens)'}
{level === 'ultrathink' && 'Maximum reasoning (32k tokens)'}
{level === 'adaptive' && 'Model decides reasoning depth'}
</span>
</div>
{isSelected && currentThinking === level && (
@@ -1564,7 +1570,7 @@ export function PhaseModelSelector({
<div className="px-2 py-1 text-xs font-medium text-muted-foreground">
Thinking Level
</div>
{THINKING_LEVELS.map((level) => (
{getThinkingLevelsForModel(model.id).map((level) => (
<button
key={level}
onClick={() => {
@@ -1589,6 +1595,7 @@ export function PhaseModelSelector({
{level === 'medium' && 'Moderate reasoning (10k tokens)'}
{level === 'high' && 'Deep reasoning (16k tokens)'}
{level === 'ultrathink' && 'Maximum reasoning (32k tokens)'}
{level === 'adaptive' && 'Model decides reasoning depth'}
</span>
</div>
{isSelected && currentThinking === level && (
@@ -1685,7 +1692,7 @@ export function PhaseModelSelector({
<div className="px-2 py-1.5 text-xs font-medium text-muted-foreground border-b border-border/50 mb-1">
Thinking Level
</div>
{THINKING_LEVELS.map((level) => (
{getThinkingLevelsForModel(model.id).map((level) => (
<button
key={level}
onClick={() => {
@@ -1710,6 +1717,7 @@ export function PhaseModelSelector({
{level === 'medium' && 'Moderate reasoning (10k tokens)'}
{level === 'high' && 'Deep reasoning (16k tokens)'}
{level === 'ultrathink' && 'Maximum reasoning (32k tokens)'}
{level === 'adaptive' && 'Model decides reasoning depth'}
</span>
</div>
{isSelected && currentThinking === level && (

View File

@@ -9,7 +9,7 @@ import {
SelectValue,
} from '@/components/ui/select';
import { cn } from '@/lib/utils';
import type { CodexModelId } from '@automaker/types';
import { supportsReasoningEffort, type CodexModelId } from '@automaker/types';
import { OpenAIIcon } from '@/components/ui/provider-icon';
interface CodexModelConfigurationProps {
@@ -27,25 +27,30 @@ interface CodexModelInfo {
}
const CODEX_MODEL_INFO: Record<CodexModelId, CodexModelInfo> = {
'codex-gpt-5.3-codex': {
id: 'codex-gpt-5.3-codex',
label: 'GPT-5.3-Codex',
description: 'Latest frontier agentic coding model',
},
'codex-gpt-5.2-codex': {
id: 'codex-gpt-5.2-codex',
label: 'GPT-5.2-Codex',
description: 'Most advanced agentic coding model for complex software engineering',
description: 'Frontier agentic coding model',
},
'codex-gpt-5.1-codex-max': {
id: 'codex-gpt-5.1-codex-max',
label: 'GPT-5.1-Codex-Max',
description: 'Optimized for long-horizon, agentic coding tasks in Codex',
description: 'Codex-optimized flagship for deep and fast reasoning',
},
'codex-gpt-5.1-codex-mini': {
id: 'codex-gpt-5.1-codex-mini',
label: 'GPT-5.1-Codex-Mini',
description: 'Smaller, more cost-effective version for faster workflows',
description: 'Optimized for codex. Cheaper, faster, but less capable',
},
'codex-gpt-5.2': {
id: 'codex-gpt-5.2',
label: 'GPT-5.2',
description: 'Best general agentic model for tasks across industries and domains',
description: 'Latest frontier model with improvements across knowledge, reasoning and coding',
},
'codex-gpt-5.1': {
id: 'codex-gpt-5.1',
@@ -157,13 +162,3 @@ export function CodexModelConfiguration({
</div>
);
}
function supportsReasoningEffort(modelId: string): boolean {
const reasoningModels = [
'codex-gpt-5.2-codex',
'codex-gpt-5.1-codex-max',
'codex-gpt-5.2',
'codex-gpt-5.1',
];
return reasoningModels.includes(modelId);
}

View File

@@ -59,6 +59,7 @@ export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps
// CLI Verification state
const [cliVerificationStatus, setCliVerificationStatus] = useState<VerificationStatus>('idle');
const [cliVerificationError, setCliVerificationError] = useState<string | null>(null);
const [cliAuthType, setCliAuthType] = useState<'oauth' | 'cli' | null>(null);
// API Key Verification state
const [apiKeyVerificationStatus, setApiKeyVerificationStatus] =
@@ -119,6 +120,7 @@ export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps
const verifyCliAuth = useCallback(async () => {
setCliVerificationStatus('verifying');
setCliVerificationError(null);
setCliAuthType(null);
try {
const api = getElectronAPI();
@@ -138,12 +140,21 @@ export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps
if (result.authenticated && !hasLimitReachedError) {
setCliVerificationStatus('verified');
// Store the auth type for displaying specific success message
const authType = result.authType === 'oauth' ? 'oauth' : 'cli';
setCliAuthType(authType);
setClaudeAuthStatus({
authenticated: true,
method: 'cli_authenticated',
method: authType === 'oauth' ? 'oauth_token' : 'cli_authenticated',
hasCredentialsFile: claudeAuthStatus?.hasCredentialsFile || false,
oauthTokenValid: authType === 'oauth',
});
toast.success('Claude CLI authentication verified!');
// Show specific success message based on auth type
if (authType === 'oauth') {
toast.success('Claude Code subscription detected and verified!');
} else {
toast.success('Claude CLI authentication verified!');
}
} else {
setCliVerificationStatus('error');
setCliVerificationError(
@@ -436,9 +447,15 @@ export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps
<div className="flex items-center gap-3 p-4 rounded-lg bg-green-500/10 border border-green-500/20">
<CheckCircle2 className="w-5 h-5 text-green-500" />
<div>
<p className="font-medium text-foreground">CLI Authentication verified!</p>
<p className="font-medium text-foreground">
{cliAuthType === 'oauth'
? 'Claude Code subscription verified!'
: 'CLI Authentication verified!'}
</p>
<p className="text-sm text-muted-foreground">
Your Claude CLI is working correctly.
{cliAuthType === 'oauth'
? 'Your Claude Code subscription is active and ready to use.'
: 'Your Claude CLI is working correctly.'}
</p>
</div>
</div>

View File

@@ -27,18 +27,20 @@ export interface AgentTaskInfo {
/**
* Default model used by the feature executor
*/
export const DEFAULT_MODEL = 'claude-opus-4-5-20251101';
export const DEFAULT_MODEL = 'claude-opus-4-6';
/**
* Formats a model name for display
*/
export function formatModelName(model: string): string {
// Claude models
if (model.includes('opus-4-6') || model === 'claude-opus') return 'Opus 4.6';
if (model.includes('opus')) return 'Opus 4.5';
if (model.includes('sonnet')) return 'Sonnet 4.5';
if (model.includes('haiku')) return 'Haiku 4.5';
// Codex/GPT models - specific formatting
if (model === 'codex-gpt-5.3-codex') return 'GPT-5.3 Codex';
if (model === 'codex-gpt-5.2-codex') return 'GPT-5.2 Codex';
if (model === 'codex-gpt-5.2') return 'GPT-5.2';
if (model === 'codex-gpt-5.1-codex-max') return 'GPT-5.1 Max';

View File

@@ -1442,6 +1442,7 @@ interface SetupAPI {
verifyClaudeAuth: (authMethod?: 'cli' | 'api_key') => Promise<{
success: boolean;
authenticated: boolean;
authType?: 'oauth' | 'api_key' | 'cli';
error?: string;
}>;
getGhStatus?: () => Promise<{

View File

@@ -1350,6 +1350,7 @@ export class HttpApiClient implements ElectronAPI {
): Promise<{
success: boolean;
authenticated: boolean;
authType?: 'oauth' | 'api_key' | 'cli';
error?: string;
}> => this.post('/api/setup/verify-claude-auth', { authMethod, apiKey }),