diff --git a/apps/ui/src/components/views/project-settings-view/project-bulk-replace-dialog.tsx b/apps/ui/src/components/views/project-settings-view/project-bulk-replace-dialog.tsx index 12881adf..66e2cb0e 100644 --- a/apps/ui/src/components/views/project-settings-view/project-bulk-replace-dialog.tsx +++ b/apps/ui/src/components/views/project-settings-view/project-bulk-replace-dialog.tsx @@ -121,11 +121,12 @@ export function ProjectBulkReplaceDialog({ // Find the model from provider that maps to a specific Claude model const findModelForClaudeAlias = ( provider: ClaudeCompatibleProvider | null, - claudeAlias: ClaudeModelAlias + claudeAlias: ClaudeModelAlias, + phase: PhaseModelKey ): PhaseModelEntry => { if (!provider) { - // Anthropic Direct - use Claude model directly - return { model: claudeAlias }; + // Anthropic Direct - reset to default phase model (includes correct thinking levels) + return DEFAULT_PHASE_MODELS[phase]; } // Find model that maps to this Claude alias @@ -152,7 +153,7 @@ export function ProjectBulkReplaceDialog({ const globalEntry = phaseModels[phase] ?? DEFAULT_PHASE_MODELS[phase]; const currentEntry = projectOverrides[phase] || globalEntry; const claudeAlias = getClaudeModelAlias(currentEntry); - const newEntry = findModelForClaudeAlias(selectedProviderConfig, claudeAlias); + const newEntry = findModelForClaudeAlias(selectedProviderConfig, claudeAlias, phase); // Get display names const getCurrentDisplay = (): string => { @@ -175,7 +176,9 @@ export function ProjectBulkReplaceDialog({ }; const isChanged = - currentEntry.model !== newEntry.model || currentEntry.providerId !== newEntry.providerId; + currentEntry.model !== newEntry.model || + currentEntry.providerId !== newEntry.providerId || + currentEntry.thinkingLevel !== newEntry.thinkingLevel; return { phase, diff --git a/apps/ui/src/components/views/settings-view/model-defaults/bulk-replace-dialog.tsx b/apps/ui/src/components/views/settings-view/model-defaults/bulk-replace-dialog.tsx index 10ac8836..aafd383d 100644 --- a/apps/ui/src/components/views/settings-view/model-defaults/bulk-replace-dialog.tsx +++ b/apps/ui/src/components/views/settings-view/model-defaults/bulk-replace-dialog.tsx @@ -112,11 +112,12 @@ export function BulkReplaceDialog({ open, onOpenChange }: BulkReplaceDialogProps // Find the model from provider that maps to a specific Claude model const findModelForClaudeAlias = ( provider: ClaudeCompatibleProvider | null, - claudeAlias: ClaudeModelAlias + claudeAlias: ClaudeModelAlias, + phase: PhaseModelKey ): PhaseModelEntry => { if (!provider) { - // Anthropic Direct - use Claude model directly - return { model: claudeAlias }; + // Anthropic Direct - reset to default phase model (includes correct thinking levels) + return DEFAULT_PHASE_MODELS[phase]; } // Find model that maps to this Claude alias @@ -141,7 +142,7 @@ export function BulkReplaceDialog({ open, onOpenChange }: BulkReplaceDialogProps return ALL_PHASES.map((phase) => { const currentEntry = phaseModels[phase] ?? DEFAULT_PHASE_MODELS[phase]; const claudeAlias = getClaudeModelAlias(currentEntry); - const newEntry = findModelForClaudeAlias(selectedProviderConfig, claudeAlias); + const newEntry = findModelForClaudeAlias(selectedProviderConfig, claudeAlias, phase); // Get display names const getCurrentDisplay = (): string => { @@ -164,7 +165,9 @@ export function BulkReplaceDialog({ open, onOpenChange }: BulkReplaceDialogProps }; const isChanged = - currentEntry.model !== newEntry.model || currentEntry.providerId !== newEntry.providerId; + currentEntry.model !== newEntry.model || + currentEntry.providerId !== newEntry.providerId || + currentEntry.thinkingLevel !== newEntry.thinkingLevel; return { phase, diff --git a/libs/utils/src/atomic-writer.ts b/libs/utils/src/atomic-writer.ts index fe07e5eb..9fc7ff4a 100644 --- a/libs/utils/src/atomic-writer.ts +++ b/libs/utils/src/atomic-writer.ts @@ -7,6 +7,7 @@ import { secureFs } from '@automaker/platform'; import path from 'path'; +import crypto from 'crypto'; import { createLogger } from './logger.js'; import { mkdirSafe } from './fs-utils.js'; @@ -99,7 +100,9 @@ export async function atomicWriteJson( ): Promise { const { indent = 2, createDirs = false, backupCount = 0 } = options; const resolvedPath = path.resolve(filePath); - const tempPath = `${resolvedPath}.tmp.${Date.now()}`; + // Use timestamp + random suffix to ensure uniqueness even for concurrent writes + const uniqueSuffix = `${Date.now()}.${crypto.randomBytes(4).toString('hex')}`; + const tempPath = `${resolvedPath}.tmp.${uniqueSuffix}`; // Create parent directories if requested if (createDirs) { diff --git a/package-lock.json b/package-lock.json index 64192c40..c86ba4aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6218,6 +6218,7 @@ "version": "19.2.7", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", + "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -6227,7 +6228,7 @@ "version": "19.2.3", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "devOptional": true, + "dev": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.2.0" @@ -8438,6 +8439,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, "license": "MIT" }, "node_modules/d3-color": { @@ -11331,7 +11333,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11353,7 +11354,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11375,7 +11375,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11397,7 +11396,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11419,7 +11417,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11441,7 +11438,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11463,7 +11459,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11485,7 +11480,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11507,7 +11501,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11529,7 +11522,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11551,7 +11543,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 12.0.0" },