2
0
Эх сурвалжийг харах

Revert "Improve provider profile management in the external API (#3386)" (#3440)

Matt Rubens 7 сар өмнө
parent
commit
730d037a12
62 өөрчлөгдсөн 1001 нэмэгдсэн , 1489 устгасан
  1. 5 0
      .changeset/lazy-bottles-travel.md
  2. 0 1
      .gitignore
  3. 1 1
      e2e/package-lock.json
  4. 1 1
      e2e/package.json
  5. 1 0
      evals/config/eslint/package.json
  6. 1 1
      evals/package.json
  7. 46 245
      evals/packages/types/src/roo-code.ts
  8. 43 30
      evals/pnpm-lock.yaml
  9. 0 0
      git
  10. 2 2
      package-lock.json
  11. 2 2
      package.json
  12. 2 2
      src/api/index.ts
  13. 31 57
      src/core/config/ProviderSettingsManager.ts
  14. 54 69
      src/core/config/__tests__/ProviderSettingsManager.test.ts
  15. 3 3
      src/core/task/Task.ts
  16. 2 2
      src/core/task/__tests__/Task.test.ts
  17. 61 127
      src/core/webview/ClineProvider.ts
  18. 41 40
      src/core/webview/__tests__/ClineProvider.test.ts
  19. 99 46
      src/core/webview/webviewMessageHandler.ts
  20. 86 107
      src/exports/api.ts
  21. 16 61
      src/exports/interface.ts
  22. 153 210
      src/exports/roo-code.d.ts
  23. 141 173
      src/exports/types.ts
  24. 49 148
      src/schemas/index.ts
  25. 5 5
      src/shared/ExtensionMessage.ts
  26. 3 2
      src/shared/WebviewMessage.ts
  27. 6 6
      src/shared/__tests__/checkExistApiConfig.test.ts
  28. 3 1
      src/shared/api.ts
  29. 4 4
      src/utils/__tests__/enhance-prompt.test.ts
  30. 2 2
      src/utils/single-completion-handler.ts
  31. 2 2
      webview-ui/src/components/chat/__tests__/TaskHeader.test.tsx
  32. 2 2
      webview-ui/src/components/settings/ApiConfigManager.tsx
  33. 5 5
      webview-ui/src/components/settings/ApiOptions.tsx
  34. 3 3
      webview-ui/src/components/settings/PromptCachingControl.tsx
  35. 3 3
      webview-ui/src/components/settings/ReasoningEffort.tsx
  36. 2 2
      webview-ui/src/components/settings/SettingsView.tsx
  37. 3 3
      webview-ui/src/components/settings/ThinkingBudget.tsx
  38. 2 2
      webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx
  39. 5 5
      webview-ui/src/components/settings/providers/Anthropic.tsx
  40. 5 5
      webview-ui/src/components/settings/providers/Bedrock.tsx
  41. 3 3
      webview-ui/src/components/settings/providers/BedrockCustomArn.tsx
  42. 5 5
      webview-ui/src/components/settings/providers/Chutes.tsx
  43. 5 5
      webview-ui/src/components/settings/providers/DeepSeek.tsx
  44. 5 5
      webview-ui/src/components/settings/providers/Gemini.tsx
  45. 5 5
      webview-ui/src/components/settings/providers/Glama.tsx
  46. 5 5
      webview-ui/src/components/settings/providers/Groq.tsx
  47. 5 5
      webview-ui/src/components/settings/providers/LMStudio.tsx
  48. 5 6
      webview-ui/src/components/settings/providers/LiteLLM.tsx
  49. 5 5
      webview-ui/src/components/settings/providers/Mistral.tsx
  50. 5 5
      webview-ui/src/components/settings/providers/Ollama.tsx
  51. 5 5
      webview-ui/src/components/settings/providers/OpenAI.tsx
  52. 5 5
      webview-ui/src/components/settings/providers/OpenAICompatible.tsx
  53. 5 5
      webview-ui/src/components/settings/providers/OpenRouter.tsx
  54. 5 5
      webview-ui/src/components/settings/providers/Requesty.tsx
  55. 5 5
      webview-ui/src/components/settings/providers/Unbound.tsx
  56. 5 5
      webview-ui/src/components/settings/providers/VSCodeLM.tsx
  57. 5 5
      webview-ui/src/components/settings/providers/Vertex.tsx
  58. 5 5
      webview-ui/src/components/settings/providers/XAI.tsx
  59. 3 3
      webview-ui/src/components/ui/hooks/useSelectedModel.ts
  60. 7 9
      webview-ui/src/context/ExtensionStateContext.tsx
  61. 5 5
      webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx
  62. 3 3
      webview-ui/src/utils/validate.ts

+ 5 - 0
.changeset/lazy-bottles-travel.md

@@ -0,0 +1,5 @@
+---
+"roo-cline": patch
+---
+
+Revert profile management changes until we track down a bug with defaults

+ 0 - 1
.gitignore

@@ -4,7 +4,6 @@ out
 out-*
 out-*
 node_modules
 node_modules
 coverage/
 coverage/
-mock/
 
 
 .DS_Store
 .DS_Store
 
 

+ 1 - 1
e2e/package-lock.json

@@ -12,7 +12,7 @@
 				"@vscode/test-cli": "^0.0.10",
 				"@vscode/test-cli": "^0.0.10",
 				"@vscode/test-electron": "^2.4.0",
 				"@vscode/test-electron": "^2.4.0",
 				"mocha": "^11.1.0",
 				"mocha": "^11.1.0",
-				"typescript": "5.8.3"
+				"typescript": "^5.4.5"
 			}
 			}
 		},
 		},
 		"node_modules/@bcoe/v8-coverage": {
 		"node_modules/@bcoe/v8-coverage": {

+ 1 - 1
e2e/package.json

@@ -16,6 +16,6 @@
 		"@vscode/test-cli": "^0.0.10",
 		"@vscode/test-cli": "^0.0.10",
 		"@vscode/test-electron": "^2.4.0",
 		"@vscode/test-electron": "^2.4.0",
 		"mocha": "^11.1.0",
 		"mocha": "^11.1.0",
-		"typescript": "5.8.3"
+		"typescript": "^5.4.5"
 	}
 	}
 }
 }

+ 1 - 0
evals/config/eslint/package.json

@@ -16,6 +16,7 @@
 		"eslint-plugin-react-hooks": "^5.2.0",
 		"eslint-plugin-react-hooks": "^5.2.0",
 		"eslint-plugin-turbo": "^2.4.4",
 		"eslint-plugin-turbo": "^2.4.4",
 		"globals": "^16.0.0",
 		"globals": "^16.0.0",
+		"typescript": "^5",
 		"typescript-eslint": "^8.26.0"
 		"typescript-eslint": "^8.26.0"
 	}
 	}
 }
 }

+ 1 - 1
evals/package.json

@@ -20,7 +20,7 @@
 		"prettier": "^3.5.3",
 		"prettier": "^3.5.3",
 		"tsx": "^4.19.4",
 		"tsx": "^4.19.4",
 		"turbo": "^2.5.2",
 		"turbo": "^2.5.2",
-		"typescript": "5.8.3",
+		"typescript": "^5.8.3",
 		"typescript-eslint": "^8.31.1"
 		"typescript-eslint": "^8.31.1"
 	}
 	}
 }
 }

+ 46 - 245
evals/packages/types/src/roo-code.ts

@@ -128,6 +128,18 @@ export const modelInfoSchema = z.object({
 
 
 export type ModelInfo = z.infer<typeof modelInfoSchema>
 export type ModelInfo = z.infer<typeof modelInfoSchema>
 
 
+/**
+ * ApiConfigMeta
+ */
+
+export const apiConfigMetaSchema = z.object({
+	id: z.string(),
+	name: z.string(),
+	apiProvider: providerNamesSchema.optional(),
+})
+
+export type ApiConfigMeta = z.infer<typeof apiConfigMetaSchema>
+
 /**
 /**
  * HistoryItem
  * HistoryItem
  */
  */
@@ -317,56 +329,27 @@ export type Experiments = z.infer<typeof experimentsSchema>
 // eslint-disable-next-line @typescript-eslint/no-unused-vars
 // eslint-disable-next-line @typescript-eslint/no-unused-vars
 type _AssertExperiments = AssertEqual<Equals<ExperimentId, Keys<Experiments>>>
 type _AssertExperiments = AssertEqual<Equals<ExperimentId, Keys<Experiments>>>
 
 
-/**
- * ProviderSettingsEntry
- */
-
-export const providerSettingsEntrySchema = z.object({
-	id: z.string(),
-	name: z.string(),
-	apiProvider: providerNamesSchema.optional(),
-})
-
-export type ProviderSettingsEntry = z.infer<typeof providerSettingsEntrySchema>
-
 /**
 /**
  * ProviderSettings
  * ProviderSettings
  */
  */
 
 
-const genericProviderSettingsSchema = z.object({
-	includeMaxTokens: z.boolean().optional(),
-	reasoningEffort: reasoningEffortsSchema.optional(),
-	promptCachingDisabled: z.boolean().optional(),
-	diffEnabled: z.boolean().optional(),
-	fuzzyMatchThreshold: z.number().optional(),
-	modelTemperature: z.number().nullish(),
-	rateLimitSeconds: z.number().optional(),
-	// Claude 3.7 Sonnet Thinking
-	modelMaxTokens: z.number().optional(),
-	modelMaxThinkingTokens: z.number().optional(),
-})
-
-const anthropicSchema = z.object({
+export const providerSettingsSchema = z.object({
+	apiProvider: providerNamesSchema.optional(),
+	// Anthropic
 	apiModelId: z.string().optional(),
 	apiModelId: z.string().optional(),
 	apiKey: z.string().optional(),
 	apiKey: z.string().optional(),
 	anthropicBaseUrl: z.string().optional(),
 	anthropicBaseUrl: z.string().optional(),
 	anthropicUseAuthToken: z.boolean().optional(),
 	anthropicUseAuthToken: z.boolean().optional(),
-})
-
-const glamaSchema = z.object({
+	// Glama
 	glamaModelId: z.string().optional(),
 	glamaModelId: z.string().optional(),
 	glamaApiKey: z.string().optional(),
 	glamaApiKey: z.string().optional(),
-})
-
-const openRouterSchema = z.object({
+	// OpenRouter
 	openRouterApiKey: z.string().optional(),
 	openRouterApiKey: z.string().optional(),
 	openRouterModelId: z.string().optional(),
 	openRouterModelId: z.string().optional(),
 	openRouterBaseUrl: z.string().optional(),
 	openRouterBaseUrl: z.string().optional(),
 	openRouterSpecificProvider: z.string().optional(),
 	openRouterSpecificProvider: z.string().optional(),
 	openRouterUseMiddleOutTransform: z.boolean().optional(),
 	openRouterUseMiddleOutTransform: z.boolean().optional(),
-})
-
-const bedrockSchema = z.object({
+	// Amazon Bedrock
 	awsAccessKey: z.string().optional(),
 	awsAccessKey: z.string().optional(),
 	awsSecretKey: z.string().optional(),
 	awsSecretKey: z.string().optional(),
 	awsSessionToken: z.string().optional(),
 	awsSessionToken: z.string().optional(),
@@ -376,18 +359,15 @@ const bedrockSchema = z.object({
 	awsProfile: z.string().optional(),
 	awsProfile: z.string().optional(),
 	awsUseProfile: z.boolean().optional(),
 	awsUseProfile: z.boolean().optional(),
 	awsCustomArn: z.string().optional(),
 	awsCustomArn: z.string().optional(),
-})
-
-const vertexSchema = z.object({
+	// Google Vertex
 	vertexKeyFile: z.string().optional(),
 	vertexKeyFile: z.string().optional(),
 	vertexJsonCredentials: z.string().optional(),
 	vertexJsonCredentials: z.string().optional(),
 	vertexProjectId: z.string().optional(),
 	vertexProjectId: z.string().optional(),
 	vertexRegion: z.string().optional(),
 	vertexRegion: z.string().optional(),
-})
-
-const openAiSchema = z.object({
+	// OpenAI
 	openAiBaseUrl: z.string().optional(),
 	openAiBaseUrl: z.string().optional(),
 	openAiApiKey: z.string().optional(),
 	openAiApiKey: z.string().optional(),
+	openAiHostHeader: z.string().optional(),
 	openAiLegacyFormat: z.boolean().optional(),
 	openAiLegacyFormat: z.boolean().optional(),
 	openAiR1FormatEnabled: z.boolean().optional(),
 	openAiR1FormatEnabled: z.boolean().optional(),
 	openAiModelId: z.string().optional(),
 	openAiModelId: z.string().optional(),
@@ -396,16 +376,10 @@ const openAiSchema = z.object({
 	azureApiVersion: z.string().optional(),
 	azureApiVersion: z.string().optional(),
 	openAiStreamingEnabled: z.boolean().optional(),
 	openAiStreamingEnabled: z.boolean().optional(),
 	enableReasoningEffort: z.boolean().optional(),
 	enableReasoningEffort: z.boolean().optional(),
-	openAiHostHeader: z.string().optional(), // Keep temporarily for backward compatibility during migration.
-	openAiHeaders: z.record(z.string(), z.string()).optional(),
-})
-
-const ollamaSchema = z.object({
+	// Ollama
 	ollamaModelId: z.string().optional(),
 	ollamaModelId: z.string().optional(),
 	ollamaBaseUrl: z.string().optional(),
 	ollamaBaseUrl: z.string().optional(),
-})
-
-const vsCodeLmSchema = z.object({
+	// VS Code LM
 	vsCodeLmModelSelector: z
 	vsCodeLmModelSelector: z
 		.object({
 		.object({
 			vendor: z.string().optional(),
 			vendor: z.string().optional(),
@@ -414,208 +388,44 @@ const vsCodeLmSchema = z.object({
 			id: z.string().optional(),
 			id: z.string().optional(),
 		})
 		})
 		.optional(),
 		.optional(),
-})
-
-const lmStudioSchema = z.object({
+	// LM Studio
 	lmStudioModelId: z.string().optional(),
 	lmStudioModelId: z.string().optional(),
 	lmStudioBaseUrl: z.string().optional(),
 	lmStudioBaseUrl: z.string().optional(),
 	lmStudioDraftModelId: z.string().optional(),
 	lmStudioDraftModelId: z.string().optional(),
 	lmStudioSpeculativeDecodingEnabled: z.boolean().optional(),
 	lmStudioSpeculativeDecodingEnabled: z.boolean().optional(),
-})
-
-const geminiSchema = z.object({
+	// Gemini
 	geminiApiKey: z.string().optional(),
 	geminiApiKey: z.string().optional(),
 	googleGeminiBaseUrl: z.string().optional(),
 	googleGeminiBaseUrl: z.string().optional(),
-})
-
-const openAiNativeSchema = z.object({
+	// OpenAI Native
 	openAiNativeApiKey: z.string().optional(),
 	openAiNativeApiKey: z.string().optional(),
 	openAiNativeBaseUrl: z.string().optional(),
 	openAiNativeBaseUrl: z.string().optional(),
-})
-
-const mistralSchema = z.object({
+	// Mistral
 	mistralApiKey: z.string().optional(),
 	mistralApiKey: z.string().optional(),
 	mistralCodestralUrl: z.string().optional(),
 	mistralCodestralUrl: z.string().optional(),
-})
-
-const deepSeekSchema = z.object({
+	// DeepSeek
 	deepSeekBaseUrl: z.string().optional(),
 	deepSeekBaseUrl: z.string().optional(),
 	deepSeekApiKey: z.string().optional(),
 	deepSeekApiKey: z.string().optional(),
-})
-
-const unboundSchema = z.object({
+	// Unbound
 	unboundApiKey: z.string().optional(),
 	unboundApiKey: z.string().optional(),
 	unboundModelId: z.string().optional(),
 	unboundModelId: z.string().optional(),
-})
-
-const requestySchema = z.object({
+	// Requesty
 	requestyApiKey: z.string().optional(),
 	requestyApiKey: z.string().optional(),
 	requestyModelId: z.string().optional(),
 	requestyModelId: z.string().optional(),
-})
-
-const humanRelaySchema = z.object({})
-
-const fakeAiSchema = z.object({
-	fakeAi: z.unknown().optional(),
-})
-
-const xaiSchema = z.object({
+	// X.AI (Grok)
 	xaiApiKey: z.string().optional(),
 	xaiApiKey: z.string().optional(),
-})
-
-const groqSchema = z.object({
-	groqApiKey: z.string().optional(),
-})
-
-const chutesSchema = z.object({
-	chutesApiKey: z.string().optional(),
-})
-
-const litellmSchema = z.object({
-	litellmBaseUrl: z.string().optional(),
-	litellmApiKey: z.string().optional(),
-	litellmModelId: z.string().optional(),
-})
-
-const defaultSchema = z.object({
-	apiProvider: z.undefined(),
-})
-
-export const providerSettingsSchemaDiscriminated = z
-	.discriminatedUnion("apiProvider", [
-		anthropicSchema.merge(
-			z.object({
-				apiProvider: z.literal("anthropic"),
-			}),
-		),
-		glamaSchema.merge(
-			z.object({
-				apiProvider: z.literal("glama"),
-			}),
-		),
-		openRouterSchema.merge(
-			z.object({
-				apiProvider: z.literal("openrouter"),
-			}),
-		),
-		bedrockSchema.merge(
-			z.object({
-				apiProvider: z.literal("bedrock"),
-			}),
-		),
-		vertexSchema.merge(
-			z.object({
-				apiProvider: z.literal("vertex"),
-			}),
-		),
-		openAiSchema.merge(
-			z.object({
-				apiProvider: z.literal("openai"),
-			}),
-		),
-		ollamaSchema.merge(
-			z.object({
-				apiProvider: z.literal("ollama"),
-			}),
-		),
-		vsCodeLmSchema.merge(
-			z.object({
-				apiProvider: z.literal("vscode-lm"),
-			}),
-		),
-		lmStudioSchema.merge(
-			z.object({
-				apiProvider: z.literal("lmstudio"),
-			}),
-		),
-		geminiSchema.merge(
-			z.object({
-				apiProvider: z.literal("gemini"),
-			}),
-		),
-		openAiNativeSchema.merge(
-			z.object({
-				apiProvider: z.literal("openai-native"),
-			}),
-		),
-		mistralSchema.merge(
-			z.object({
-				apiProvider: z.literal("mistral"),
-			}),
-		),
-		deepSeekSchema.merge(
-			z.object({
-				apiProvider: z.literal("deepseek"),
-			}),
-		),
-		unboundSchema.merge(
-			z.object({
-				apiProvider: z.literal("unbound"),
-			}),
-		),
-		requestySchema.merge(
-			z.object({
-				apiProvider: z.literal("requesty"),
-			}),
-		),
-		humanRelaySchema.merge(
-			z.object({
-				apiProvider: z.literal("human-relay"),
-			}),
-		),
-		fakeAiSchema.merge(
-			z.object({
-				apiProvider: z.literal("fake-ai"),
-			}),
-		),
-		xaiSchema.merge(
-			z.object({
-				apiProvider: z.literal("xai"),
-			}),
-		),
-		groqSchema.merge(
-			z.object({
-				apiProvider: z.literal("groq"),
-			}),
-		),
-		chutesSchema.merge(
-			z.object({
-				apiProvider: z.literal("chutes"),
-			}),
-		),
-		litellmSchema.merge(
-			z.object({
-				apiProvider: z.literal("litellm"),
-			}),
-		),
-		defaultSchema,
-	])
-	.and(genericProviderSettingsSchema)
-
-export const providerSettingsSchema = z.object({
-	apiProvider: providerNamesSchema.optional(),
-	...anthropicSchema.shape,
-	...glamaSchema.shape,
-	...openRouterSchema.shape,
-	...bedrockSchema.shape,
-	...vertexSchema.shape,
-	...openAiSchema.shape,
-	...ollamaSchema.shape,
-	...vsCodeLmSchema.shape,
-	...lmStudioSchema.shape,
-	...geminiSchema.shape,
-	...openAiNativeSchema.shape,
-	...mistralSchema.shape,
-	...deepSeekSchema.shape,
-	...unboundSchema.shape,
-	...requestySchema.shape,
-	...humanRelaySchema.shape,
-	...fakeAiSchema.shape,
-	...xaiSchema.shape,
-	...groqSchema.shape,
-	...chutesSchema.shape,
-	...litellmSchema.shape,
-	...genericProviderSettingsSchema.shape,
+	// Claude 3.7 Sonnet Thinking
+	modelMaxTokens: z.number().optional(),
+	modelMaxThinkingTokens: z.number().optional(),
+	// Generic
+	includeMaxTokens: z.boolean().optional(),
+	reasoningEffort: reasoningEffortsSchema.optional(),
+	promptCachingDisabled: z.boolean().optional(),
+	diffEnabled: z.boolean().optional(),
+	fuzzyMatchThreshold: z.number().optional(),
+	modelTemperature: z.number().nullish(),
+	rateLimitSeconds: z.number().optional(),
+	// Fake AI
+	fakeAi: z.unknown().optional(),
 })
 })
 
 
 export type ProviderSettings = z.infer<typeof providerSettingsSchema>
 export type ProviderSettings = z.infer<typeof providerSettingsSchema>
@@ -656,6 +466,7 @@ const providerSettingsRecord: ProviderSettingsRecord = {
 	// OpenAI
 	// OpenAI
 	openAiBaseUrl: undefined,
 	openAiBaseUrl: undefined,
 	openAiApiKey: undefined,
 	openAiApiKey: undefined,
+	openAiHostHeader: undefined,
 	openAiLegacyFormat: undefined,
 	openAiLegacyFormat: undefined,
 	openAiR1FormatEnabled: undefined,
 	openAiR1FormatEnabled: undefined,
 	openAiModelId: undefined,
 	openAiModelId: undefined,
@@ -664,8 +475,6 @@ const providerSettingsRecord: ProviderSettingsRecord = {
 	azureApiVersion: undefined,
 	azureApiVersion: undefined,
 	openAiStreamingEnabled: undefined,
 	openAiStreamingEnabled: undefined,
 	enableReasoningEffort: undefined,
 	enableReasoningEffort: undefined,
-	openAiHostHeader: undefined, // Keep temporarily for backward compatibility during migration
-	openAiHeaders: undefined,
 	// Ollama
 	// Ollama
 	ollamaModelId: undefined,
 	ollamaModelId: undefined,
 	ollamaBaseUrl: undefined,
 	ollamaBaseUrl: undefined,
@@ -708,14 +517,6 @@ const providerSettingsRecord: ProviderSettingsRecord = {
 	fakeAi: undefined,
 	fakeAi: undefined,
 	// X.AI (Grok)
 	// X.AI (Grok)
 	xaiApiKey: undefined,
 	xaiApiKey: undefined,
-	// Groq
-	groqApiKey: undefined,
-	// Chutes AI
-	chutesApiKey: undefined,
-	// LiteLLM
-	litellmBaseUrl: undefined,
-	litellmApiKey: undefined,
-	litellmModelId: undefined,
 }
 }
 
 
 export const PROVIDER_SETTINGS_KEYS = Object.keys(providerSettingsRecord) as Keys<ProviderSettings>[]
 export const PROVIDER_SETTINGS_KEYS = Object.keys(providerSettingsRecord) as Keys<ProviderSettings>[]
@@ -726,7 +527,7 @@ export const PROVIDER_SETTINGS_KEYS = Object.keys(providerSettingsRecord) as Key
 
 
 export const globalSettingsSchema = z.object({
 export const globalSettingsSchema = z.object({
 	currentApiConfigName: z.string().optional(),
 	currentApiConfigName: z.string().optional(),
-	listApiConfigMeta: z.array(providerSettingsEntrySchema).optional(),
+	listApiConfigMeta: z.array(apiConfigMetaSchema).optional(),
 	pinnedApiConfigs: z.record(z.string(), z.boolean()).optional(),
 	pinnedApiConfigs: z.record(z.string(), z.boolean()).optional(),
 
 
 	lastShownAnnouncementId: z.string().optional(),
 	lastShownAnnouncementId: z.string().optional(),

+ 43 - 30
evals/pnpm-lock.yaml

@@ -30,7 +30,7 @@ importers:
         specifier: ^2.5.2
         specifier: ^2.5.2
         version: 2.5.3
         version: 2.5.3
       typescript:
       typescript:
-        specifier: 5.8.3
+        specifier: ^5.8.3
         version: 5.8.3
         version: 5.8.3
       typescript-eslint:
       typescript-eslint:
         specifier: ^8.31.1
         specifier: ^8.31.1
@@ -235,9 +235,12 @@ importers:
       globals:
       globals:
         specifier: ^16.0.0
         specifier: ^16.0.0
         version: 16.0.0
         version: 16.0.0
+      typescript:
+        specifier: ^5
+        version: 5.8.2
       typescript-eslint:
       typescript-eslint:
         specifier: ^8.26.0
         specifier: ^8.26.0
-        version: 8.26.1([email protected]([email protected]))([email protected])
+        version: 8.26.1([email protected]([email protected]))([email protected].2)
 
 
   config/typescript: {}
   config/typescript: {}
 
 
@@ -3367,7 +3370,6 @@ packages:
 
 
   [email protected]:
   [email protected]:
     resolution: {integrity: sha512-+OopMI1wM/NvAJTHf3O3+beHd1YfKLnSVsOGBl3/7UBDZ4ydVadkbBk5Hjjs9d3ALC5rBaftMY59AvwyC8MzPw==}
     resolution: {integrity: sha512-+OopMI1wM/NvAJTHf3O3+beHd1YfKLnSVsOGBl3/7UBDZ4ydVadkbBk5Hjjs9d3ALC5rBaftMY59AvwyC8MzPw==}
-    cpu: [x64, arm64, wasm32]
     os: [darwin, linux, win32]
     os: [darwin, linux, win32]
 
 
   [email protected]:
   [email protected]:
@@ -4374,6 +4376,11 @@ packages:
       eslint: ^8.57.0 || ^9.0.0
       eslint: ^8.57.0 || ^9.0.0
       typescript: '>=4.8.4 <5.9.0'
       typescript: '>=4.8.4 <5.9.0'
 
 
+  [email protected]:
+    resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==}
+    engines: {node: '>=14.17'}
+    hasBin: true
+
   [email protected]:
   [email protected]:
     resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
     resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==}
     engines: {node: '>=14.17'}
     engines: {node: '>=14.17'}
@@ -6029,20 +6036,20 @@ snapshots:
     dependencies:
     dependencies:
       '@types/node': 20.17.24
       '@types/node': 20.17.24
 
 
-  '@typescript-eslint/[email protected](@typescript-eslint/[email protected]([email protected]([email protected]))([email protected].3))([email protected]([email protected]))([email protected])':
+  '@typescript-eslint/[email protected](@typescript-eslint/[email protected]([email protected]([email protected]))([email protected].2))([email protected]([email protected]))([email protected])':
     dependencies:
     dependencies:
       '@eslint-community/regexpp': 4.12.1
       '@eslint-community/regexpp': 4.12.1
-      '@typescript-eslint/parser': 8.26.1([email protected]([email protected]))([email protected].3)
+      '@typescript-eslint/parser': 8.26.1([email protected]([email protected]))([email protected].2)
       '@typescript-eslint/scope-manager': 8.26.1
       '@typescript-eslint/scope-manager': 8.26.1
-      '@typescript-eslint/type-utils': 8.26.1([email protected]([email protected]))([email protected].3)
-      '@typescript-eslint/utils': 8.26.1([email protected]([email protected]))([email protected].3)
+      '@typescript-eslint/type-utils': 8.26.1([email protected]([email protected]))([email protected].2)
+      '@typescript-eslint/utils': 8.26.1([email protected]([email protected]))([email protected].2)
       '@typescript-eslint/visitor-keys': 8.26.1
       '@typescript-eslint/visitor-keys': 8.26.1
       eslint: 9.22.0([email protected])
       eslint: 9.22.0([email protected])
       graphemer: 1.4.0
       graphemer: 1.4.0
       ignore: 5.3.2
       ignore: 5.3.2
       natural-compare: 1.4.0
       natural-compare: 1.4.0
-      ts-api-utils: 2.0.1([email protected].3)
-      typescript: 5.8.3
+      ts-api-utils: 2.0.1([email protected].2)
+      typescript: 5.8.2
     transitivePeerDependencies:
     transitivePeerDependencies:
       - supports-color
       - supports-color
 
 
@@ -6063,15 +6070,15 @@ snapshots:
     transitivePeerDependencies:
     transitivePeerDependencies:
       - supports-color
       - supports-color
 
 
-  '@typescript-eslint/[email protected]([email protected]([email protected]))([email protected].3)':
+  '@typescript-eslint/[email protected]([email protected]([email protected]))([email protected].2)':
     dependencies:
     dependencies:
       '@typescript-eslint/scope-manager': 8.26.1
       '@typescript-eslint/scope-manager': 8.26.1
       '@typescript-eslint/types': 8.26.1
       '@typescript-eslint/types': 8.26.1
-      '@typescript-eslint/typescript-estree': 8.26.1([email protected].3)
+      '@typescript-eslint/typescript-estree': 8.26.1([email protected].2)
       '@typescript-eslint/visitor-keys': 8.26.1
       '@typescript-eslint/visitor-keys': 8.26.1
       debug: 4.4.0
       debug: 4.4.0
       eslint: 9.22.0([email protected])
       eslint: 9.22.0([email protected])
-      typescript: 5.8.3
+      typescript: 5.8.2
     transitivePeerDependencies:
     transitivePeerDependencies:
       - supports-color
       - supports-color
 
 
@@ -6097,14 +6104,14 @@ snapshots:
       '@typescript-eslint/types': 8.32.0
       '@typescript-eslint/types': 8.32.0
       '@typescript-eslint/visitor-keys': 8.32.0
       '@typescript-eslint/visitor-keys': 8.32.0
 
 
-  '@typescript-eslint/[email protected]([email protected]([email protected]))([email protected].3)':
+  '@typescript-eslint/[email protected]([email protected]([email protected]))([email protected].2)':
     dependencies:
     dependencies:
-      '@typescript-eslint/typescript-estree': 8.26.1([email protected].3)
-      '@typescript-eslint/utils': 8.26.1([email protected]([email protected]))([email protected].3)
+      '@typescript-eslint/typescript-estree': 8.26.1([email protected].2)
+      '@typescript-eslint/utils': 8.26.1([email protected]([email protected]))([email protected].2)
       debug: 4.4.0
       debug: 4.4.0
       eslint: 9.22.0([email protected])
       eslint: 9.22.0([email protected])
-      ts-api-utils: 2.0.1([email protected].3)
-      typescript: 5.8.3
+      ts-api-utils: 2.0.1([email protected].2)
+      typescript: 5.8.2
     transitivePeerDependencies:
     transitivePeerDependencies:
       - supports-color
       - supports-color
 
 
@@ -6123,7 +6130,7 @@ snapshots:
 
 
   '@typescript-eslint/[email protected]': {}
   '@typescript-eslint/[email protected]': {}
 
 
-  '@typescript-eslint/[email protected]([email protected].3)':
+  '@typescript-eslint/[email protected]([email protected].2)':
     dependencies:
     dependencies:
       '@typescript-eslint/types': 8.26.1
       '@typescript-eslint/types': 8.26.1
       '@typescript-eslint/visitor-keys': 8.26.1
       '@typescript-eslint/visitor-keys': 8.26.1
@@ -6132,8 +6139,8 @@ snapshots:
       is-glob: 4.0.3
       is-glob: 4.0.3
       minimatch: 9.0.5
       minimatch: 9.0.5
       semver: 7.7.1
       semver: 7.7.1
-      ts-api-utils: 2.1.0([email protected].3)
-      typescript: 5.8.3
+      ts-api-utils: 2.1.0([email protected].2)
+      typescript: 5.8.2
     transitivePeerDependencies:
     transitivePeerDependencies:
       - supports-color
       - supports-color
 
 
@@ -6151,14 +6158,14 @@ snapshots:
     transitivePeerDependencies:
     transitivePeerDependencies:
       - supports-color
       - supports-color
 
 
-  '@typescript-eslint/[email protected]([email protected]([email protected]))([email protected].3)':
+  '@typescript-eslint/[email protected]([email protected]([email protected]))([email protected].2)':
     dependencies:
     dependencies:
       '@eslint-community/eslint-utils': 4.6.1([email protected]([email protected]))
       '@eslint-community/eslint-utils': 4.6.1([email protected]([email protected]))
       '@typescript-eslint/scope-manager': 8.26.1
       '@typescript-eslint/scope-manager': 8.26.1
       '@typescript-eslint/types': 8.26.1
       '@typescript-eslint/types': 8.26.1
-      '@typescript-eslint/typescript-estree': 8.26.1([email protected].3)
+      '@typescript-eslint/typescript-estree': 8.26.1([email protected].2)
       eslint: 9.22.0([email protected])
       eslint: 9.22.0([email protected])
-      typescript: 5.8.3
+      typescript: 5.8.2
     transitivePeerDependencies:
     transitivePeerDependencies:
       - supports-color
       - supports-color
 
 
@@ -8569,9 +8576,13 @@ snapshots:
 
 
   [email protected]: {}
   [email protected]: {}
 
 
-  [email protected]([email protected].3):
+  [email protected]([email protected].2):
     dependencies:
     dependencies:
-      typescript: 5.8.3
+      typescript: 5.8.2
+
+  [email protected]([email protected]):
+    dependencies:
+      typescript: 5.8.2
 
 
   [email protected]([email protected]):
   [email protected]([email protected]):
     dependencies:
     dependencies:
@@ -8658,13 +8669,13 @@ snapshots:
       possible-typed-array-names: 1.1.0
       possible-typed-array-names: 1.1.0
       reflect.getprototypeof: 1.0.10
       reflect.getprototypeof: 1.0.10
 
 
-  [email protected]([email protected]([email protected]))([email protected].3):
+  [email protected]([email protected]([email protected]))([email protected].2):
     dependencies:
     dependencies:
-      '@typescript-eslint/eslint-plugin': 8.26.1(@typescript-eslint/[email protected]([email protected]([email protected]))([email protected].3))([email protected]([email protected]))([email protected])
-      '@typescript-eslint/parser': 8.26.1([email protected]([email protected]))([email protected].3)
-      '@typescript-eslint/utils': 8.26.1([email protected]([email protected]))([email protected].3)
+      '@typescript-eslint/eslint-plugin': 8.26.1(@typescript-eslint/[email protected]([email protected]([email protected]))([email protected].2))([email protected]([email protected]))([email protected])
+      '@typescript-eslint/parser': 8.26.1([email protected]([email protected]))([email protected].2)
+      '@typescript-eslint/utils': 8.26.1([email protected]([email protected]))([email protected].2)
       eslint: 9.22.0([email protected])
       eslint: 9.22.0([email protected])
-      typescript: 5.8.3
+      typescript: 5.8.2
     transitivePeerDependencies:
     transitivePeerDependencies:
       - supports-color
       - supports-color
 
 
@@ -8678,6 +8689,8 @@ snapshots:
     transitivePeerDependencies:
     transitivePeerDependencies:
       - supports-color
       - supports-color
 
 
+  [email protected]: {}
+
   [email protected]: {}
   [email protected]: {}
 
 
   [email protected]:
   [email protected]:

+ 0 - 0
git


+ 2 - 2
package-lock.json

@@ -65,7 +65,7 @@
 				"vscode-material-icons": "^0.1.1",
 				"vscode-material-icons": "^0.1.1",
 				"web-tree-sitter": "^0.22.6",
 				"web-tree-sitter": "^0.22.6",
 				"workerpool": "^9.2.0",
 				"workerpool": "^9.2.0",
-				"zod": "^3.24.2"
+				"zod": "^3.23.8"
 			},
 			},
 			"devDependencies": {
 			"devDependencies": {
 				"@changesets/cli": "^2.27.10",
 				"@changesets/cli": "^2.27.10",
@@ -103,7 +103,7 @@
 				"ts-jest": "^29.2.5",
 				"ts-jest": "^29.2.5",
 				"tsup": "^8.4.0",
 				"tsup": "^8.4.0",
 				"tsx": "^4.19.3",
 				"tsx": "^4.19.3",
-				"typescript": "5.8.3",
+				"typescript": "^5.4.5",
 				"vitest": "^3.1.3",
 				"vitest": "^3.1.3",
 				"zod-to-ts": "^1.2.0"
 				"zod-to-ts": "^1.2.0"
 			},
 			},

+ 2 - 2
package.json

@@ -421,7 +421,7 @@
 		"vscode-material-icons": "^0.1.1",
 		"vscode-material-icons": "^0.1.1",
 		"web-tree-sitter": "^0.22.6",
 		"web-tree-sitter": "^0.22.6",
 		"workerpool": "^9.2.0",
 		"workerpool": "^9.2.0",
-		"zod": "^3.24.2"
+		"zod": "^3.23.8"
 	},
 	},
 	"devDependencies": {
 	"devDependencies": {
 		"@changesets/cli": "^2.27.10",
 		"@changesets/cli": "^2.27.10",
@@ -459,7 +459,7 @@
 		"ts-jest": "^29.2.5",
 		"ts-jest": "^29.2.5",
 		"tsup": "^8.4.0",
 		"tsup": "^8.4.0",
 		"tsx": "^4.19.3",
 		"tsx": "^4.19.3",
-		"typescript": "5.8.3",
+		"typescript": "^5.4.5",
 		"vitest": "^3.1.3",
 		"vitest": "^3.1.3",
 		"zod-to-ts": "^1.2.0"
 		"zod-to-ts": "^1.2.0"
 	},
 	},

+ 2 - 2
src/api/index.ts

@@ -1,7 +1,7 @@
 import { Anthropic } from "@anthropic-ai/sdk"
 import { Anthropic } from "@anthropic-ai/sdk"
 import { BetaThinkingConfigParam } from "@anthropic-ai/sdk/resources/beta/messages/index.mjs"
 import { BetaThinkingConfigParam } from "@anthropic-ai/sdk/resources/beta/messages/index.mjs"
 
 
-import { ProviderSettings, ModelInfo, ApiHandlerOptions } from "../shared/api"
+import { ApiConfiguration, ModelInfo, ApiHandlerOptions } from "../shared/api"
 import { ANTHROPIC_DEFAULT_MAX_TOKENS } from "./providers/constants"
 import { ANTHROPIC_DEFAULT_MAX_TOKENS } from "./providers/constants"
 import { GlamaHandler } from "./providers/glama"
 import { GlamaHandler } from "./providers/glama"
 import { AnthropicHandler } from "./providers/anthropic"
 import { AnthropicHandler } from "./providers/anthropic"
@@ -47,7 +47,7 @@ export interface ApiHandler {
 	countTokens(content: Array<Anthropic.Messages.ContentBlockParam>): Promise<number>
 	countTokens(content: Array<Anthropic.Messages.ContentBlockParam>): Promise<number>
 }
 }
 
 
-export function buildApiHandler(configuration: ProviderSettings): ApiHandler {
+export function buildApiHandler(configuration: ApiConfiguration): ApiHandler {
 	const { apiProvider, ...options } = configuration
 	const { apiProvider, ...options } = configuration
 
 
 	switch (apiProvider) {
 	switch (apiProvider) {

+ 31 - 57
src/core/config/ProviderSettingsManager.ts

@@ -1,14 +1,11 @@
 import { ExtensionContext } from "vscode"
 import { ExtensionContext } from "vscode"
 import { z, ZodError } from "zod"
 import { z, ZodError } from "zod"
 
 
-import { providerSettingsSchema, ProviderSettingsEntry, providerSettingsSchemaDiscriminated } from "../../schemas"
+import { providerSettingsSchema, ApiConfigMeta } from "../../schemas"
 import { Mode, modes } from "../../shared/modes"
 import { Mode, modes } from "../../shared/modes"
 import { telemetryService } from "../../services/telemetry/TelemetryService"
 import { telemetryService } from "../../services/telemetry/TelemetryService"
 
 
 const providerSettingsWithIdSchema = providerSettingsSchema.extend({ id: z.string().optional() })
 const providerSettingsWithIdSchema = providerSettingsSchema.extend({ id: z.string().optional() })
-const discriminatedProviderSettingsWithIdSchema = providerSettingsSchemaDiscriminated.and(
-	z.object({ id: z.string().optional() }),
-)
 
 
 type ProviderSettingsWithId = z.infer<typeof providerSettingsWithIdSchema>
 type ProviderSettingsWithId = z.infer<typeof providerSettingsWithIdSchema>
 
 
@@ -226,7 +223,7 @@ export class ProviderSettingsManager {
 	/**
 	/**
 	 * List all available configs with metadata.
 	 * List all available configs with metadata.
 	 */
 	 */
-	public async listConfig(): Promise<ProviderSettingsEntry[]> {
+	public async listConfig(): Promise<ApiConfigMeta[]> {
 		try {
 		try {
 			return await this.lock(async () => {
 			return await this.lock(async () => {
 				const providerProfiles = await this.load()
 				const providerProfiles = await this.load()
@@ -247,81 +244,66 @@ export class ProviderSettingsManager {
 	 * Preserves the ID from the input 'config' object if it exists,
 	 * Preserves the ID from the input 'config' object if it exists,
 	 * otherwise generates a new one (for creation scenarios).
 	 * otherwise generates a new one (for creation scenarios).
 	 */
 	 */
-	public async saveConfig(name: string, config: ProviderSettingsWithId): Promise<string> {
+	public async saveConfig(name: string, config: ProviderSettingsWithId) {
 		try {
 		try {
 			return await this.lock(async () => {
 			return await this.lock(async () => {
 				const providerProfiles = await this.load()
 				const providerProfiles = await this.load()
 				// Preserve the existing ID if this is an update to an existing config.
 				// Preserve the existing ID if this is an update to an existing config.
 				const existingId = providerProfiles.apiConfigs[name]?.id
 				const existingId = providerProfiles.apiConfigs[name]?.id
-				const id = config.id || existingId || this.generateId()
-
-				// Filter out settings from other providers.
-				const filteredConfig = providerSettingsSchemaDiscriminated.parse(config)
-				providerProfiles.apiConfigs[name] = { ...filteredConfig, id }
+				providerProfiles.apiConfigs[name] = { ...config, id: config.id || existingId || this.generateId() }
 				await this.store(providerProfiles)
 				await this.store(providerProfiles)
-				return id
 			})
 			})
 		} catch (error) {
 		} catch (error) {
 			throw new Error(`Failed to save config: ${error}`)
 			throw new Error(`Failed to save config: ${error}`)
 		}
 		}
 	}
 	}
 
 
-	public async getProfile(
-		params: { name: string } | { id: string },
-	): Promise<ProviderSettingsWithId & { name: string }> {
+	/**
+	 * Load a config by name and set it as the current config.
+	 */
+	public async loadConfig(name: string) {
 		try {
 		try {
 			return await this.lock(async () => {
 			return await this.lock(async () => {
 				const providerProfiles = await this.load()
 				const providerProfiles = await this.load()
-				let name: string
-				let providerSettings: ProviderSettingsWithId
-
-				if ("name" in params) {
-					name = params.name
-
-					if (!providerProfiles.apiConfigs[name]) {
-						throw new Error(`Config with name '${name}' not found`)
-					}
+				const providerSettings = providerProfiles.apiConfigs[name]
 
 
-					providerSettings = providerProfiles.apiConfigs[name]
-				} else {
-					const id = params.id
-
-					const entry = Object.entries(providerProfiles.apiConfigs).find(
-						([_, apiConfig]) => apiConfig.id === id,
-					)
-
-					if (!entry) {
-						throw new Error(`Config with ID '${id}' not found`)
-					}
-
-					name = entry[0]
-					providerSettings = entry[1]
+				if (!providerSettings) {
+					throw new Error(`Config '${name}' not found`)
 				}
 				}
 
 
-				return { name, ...providerSettings }
+				providerProfiles.currentApiConfigName = name
+				await this.store(providerProfiles)
+
+				return providerSettings
 			})
 			})
 		} catch (error) {
 		} catch (error) {
-			throw new Error(`Failed to get profile: ${error instanceof Error ? error.message : error}`)
+			throw new Error(`Failed to load config: ${error}`)
 		}
 		}
 	}
 	}
 
 
 	/**
 	/**
-	 * Activate a profile by name or ID.
+	 * Load a config by ID and set it as the current config.
 	 */
 	 */
-	public async activateProfile(
-		params: { name: string } | { id: string },
-	): Promise<ProviderSettingsWithId & { name: string }> {
-		const { name, ...providerSettings } = await this.getProfile(params)
-
+	public async loadConfigById(id: string) {
 		try {
 		try {
 			return await this.lock(async () => {
 			return await this.lock(async () => {
 				const providerProfiles = await this.load()
 				const providerProfiles = await this.load()
+				const providerSettings = Object.entries(providerProfiles.apiConfigs).find(
+					([_, apiConfig]) => apiConfig.id === id,
+				)
+
+				if (!providerSettings) {
+					throw new Error(`Config with ID '${id}' not found`)
+				}
+
+				const [name, apiConfig] = providerSettings
 				providerProfiles.currentApiConfigName = name
 				providerProfiles.currentApiConfigName = name
 				await this.store(providerProfiles)
 				await this.store(providerProfiles)
-				return { name, ...providerSettings }
+
+				return { config: apiConfig, name }
 			})
 			})
 		} catch (error) {
 		} catch (error) {
-			throw new Error(`Failed to activate profile: ${error instanceof Error ? error.message : error}`)
+			throw new Error(`Failed to load config by ID: ${error}`)
 		}
 		}
 	}
 	}
 
 
@@ -399,15 +381,7 @@ export class ProviderSettingsManager {
 
 
 	public async export() {
 	public async export() {
 		try {
 		try {
-			return await this.lock(async () => {
-				const profiles = providerProfilesSchema.parse(await this.load())
-				const configs = profiles.apiConfigs
-				for (const name in configs) {
-					// Avoid leaking properties from other providers.
-					configs[name] = discriminatedProviderSettingsWithIdSchema.parse(configs[name])
-				}
-				return profiles
-			})
+			return await this.lock(async () => providerProfilesSchema.parse(await this.load()))
 		} catch (error) {
 		} catch (error) {
 			throw new Error(`Failed to export provider profiles: ${error}`)
 			throw new Error(`Failed to export provider profiles: ${error}`)
 		}
 		}

+ 54 - 69
src/core/config/__tests__/ProviderSettingsManager.test.ts

@@ -247,58 +247,10 @@ describe("ProviderSettingsManager", () => {
 				},
 				},
 			}
 			}
 
 
-			expect(mockSecrets.store.mock.calls[0][0]).toEqual("roo_cline_config_api_config")
-			expect(storedConfig).toEqual(expectedConfig)
-		})
-
-		it("should only save provider relevant settings", async () => {
-			mockSecrets.get.mockResolvedValue(
-				JSON.stringify({
-					currentApiConfigName: "default",
-					apiConfigs: {
-						default: {},
-					},
-					modeApiConfigs: {
-						code: "default",
-						architect: "default",
-						ask: "default",
-					},
-				}),
+			expect(mockSecrets.store).toHaveBeenCalledWith(
+				"roo_cline_config_api_config",
+				JSON.stringify(expectedConfig, null, 2),
 			)
 			)
-
-			const newConfig: ProviderSettings = {
-				apiProvider: "anthropic",
-				apiKey: "test-key",
-			}
-			const newConfigWithExtra: ProviderSettings = {
-				...newConfig,
-				openRouterApiKey: "another-key",
-			}
-
-			await providerSettingsManager.saveConfig("test", newConfigWithExtra)
-
-			// Get the actual stored config to check the generated ID
-			const storedConfig = JSON.parse(mockSecrets.store.mock.lastCall[1])
-			const testConfigId = storedConfig.apiConfigs.test.id
-
-			const expectedConfig = {
-				currentApiConfigName: "default",
-				apiConfigs: {
-					default: {},
-					test: {
-						...newConfig,
-						id: testConfigId,
-					},
-				},
-				modeApiConfigs: {
-					code: "default",
-					architect: "default",
-					ask: "default",
-				},
-			}
-
-			expect(mockSecrets.store.mock.calls[0][0]).toEqual("roo_cline_config_api_config")
-			expect(storedConfig).toEqual(expectedConfig)
 		})
 		})
 
 
 		it("should update existing config", async () => {
 		it("should update existing config", async () => {
@@ -339,9 +291,10 @@ describe("ProviderSettingsManager", () => {
 				},
 				},
 			}
 			}
 
 
-			const storedConfig = JSON.parse(mockSecrets.store.mock.lastCall[1])
-			expect(mockSecrets.store.mock.lastCall[0]).toEqual("roo_cline_config_api_config")
-			expect(storedConfig).toEqual(expectedConfig)
+			expect(mockSecrets.store).toHaveBeenCalledWith(
+				"roo_cline_config_api_config",
+				JSON.stringify(expectedConfig, null, 2),
+			)
 		})
 		})
 
 
 		it("should throw error if secrets storage fails", async () => {
 		it("should throw error if secrets storage fails", async () => {
@@ -438,15 +391,17 @@ describe("ProviderSettingsManager", () => {
 			mockGlobalState.get.mockResolvedValue(42)
 			mockGlobalState.get.mockResolvedValue(42)
 			mockSecrets.get.mockResolvedValue(JSON.stringify(existingConfig))
 			mockSecrets.get.mockResolvedValue(JSON.stringify(existingConfig))
 
 
-			const { name, ...providerSettings } = await providerSettingsManager.activateProfile({ name: "test" })
+			const config = await providerSettingsManager.loadConfig("test")
 
 
-			expect(name).toBe("test")
-			expect(providerSettings).toEqual({ apiProvider: "anthropic", apiKey: "test-key", id: "test-id" })
+			expect(config).toEqual({
+				apiProvider: "anthropic",
+				apiKey: "test-key",
+				id: "test-id",
+			})
 
 
-			// Get the stored config to check the structure.
+			// Get the stored config to check the structure
 			const storedConfig = JSON.parse(mockSecrets.store.mock.calls[1][1])
 			const storedConfig = JSON.parse(mockSecrets.store.mock.calls[1][1])
 			expect(storedConfig.currentApiConfigName).toBe("test")
 			expect(storedConfig.currentApiConfigName).toBe("test")
-
 			expect(storedConfig.apiConfigs.test).toEqual({
 			expect(storedConfig.apiConfigs.test).toEqual({
 				apiProvider: "anthropic",
 				apiProvider: "anthropic",
 				apiKey: "test-key",
 				apiKey: "test-key",
@@ -458,12 +413,17 @@ describe("ProviderSettingsManager", () => {
 			mockSecrets.get.mockResolvedValue(
 			mockSecrets.get.mockResolvedValue(
 				JSON.stringify({
 				JSON.stringify({
 					currentApiConfigName: "default",
 					currentApiConfigName: "default",
-					apiConfigs: { default: { config: {}, id: "default" } },
+					apiConfigs: {
+						default: {
+							config: {},
+							id: "default",
+						},
+					},
 				}),
 				}),
 			)
 			)
 
 
-			await expect(providerSettingsManager.activateProfile({ name: "nonexistent" })).rejects.toThrow(
-				"Config with name 'nonexistent' not found",
+			await expect(providerSettingsManager.loadConfig("nonexistent")).rejects.toThrow(
+				"Config 'nonexistent' not found",
 			)
 			)
 		})
 		})
 
 
@@ -471,13 +431,20 @@ describe("ProviderSettingsManager", () => {
 			mockSecrets.get.mockResolvedValue(
 			mockSecrets.get.mockResolvedValue(
 				JSON.stringify({
 				JSON.stringify({
 					currentApiConfigName: "default",
 					currentApiConfigName: "default",
-					apiConfigs: { test: { config: { apiProvider: "anthropic" }, id: "test-id" } },
+					apiConfigs: {
+						test: {
+							config: {
+								apiProvider: "anthropic",
+							},
+							id: "test-id",
+						},
+					},
 				}),
 				}),
 			)
 			)
 			mockSecrets.store.mockRejectedValueOnce(new Error("Storage failed"))
 			mockSecrets.store.mockRejectedValueOnce(new Error("Storage failed"))
 
 
-			await expect(providerSettingsManager.activateProfile({ name: "test" })).rejects.toThrow(
-				"Failed to activate profile: Failed to write provider profiles to secrets: Error: Storage failed",
+			await expect(providerSettingsManager.loadConfig("test")).rejects.toThrow(
+				"Failed to load config: Error: Failed to write provider profiles to secrets: Error: Storage failed",
 			)
 			)
 		})
 		})
 
 
@@ -527,7 +494,12 @@ describe("ProviderSettingsManager", () => {
 			mockSecrets.get.mockResolvedValue(
 			mockSecrets.get.mockResolvedValue(
 				JSON.stringify({
 				JSON.stringify({
 					currentApiConfigName: "test",
 					currentApiConfigName: "test",
-					apiConfigs: { test: { apiProvider: "anthropic", id: "test-id" } },
+					apiConfigs: {
+						test: {
+							apiProvider: "anthropic",
+							id: "test-id",
+						},
+					},
 				}),
 				}),
 			)
 			)
 
 
@@ -542,8 +514,18 @@ describe("ProviderSettingsManager", () => {
 		it("should return true for existing config", async () => {
 		it("should return true for existing config", async () => {
 			const existingConfig: ProviderProfiles = {
 			const existingConfig: ProviderProfiles = {
 				currentApiConfigName: "default",
 				currentApiConfigName: "default",
-				apiConfigs: { default: { id: "default" }, test: { apiProvider: "anthropic", id: "test-id" } },
-				migrations: { rateLimitSecondsMigrated: false },
+				apiConfigs: {
+					default: {
+						id: "default",
+					},
+					test: {
+						apiProvider: "anthropic",
+						id: "test-id",
+					},
+				},
+				migrations: {
+					rateLimitSecondsMigrated: false,
+				},
 			}
 			}
 
 
 			mockSecrets.get.mockResolvedValue(JSON.stringify(existingConfig))
 			mockSecrets.get.mockResolvedValue(JSON.stringify(existingConfig))
@@ -554,7 +536,10 @@ describe("ProviderSettingsManager", () => {
 
 
 		it("should return false for non-existent config", async () => {
 		it("should return false for non-existent config", async () => {
 			mockSecrets.get.mockResolvedValue(
 			mockSecrets.get.mockResolvedValue(
-				JSON.stringify({ currentApiConfigName: "default", apiConfigs: { default: {} } }),
+				JSON.stringify({
+					currentApiConfigName: "default",
+					apiConfigs: { default: {} },
+				}),
 			)
 			)
 
 
 			const hasConfig = await providerSettingsManager.hasConfig("nonexistent")
 			const hasConfig = await providerSettingsManager.hasConfig("nonexistent")

+ 3 - 3
src/core/task/Task.ts

@@ -16,7 +16,7 @@ import { ApiHandler, buildApiHandler } from "../../api"
 import { ApiStream } from "../../api/transform/stream"
 import { ApiStream } from "../../api/transform/stream"
 
 
 // shared
 // shared
-import { ProviderSettings } from "../../shared/api"
+import { ApiConfiguration } from "../../shared/api"
 import { findLastIndex } from "../../shared/array"
 import { findLastIndex } from "../../shared/array"
 import { combineApiRequests } from "../../shared/combineApiRequests"
 import { combineApiRequests } from "../../shared/combineApiRequests"
 import { combineCommandSequences } from "../../shared/combineCommandSequences"
 import { combineCommandSequences } from "../../shared/combineCommandSequences"
@@ -92,7 +92,7 @@ export type ClineEvents = {
 
 
 export type TaskOptions = {
 export type TaskOptions = {
 	provider: ClineProvider
 	provider: ClineProvider
-	apiConfiguration: ProviderSettings
+	apiConfiguration: ApiConfiguration
 	customInstructions?: string
 	customInstructions?: string
 	enableDiff?: boolean
 	enableDiff?: boolean
 	enableCheckpoints?: boolean
 	enableCheckpoints?: boolean
@@ -130,7 +130,7 @@ export class Task extends EventEmitter<ClineEvents> {
 	customInstructions?: string
 	customInstructions?: string
 
 
 	// API
 	// API
-	readonly apiConfiguration: ProviderSettings
+	readonly apiConfiguration: ApiConfiguration
 	api: ApiHandler
 	api: ApiHandler
 	private promptCacheKey: string
 	private promptCacheKey: string
 	private lastApiRequestTime?: number
 	private lastApiRequestTime?: number

+ 2 - 2
src/core/task/__tests__/Task.test.ts

@@ -9,7 +9,7 @@ import { Anthropic } from "@anthropic-ai/sdk"
 import { GlobalState } from "../../../schemas"
 import { GlobalState } from "../../../schemas"
 import { Task } from "../Task"
 import { Task } from "../Task"
 import { ClineProvider } from "../../webview/ClineProvider"
 import { ClineProvider } from "../../webview/ClineProvider"
-import { ProviderSettings, ModelInfo } from "../../../shared/api"
+import { ApiConfiguration, ModelInfo } from "../../../shared/api"
 import { ApiStreamChunk } from "../../../api/transform/stream"
 import { ApiStreamChunk } from "../../../api/transform/stream"
 import { ContextProxy } from "../../config/ContextProxy"
 import { ContextProxy } from "../../config/ContextProxy"
 import { processUserContentMentions } from "../../mentions/processUserContentMentions"
 import { processUserContentMentions } from "../../mentions/processUserContentMentions"
@@ -156,7 +156,7 @@ const mockMessages = [
 
 
 describe("Cline", () => {
 describe("Cline", () => {
 	let mockProvider: jest.Mocked<ClineProvider>
 	let mockProvider: jest.Mocked<ClineProvider>
-	let mockApiConfig: ProviderSettings
+	let mockApiConfig: ApiConfiguration
 	let mockOutputChannel: any
 	let mockOutputChannel: any
 	let mockExtensionContext: vscode.ExtensionContext
 	let mockExtensionContext: vscode.ExtensionContext
 
 

+ 61 - 127
src/core/webview/ClineProvider.ts

@@ -9,10 +9,16 @@ import axios from "axios"
 import pWaitFor from "p-wait-for"
 import pWaitFor from "p-wait-for"
 import * as vscode from "vscode"
 import * as vscode from "vscode"
 
 
-import type { GlobalState, ProviderName, ProviderSettings, RooCodeSettings, ProviderSettingsEntry } from "../../schemas"
+import { GlobalState, ProviderSettings, RooCodeSettings } from "../../schemas"
 import { t } from "../../i18n"
 import { t } from "../../i18n"
 import { setPanel } from "../../activate/registerCommands"
 import { setPanel } from "../../activate/registerCommands"
-import { requestyDefaultModelId, openRouterDefaultModelId, glamaDefaultModelId } from "../../shared/api"
+import {
+	ProviderName,
+	ApiConfiguration,
+	requestyDefaultModelId,
+	openRouterDefaultModelId,
+	glamaDefaultModelId,
+} from "../../shared/api"
 import { findLast } from "../../shared/array"
 import { findLast } from "../../shared/array"
 import { supportPrompt } from "../../shared/support-prompt"
 import { supportPrompt } from "../../shared/support-prompt"
 import { GlobalFileNames } from "../../shared/globalFileNames"
 import { GlobalFileNames } from "../../shared/globalFileNames"
@@ -246,7 +252,7 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
 			return false
 			return false
 		}
 		}
 
 
-		// Check if there is a cline instance in the stack (if this provider has an active task)
+		// check if there is a cline instance in the stack (if this provider has an active task)
 		if (visibleProvider.getCurrentCline()) {
 		if (visibleProvider.getCurrentCline()) {
 			return true
 			return true
 		}
 		}
@@ -756,6 +762,7 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
 	 * @param newMode The mode to switch to
 	 * @param newMode The mode to switch to
 	 */
 	 */
 	public async handleModeSwitch(newMode: Mode) {
 	public async handleModeSwitch(newMode: Mode) {
+		// Capture mode switch telemetry event
 		const cline = this.getCurrentCline()
 		const cline = this.getCurrentCline()
 
 
 		if (cline) {
 		if (cline) {
@@ -772,19 +779,24 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
 		// Update listApiConfigMeta first to ensure UI has latest data
 		// Update listApiConfigMeta first to ensure UI has latest data
 		await this.updateGlobalState("listApiConfigMeta", listApiConfig)
 		await this.updateGlobalState("listApiConfigMeta", listApiConfig)
 
 
-		// If this mode has a saved config, use it.
+		// If this mode has a saved config, use it
 		if (savedConfigId) {
 		if (savedConfigId) {
-			const profile = listApiConfig.find(({ id }) => id === savedConfigId)
+			const config = listApiConfig?.find((c) => c.id === savedConfigId)
 
 
-			if (profile?.name) {
-				await this.activateProviderProfile({ name: profile.name })
+			if (config?.name) {
+				const apiConfig = await this.providerSettingsManager.loadConfig(config.name)
+
+				await Promise.all([
+					this.updateGlobalState("currentApiConfigName", config.name),
+					this.updateApiConfiguration(apiConfig),
+				])
 			}
 			}
 		} else {
 		} else {
-			// If no saved config for this mode, save current config as default.
+			// If no saved config for this mode, save current config as default
 			const currentApiConfigName = this.getGlobalState("currentApiConfigName")
 			const currentApiConfigName = this.getGlobalState("currentApiConfigName")
 
 
 			if (currentApiConfigName) {
 			if (currentApiConfigName) {
-				const config = listApiConfig.find((c) => c.name === currentApiConfigName)
+				const config = listApiConfig?.find((c) => c.name === currentApiConfigName)
 
 
 				if (config?.id) {
 				if (config?.id) {
 					await this.providerSettingsManager.setModeConfig(newMode, config.id)
 					await this.providerSettingsManager.setModeConfig(newMode, config.id)
@@ -795,127 +807,27 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
 		await this.postStateToWebview()
 		await this.postStateToWebview()
 	}
 	}
 
 
-	// Provider Profile Management
-
-	getProviderProfileEntries(): ProviderSettingsEntry[] {
-		return this.contextProxy.getValues().listApiConfigMeta || []
-	}
-
-	getProviderProfileEntry(name: string): ProviderSettingsEntry | undefined {
-		return this.getProviderProfileEntries().find((profile) => profile.name === name)
-	}
-
-	public hasProviderProfileEntry(name: string): boolean {
-		return !!this.getProviderProfileEntry(name)
-	}
-
-	async upsertProviderProfile(
-		name: string,
-		providerSettings: ProviderSettings,
-		activate: boolean = true,
-	): Promise<string | undefined> {
-		try {
-			// TODO: Do we need to be calling `activateProfile`? It's not
-			// clear to me what the source of truth should be; in some cases
-			// we rely on the `ContextProxy`'s data store and in other cases
-			// we rely on the `ProviderSettingsManager`'s data store. It might
-			// be simpler to unify these two.
-			const id = await this.providerSettingsManager.saveConfig(name, providerSettings)
-
-			if (activate) {
-				const { mode } = await this.getState()
-
-				// These promises do the following:
-				// 1. Adds or updates the list of provider profiles.
-				// 2. Sets the current provider profile.
-				// 3. Sets the current mode's provider profile.
-				// 4. Copies the provider settings to the context.
-				//
-				// Note: 1, 2, and 4 can be done in one `ContextProxy` call:
-				// this.contextProxy.setValues({ ...providerSettings, listApiConfigMeta: ..., currentApiConfigName: ... })
-				// We should probably switch to that and verify that it works.
-				// I left the original implementation in just to be safe.
-				await Promise.all([
-					this.updateGlobalState("listApiConfigMeta", await this.providerSettingsManager.listConfig()),
-					this.updateGlobalState("currentApiConfigName", name),
-					this.providerSettingsManager.setModeConfig(mode, id),
-					this.contextProxy.setProviderSettings(providerSettings),
-				])
+	async updateApiConfiguration(providerSettings: ProviderSettings) {
+		// Update mode's default config.
+		const { mode } = await this.getState()
 
 
-				// Change the provider for the current task.
-				// TODO: We should rename `buildApiHandler` for clarity (e.g. `getProviderClient`).
-				const task = this.getCurrentCline()
+		if (mode) {
+			const currentApiConfigName = this.getGlobalState("currentApiConfigName")
+			const listApiConfig = await this.providerSettingsManager.listConfig()
+			const config = listApiConfig?.find((c) => c.name === currentApiConfigName)
 
 
-				if (task) {
-					task.api = buildApiHandler(providerSettings)
-				}
-			} else {
-				await this.updateGlobalState("listApiConfigMeta", await this.providerSettingsManager.listConfig())
+			if (config?.id) {
+				await this.providerSettingsManager.setModeConfig(mode, config.id)
 			}
 			}
-
-			await this.postStateToWebview()
-			return id
-		} catch (error) {
-			this.log(
-				`Error create new api configuration: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
-			)
-
-			vscode.window.showErrorMessage(t("common:errors.create_api_config"))
-			return undefined
-		}
-	}
-
-	async deleteProviderProfile(profileToDelete: ProviderSettingsEntry) {
-		const globalSettings = this.contextProxy.getValues()
-		let profileToActivate: string | undefined = globalSettings.currentApiConfigName
-
-		if (profileToDelete.name === profileToActivate) {
-			profileToActivate = this.getProviderProfileEntries().find(({ name }) => name !== profileToDelete.name)?.name
 		}
 		}
 
 
-		if (!profileToActivate) {
-			throw new Error("You cannot delete the last profile")
-		}
+		await this.contextProxy.setProviderSettings(providerSettings)
 
 
-		const entries = this.getProviderProfileEntries().filter(({ name }) => name !== profileToDelete.name)
-
-		await this.contextProxy.setValues({
-			...globalSettings,
-			currentApiConfigName: profileToActivate,
-			listApiConfigMeta: entries,
-		})
-
-		await this.postStateToWebview()
-	}
-
-	async activateProviderProfile(args: { name: string } | { id: string }) {
-		const { name, id, ...providerSettings } = await this.providerSettingsManager.activateProfile(args)
-
-		// See `upsertProviderProfile` for a description of what this is doing.
-		await Promise.all([
-			this.contextProxy.setValue("listApiConfigMeta", await this.providerSettingsManager.listConfig()),
-			this.contextProxy.setValue("currentApiConfigName", name),
-			this.contextProxy.setProviderSettings(providerSettings),
-		])
-
-		const { mode } = await this.getState()
-
-		if (id) {
-			await this.providerSettingsManager.setModeConfig(mode, id)
-		}
-
-		// Change the provider for the current task.
-		const task = this.getCurrentCline()
-
-		if (task) {
-			task.api = buildApiHandler(providerSettings)
+		if (this.getCurrentCline()) {
+			this.getCurrentCline()!.api = buildApiHandler(providerSettings)
 		}
 		}
-
-		await this.postStateToWebview()
 	}
 	}
 
 
-	// Task Management
-
 	async cancelTask() {
 	async cancelTask() {
 		const cline = this.getCurrentCline()
 		const cline = this.getCurrentCline()
 
 
@@ -1024,14 +936,14 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
 			throw error
 			throw error
 		}
 		}
 
 
-		const newConfiguration: ProviderSettings = {
+		const newConfiguration: ApiConfiguration = {
 			...apiConfiguration,
 			...apiConfiguration,
 			apiProvider: "openrouter",
 			apiProvider: "openrouter",
 			openRouterApiKey: apiKey,
 			openRouterApiKey: apiKey,
 			openRouterModelId: apiConfiguration?.openRouterModelId || openRouterDefaultModelId,
 			openRouterModelId: apiConfiguration?.openRouterModelId || openRouterDefaultModelId,
 		}
 		}
 
 
-		await this.upsertProviderProfile(currentApiConfigName, newConfiguration)
+		await this.upsertApiConfiguration(currentApiConfigName, newConfiguration)
 	}
 	}
 
 
 	// Glama
 	// Glama
@@ -1054,14 +966,14 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
 
 
 		const { apiConfiguration, currentApiConfigName } = await this.getState()
 		const { apiConfiguration, currentApiConfigName } = await this.getState()
 
 
-		const newConfiguration: ProviderSettings = {
+		const newConfiguration: ApiConfiguration = {
 			...apiConfiguration,
 			...apiConfiguration,
 			apiProvider: "glama",
 			apiProvider: "glama",
 			glamaApiKey: apiKey,
 			glamaApiKey: apiKey,
 			glamaModelId: apiConfiguration?.glamaModelId || glamaDefaultModelId,
 			glamaModelId: apiConfiguration?.glamaModelId || glamaDefaultModelId,
 		}
 		}
 
 
-		await this.upsertProviderProfile(currentApiConfigName, newConfiguration)
+		await this.upsertApiConfiguration(currentApiConfigName, newConfiguration)
 	}
 	}
 
 
 	// Requesty
 	// Requesty
@@ -1069,14 +981,36 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
 	async handleRequestyCallback(code: string) {
 	async handleRequestyCallback(code: string) {
 		let { apiConfiguration, currentApiConfigName } = await this.getState()
 		let { apiConfiguration, currentApiConfigName } = await this.getState()
 
 
-		const newConfiguration: ProviderSettings = {
+		const newConfiguration: ApiConfiguration = {
 			...apiConfiguration,
 			...apiConfiguration,
 			apiProvider: "requesty",
 			apiProvider: "requesty",
 			requestyApiKey: code,
 			requestyApiKey: code,
 			requestyModelId: apiConfiguration?.requestyModelId || requestyDefaultModelId,
 			requestyModelId: apiConfiguration?.requestyModelId || requestyDefaultModelId,
 		}
 		}
 
 
-		await this.upsertProviderProfile(currentApiConfigName, newConfiguration)
+		await this.upsertApiConfiguration(currentApiConfigName, newConfiguration)
+	}
+
+	// Save configuration
+
+	async upsertApiConfiguration(configName: string, apiConfiguration: ApiConfiguration) {
+		try {
+			await this.providerSettingsManager.saveConfig(configName, apiConfiguration)
+			const listApiConfig = await this.providerSettingsManager.listConfig()
+
+			await Promise.all([
+				this.updateGlobalState("listApiConfigMeta", listApiConfig),
+				this.updateApiConfiguration(apiConfiguration),
+				this.updateGlobalState("currentApiConfigName", configName),
+			])
+
+			await this.postStateToWebview()
+		} catch (error) {
+			this.log(
+				`Error create new api configuration: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
+			)
+			vscode.window.showErrorMessage(t("common:errors.create_api_config"))
+		}
 	}
 	}
 
 
 	// Task history
 	// Task history

+ 41 - 40
src/core/webview/__tests__/ClineProvider.test.ts

@@ -5,7 +5,7 @@ import * as vscode from "vscode"
 import axios from "axios"
 import axios from "axios"
 
 
 import { ClineProvider } from "../ClineProvider"
 import { ClineProvider } from "../ClineProvider"
-import { ProviderSettingsEntry, ClineMessage, ExtensionMessage, ExtensionState } from "../../../shared/ExtensionMessage"
+import { ClineMessage, ExtensionMessage, ExtensionState } from "../../../shared/ExtensionMessage"
 import { setSoundEnabled } from "../../../utils/sound"
 import { setSoundEnabled } from "../../../utils/sound"
 import { setTtsEnabled } from "../../../utils/tts"
 import { setTtsEnabled } from "../../../utils/tts"
 import { defaultModeSlug } from "../../../shared/modes"
 import { defaultModeSlug } from "../../../shared/modes"
@@ -227,6 +227,12 @@ jest.mock("../../../integrations/misc/extract-text", () => ({
 	}),
 	}),
 }))
 }))
 
 
+// Spy on console.error and console.log to suppress expected messages
+beforeAll(() => {
+	jest.spyOn(console, "error").mockImplementation(() => {})
+	jest.spyOn(console, "log").mockImplementation(() => {})
+})
+
 afterAll(() => {
 afterAll(() => {
 	jest.restoreAllMocks()
 	jest.restoreAllMocks()
 })
 })
@@ -590,16 +596,14 @@ describe("ClineProvider", () => {
 		expect(state.alwaysApproveResubmit).toBe(false)
 		expect(state.alwaysApproveResubmit).toBe(false)
 	})
 	})
 
 
-	it("loads saved API config when switching modes", async () => {
+	test("loads saved API config when switching modes", async () => {
 		await provider.resolveWebviewView(mockWebviewView)
 		await provider.resolveWebviewView(mockWebviewView)
 		const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
 		const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
 
 
-		const profile: ProviderSettingsEntry = { name: "test-config", id: "test-id", apiProvider: "anthropic" }
-
 		;(provider as any).providerSettingsManager = {
 		;(provider as any).providerSettingsManager = {
 			getModeConfigId: jest.fn().mockResolvedValue("test-id"),
 			getModeConfigId: jest.fn().mockResolvedValue("test-id"),
-			listConfig: jest.fn().mockResolvedValue([profile]),
-			activateProfile: jest.fn().mockResolvedValue(profile),
+			listConfig: jest.fn().mockResolvedValue([{ name: "test-config", id: "test-id", apiProvider: "anthropic" }]),
+			loadConfig: jest.fn().mockResolvedValue({ apiProvider: "anthropic" }),
 			setModeConfig: jest.fn(),
 			setModeConfig: jest.fn(),
 		} as any
 		} as any
 
 
@@ -608,11 +612,11 @@ describe("ClineProvider", () => {
 
 
 		// Should load the saved config for architect mode
 		// Should load the saved config for architect mode
 		expect(provider.providerSettingsManager.getModeConfigId).toHaveBeenCalledWith("architect")
 		expect(provider.providerSettingsManager.getModeConfigId).toHaveBeenCalledWith("architect")
-		expect(provider.providerSettingsManager.activateProfile).toHaveBeenCalledWith({ name: "test-config" })
+		expect(provider.providerSettingsManager.loadConfig).toHaveBeenCalledWith("test-config")
 		expect(mockContext.globalState.update).toHaveBeenCalledWith("currentApiConfigName", "test-config")
 		expect(mockContext.globalState.update).toHaveBeenCalledWith("currentApiConfigName", "test-config")
 	})
 	})
 
 
-	it("saves current config when switching to mode without config", async () => {
+	test("saves current config when switching to mode without config", async () => {
 		await provider.resolveWebviewView(mockWebviewView)
 		await provider.resolveWebviewView(mockWebviewView)
 		const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
 		const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
 
 
@@ -633,15 +637,16 @@ describe("ClineProvider", () => {
 		expect(provider.providerSettingsManager.setModeConfig).toHaveBeenCalledWith("architect", "current-id")
 		expect(provider.providerSettingsManager.setModeConfig).toHaveBeenCalledWith("architect", "current-id")
 	})
 	})
 
 
-	it("saves config as default for current mode when loading config", async () => {
+	test("saves config as default for current mode when loading config", async () => {
 		await provider.resolveWebviewView(mockWebviewView)
 		await provider.resolveWebviewView(mockWebviewView)
 		const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
 		const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
 
 
-		const profile: ProviderSettingsEntry = { apiProvider: "anthropic", id: "new-id", name: "new-config" }
-
 		;(provider as any).providerSettingsManager = {
 		;(provider as any).providerSettingsManager = {
-			activateProfile: jest.fn().mockResolvedValue(profile),
-			listConfig: jest.fn().mockResolvedValue([profile]),
+			loadConfig: jest.fn().mockResolvedValue({ apiProvider: "anthropic", id: "new-id" }),
+			loadConfigById: jest
+				.fn()
+				.mockResolvedValue({ config: { apiProvider: "anthropic", id: "new-id" }, name: "new-config" }),
+			listConfig: jest.fn().mockResolvedValue([{ name: "new-config", id: "new-id", apiProvider: "anthropic" }]),
 			setModeConfig: jest.fn(),
 			setModeConfig: jest.fn(),
 			getModeConfigId: jest.fn().mockResolvedValue(undefined),
 			getModeConfigId: jest.fn().mockResolvedValue(undefined),
 		} as any
 		} as any
@@ -656,19 +661,18 @@ describe("ClineProvider", () => {
 		expect(provider.providerSettingsManager.setModeConfig).toHaveBeenCalledWith("architect", "new-id")
 		expect(provider.providerSettingsManager.setModeConfig).toHaveBeenCalledWith("architect", "new-id")
 	})
 	})
 
 
-	it("load API configuration by ID works and updates mode config", async () => {
+	test("load API configuration by ID works and updates mode config", async () => {
 		await provider.resolveWebviewView(mockWebviewView)
 		await provider.resolveWebviewView(mockWebviewView)
 		const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
 		const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]
 
 
-		const profile: ProviderSettingsEntry = {
-			name: "config-by-id",
-			id: "config-id-123",
-			apiProvider: "anthropic",
-		}
-
 		;(provider as any).providerSettingsManager = {
 		;(provider as any).providerSettingsManager = {
-			activateProfile: jest.fn().mockResolvedValue(profile),
-			listConfig: jest.fn().mockResolvedValue([profile]),
+			loadConfigById: jest.fn().mockResolvedValue({
+				config: { apiProvider: "anthropic", id: "config-id-123" },
+				name: "config-by-id",
+			}),
+			listConfig: jest
+				.fn()
+				.mockResolvedValue([{ name: "config-by-id", id: "config-id-123", apiProvider: "anthropic" }]),
 			setModeConfig: jest.fn(),
 			setModeConfig: jest.fn(),
 			getModeConfigId: jest.fn().mockResolvedValue(undefined),
 			getModeConfigId: jest.fn().mockResolvedValue(undefined),
 		} as any
 		} as any
@@ -682,8 +686,8 @@ describe("ClineProvider", () => {
 		// Should save new config as default for architect mode
 		// Should save new config as default for architect mode
 		expect(provider.providerSettingsManager.setModeConfig).toHaveBeenCalledWith("architect", "config-id-123")
 		expect(provider.providerSettingsManager.setModeConfig).toHaveBeenCalledWith("architect", "config-id-123")
 
 
-		// Ensure the `activateProfile` method was called with the correct ID
-		expect(provider.providerSettingsManager.activateProfile).toHaveBeenCalledWith({ id: "config-id-123" })
+		// Ensure the loadConfigById method was called with the correct ID
+		expect(provider.providerSettingsManager.loadConfigById).toHaveBeenCalledWith("config-id-123")
 	})
 	})
 
 
 	test("handles browserToolEnabled setting", async () => {
 	test("handles browserToolEnabled setting", async () => {
@@ -882,7 +886,7 @@ describe("ClineProvider", () => {
 		})
 		})
 	})
 	})
 
 
-	it("saves mode config when updating API configuration", async () => {
+	test("saves mode config when updating API configuration", async () => {
 		// Setup mock context with mode and config name
 		// Setup mock context with mode and config name
 		mockContext = {
 		mockContext = {
 			...mockContext,
 			...mockContext,
@@ -908,14 +912,12 @@ describe("ClineProvider", () => {
 
 
 		;(provider as any).providerSettingsManager = {
 		;(provider as any).providerSettingsManager = {
 			listConfig: jest.fn().mockResolvedValue([{ name: "test-config", id: "test-id", apiProvider: "anthropic" }]),
 			listConfig: jest.fn().mockResolvedValue([{ name: "test-config", id: "test-id", apiProvider: "anthropic" }]),
-			saveConfig: jest.fn().mockResolvedValue("test-id"),
 			setModeConfig: jest.fn(),
 			setModeConfig: jest.fn(),
 		} as any
 		} as any
 
 
 		// Update API configuration
 		// Update API configuration
 		await messageHandler({
 		await messageHandler({
-			type: "upsertApiConfiguration",
-			text: "test-config",
+			type: "apiConfiguration",
 			apiConfiguration: { apiProvider: "anthropic" },
 			apiConfiguration: { apiProvider: "anthropic" },
 		})
 		})
 
 
@@ -1540,17 +1542,13 @@ describe("ClineProvider", () => {
 			await provider.resolveWebviewView(mockWebviewView)
 			await provider.resolveWebviewView(mockWebviewView)
 		})
 		})
 
 
-		it("loads saved API config when switching modes", async () => {
-			const profile: ProviderSettingsEntry = {
-				name: "saved-config",
-				id: "saved-config-id",
-				apiProvider: "anthropic",
-			}
-
+		test("loads saved API config when switching modes", async () => {
 			;(provider as any).providerSettingsManager = {
 			;(provider as any).providerSettingsManager = {
 				getModeConfigId: jest.fn().mockResolvedValue("saved-config-id"),
 				getModeConfigId: jest.fn().mockResolvedValue("saved-config-id"),
-				listConfig: jest.fn().mockResolvedValue([profile]),
-				activateProfile: jest.fn().mockResolvedValue(profile),
+				listConfig: jest
+					.fn()
+					.mockResolvedValue([{ name: "saved-config", id: "saved-config-id", apiProvider: "anthropic" }]),
+				loadConfig: jest.fn().mockResolvedValue({ apiProvider: "anthropic" }),
 				setModeConfig: jest.fn(),
 				setModeConfig: jest.fn(),
 			} as any
 			} as any
 
 
@@ -1562,7 +1560,7 @@ describe("ClineProvider", () => {
 
 
 			// Verify saved config was loaded
 			// Verify saved config was loaded
 			expect(provider.providerSettingsManager.getModeConfigId).toHaveBeenCalledWith("architect")
 			expect(provider.providerSettingsManager.getModeConfigId).toHaveBeenCalledWith("architect")
-			expect(provider.providerSettingsManager.activateProfile).toHaveBeenCalledWith({ name: "saved-config" })
+			expect(provider.providerSettingsManager.loadConfig).toHaveBeenCalledWith("saved-config")
 			expect(mockContext.globalState.update).toHaveBeenCalledWith("currentApiConfigName", "saved-config")
 			expect(mockContext.globalState.update).toHaveBeenCalledWith("currentApiConfigName", "saved-config")
 
 
 			// Verify state was posted to webview
 			// Verify state was posted to webview
@@ -1678,11 +1676,14 @@ describe("ClineProvider", () => {
 				currentApiConfigName: "test-config",
 				currentApiConfigName: "test-config",
 			} as any)
 			} as any)
 
 
-			// Trigger upsertApiConfiguration
+			// Trigger updateApiConfiguration
 			await messageHandler({
 			await messageHandler({
 				type: "upsertApiConfiguration",
 				type: "upsertApiConfiguration",
 				text: "test-config",
 				text: "test-config",
-				apiConfiguration: { apiProvider: "anthropic", apiKey: "test-key" },
+				apiConfiguration: {
+					apiProvider: "anthropic",
+					apiKey: "test-key",
+				},
 			})
 			})
 
 
 			// Verify error was logged and user was notified
 			// Verify error was logged and user was notified

+ 99 - 46
src/core/webview/webviewMessageHandler.ts

@@ -4,9 +4,9 @@ import pWaitFor from "p-wait-for"
 import * as vscode from "vscode"
 import * as vscode from "vscode"
 
 
 import { ClineProvider } from "./ClineProvider"
 import { ClineProvider } from "./ClineProvider"
-import { Language, ProviderSettings } from "../../schemas"
+import { Language, ApiConfigMeta } from "../../schemas"
 import { changeLanguage, t } from "../../i18n"
 import { changeLanguage, t } from "../../i18n"
-import { RouterName, toRouterName } from "../../shared/api"
+import { ApiConfiguration, RouterName, toRouterName } from "../../shared/api"
 import { supportPrompt } from "../../shared/support-prompt"
 import { supportPrompt } from "../../shared/support-prompt"
 
 
 import { checkoutDiffPayloadSchema, checkoutRestorePayloadSchema, WebviewMessage } from "../../shared/WebviewMessage"
 import { checkoutDiffPayloadSchema, checkoutRestorePayloadSchema, WebviewMessage } from "../../shared/WebviewMessage"
@@ -89,12 +89,19 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
 
 
 					if (currentConfigName) {
 					if (currentConfigName) {
 						if (!(await provider.providerSettingsManager.hasConfig(currentConfigName))) {
 						if (!(await provider.providerSettingsManager.hasConfig(currentConfigName))) {
-							// Current config name not valid, get first config in list.
-							const name = listApiConfig[0]?.name
-							await updateGlobalState("currentApiConfigName", name)
+							// current config name not valid, get first config in list
+							await updateGlobalState("currentApiConfigName", listApiConfig?.[0]?.name)
+							if (listApiConfig?.[0]?.name) {
+								const apiConfig = await provider.providerSettingsManager.loadConfig(
+									listApiConfig?.[0]?.name,
+								)
 
 
-							if (name) {
-								await provider.activateProviderProfile({ name })
+								await Promise.all([
+									updateGlobalState("listApiConfigMeta", listApiConfig),
+									provider.postMessageToWebview({ type: "listApiConfig", listApiConfig }),
+									provider.updateApiConfiguration(apiConfig),
+								])
+								await provider.postStateToWebview()
 								return
 								return
 							}
 							}
 						}
 						}
@@ -126,6 +133,12 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
 			// task. This essentially creates a fresh slate for the new task.
 			// task. This essentially creates a fresh slate for the new task.
 			await provider.initClineWithTask(message.text, message.images)
 			await provider.initClineWithTask(message.text, message.images)
 			break
 			break
+		case "apiConfiguration":
+			if (message.apiConfiguration) {
+				await provider.updateApiConfiguration(message.apiConfiguration)
+			}
+			await provider.postStateToWebview()
+			break
 		case "customInstructions":
 		case "customInstructions":
 			await provider.updateCustomInstructions(message.text)
 			await provider.updateCustomInstructions(message.text)
 			break
 			break
@@ -926,36 +939,45 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
 					const { apiConfiguration, customSupportPrompts, listApiConfigMeta, enhancementApiConfigId } =
 					const { apiConfiguration, customSupportPrompts, listApiConfigMeta, enhancementApiConfigId } =
 						await provider.getState()
 						await provider.getState()
 
 
-					// Try to get enhancement config first, fall back to current config.
-					let configToUse: ProviderSettings = apiConfiguration
-
-					if (enhancementApiConfigId && !!listApiConfigMeta.find(({ id }) => id === enhancementApiConfigId)) {
-						const { name: _, ...providerSettings } = await provider.providerSettingsManager.getProfile({
-							id: enhancementApiConfigId,
-						})
-
-						if (providerSettings.apiProvider) {
-							configToUse = providerSettings
+					// Try to get enhancement config first, fall back to current config
+					let configToUse: ApiConfiguration = apiConfiguration
+					if (enhancementApiConfigId) {
+						const config = listApiConfigMeta?.find((c: ApiConfigMeta) => c.id === enhancementApiConfigId)
+						if (config?.name) {
+							const loadedConfig = await provider.providerSettingsManager.loadConfig(config.name)
+							if (loadedConfig.apiProvider) {
+								configToUse = loadedConfig
+							}
 						}
 						}
 					}
 					}
 
 
 					const enhancedPrompt = await singleCompletionHandler(
 					const enhancedPrompt = await singleCompletionHandler(
 						configToUse,
 						configToUse,
-						supportPrompt.create("ENHANCE", { userInput: message.text }, customSupportPrompts),
+						supportPrompt.create(
+							"ENHANCE",
+							{
+								userInput: message.text,
+							},
+							customSupportPrompts,
+						),
 					)
 					)
 
 
-					// Capture telemetry for prompt enhancement.
+					// Capture telemetry for prompt enhancement
 					const currentCline = provider.getCurrentCline()
 					const currentCline = provider.getCurrentCline()
 					telemetryService.capturePromptEnhanced(currentCline?.taskId)
 					telemetryService.capturePromptEnhanced(currentCline?.taskId)
 
 
-					await provider.postMessageToWebview({ type: "enhancedPrompt", text: enhancedPrompt })
+					await provider.postMessageToWebview({
+						type: "enhancedPrompt",
+						text: enhancedPrompt,
+					})
 				} catch (error) {
 				} catch (error) {
 					provider.log(
 					provider.log(
 						`Error enhancing prompt: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
 						`Error enhancing prompt: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
 					)
 					)
-
 					vscode.window.showErrorMessage(t("common:errors.enhance_prompt"))
 					vscode.window.showErrorMessage(t("common:errors.enhance_prompt"))
-					await provider.postMessageToWebview({ type: "enhancedPrompt" })
+					await provider.postMessageToWebview({
+						type: "enhancedPrompt",
+					})
 				}
 				}
 			}
 			}
 			break
 			break
@@ -1062,7 +1084,7 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
 			break
 			break
 		case "upsertApiConfiguration":
 		case "upsertApiConfiguration":
 			if (message.text && message.apiConfiguration) {
 			if (message.text && message.apiConfiguration) {
-				await provider.upsertProviderProfile(message.text, message.apiConfiguration)
+				await provider.upsertApiConfiguration(message.text, message.apiConfiguration)
 			}
 			}
 			break
 			break
 		case "renameApiConfiguration":
 		case "renameApiConfiguration":
@@ -1074,23 +1096,30 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
 						break
 						break
 					}
 					}
 
 
-					// Load the old configuration to get its ID.
-					const { id } = await provider.providerSettingsManager.getProfile({ name: oldName })
+					// Load the old configuration to get its ID
+					const oldConfig = await provider.providerSettingsManager.loadConfig(oldName)
 
 
-					// Create a new configuration with the new name and old ID.
-					await provider.providerSettingsManager.saveConfig(newName, { ...message.apiConfiguration, id })
+					// Create a new configuration with the same ID
+					const newConfig = {
+						...message.apiConfiguration,
+						id: oldConfig.id, // Preserve the ID
+					}
 
 
-					// Delete the old configuration.
+					// Save with the new name but same ID
+					await provider.providerSettingsManager.saveConfig(newName, newConfig)
 					await provider.providerSettingsManager.deleteConfig(oldName)
 					await provider.providerSettingsManager.deleteConfig(oldName)
 
 
-					// Re-activate to update the global settings related to the
-					// currently activated provider profile.
-					await provider.activateProviderProfile({ name: newName })
+					const listApiConfig = await provider.providerSettingsManager.listConfig()
+
+					// Update listApiConfigMeta first to ensure UI has latest data
+					await updateGlobalState("listApiConfigMeta", listApiConfig)
+					await updateGlobalState("currentApiConfigName", newName)
+
+					await provider.postStateToWebview()
 				} catch (error) {
 				} catch (error) {
 					provider.log(
 					provider.log(
 						`Error rename api configuration: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
 						`Error rename api configuration: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
 					)
 					)
-
 					vscode.window.showErrorMessage(t("common:errors.rename_api_config"))
 					vscode.window.showErrorMessage(t("common:errors.rename_api_config"))
 				}
 				}
 			}
 			}
@@ -1098,7 +1127,16 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
 		case "loadApiConfiguration":
 		case "loadApiConfiguration":
 			if (message.text) {
 			if (message.text) {
 				try {
 				try {
-					await provider.activateProviderProfile({ name: message.text })
+					const apiConfig = await provider.providerSettingsManager.loadConfig(message.text)
+					const listApiConfig = await provider.providerSettingsManager.listConfig()
+
+					await Promise.all([
+						updateGlobalState("listApiConfigMeta", listApiConfig),
+						updateGlobalState("currentApiConfigName", message.text),
+						provider.updateApiConfiguration(apiConfig),
+					])
+
+					await provider.postStateToWebview()
 				} catch (error) {
 				} catch (error) {
 					provider.log(
 					provider.log(
 						`Error load api configuration: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
 						`Error load api configuration: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
@@ -1110,7 +1148,18 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
 		case "loadApiConfigurationById":
 		case "loadApiConfigurationById":
 			if (message.text) {
 			if (message.text) {
 				try {
 				try {
-					await provider.activateProviderProfile({ id: message.text })
+					const { config: apiConfig, name } = await provider.providerSettingsManager.loadConfigById(
+						message.text,
+					)
+					const listApiConfig = await provider.providerSettingsManager.listConfig()
+
+					await Promise.all([
+						updateGlobalState("listApiConfigMeta", listApiConfig),
+						updateGlobalState("currentApiConfigName", name),
+						provider.updateApiConfiguration(apiConfig),
+					])
+
+					await provider.postStateToWebview()
 				} catch (error) {
 				} catch (error) {
 					provider.log(
 					provider.log(
 						`Error load api configuration by ID: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
 						`Error load api configuration by ID: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
@@ -1131,25 +1180,29 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
 					break
 					break
 				}
 				}
 
 
-				const oldName = message.text
+				try {
+					await provider.providerSettingsManager.deleteConfig(message.text)
+					const listApiConfig = await provider.providerSettingsManager.listConfig()
+
+					// Update listApiConfigMeta first to ensure UI has latest data
+					await updateGlobalState("listApiConfigMeta", listApiConfig)
 
 
-				const newName = (await provider.providerSettingsManager.listConfig()).filter(
-					(c) => c.name !== oldName,
-				)[0]?.name
+					// If this was the current config, switch to first available
+					const currentApiConfigName = getGlobalState("currentApiConfigName")
 
 
-				if (!newName) {
-					vscode.window.showErrorMessage(t("common:errors.delete_api_config"))
-					return
-				}
+					if (message.text === currentApiConfigName && listApiConfig?.[0]?.name) {
+						const apiConfig = await provider.providerSettingsManager.loadConfig(listApiConfig[0].name)
+						await Promise.all([
+							updateGlobalState("currentApiConfigName", listApiConfig[0].name),
+							provider.updateApiConfiguration(apiConfig),
+						])
+					}
 
 
-				try {
-					await provider.providerSettingsManager.deleteConfig(oldName)
-					await provider.activateProviderProfile({ name: newName })
+					await provider.postStateToWebview()
 				} catch (error) {
 				} catch (error) {
 					provider.log(
 					provider.log(
 						`Error delete api configuration: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
 						`Error delete api configuration: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`,
 					)
 					)
-
 					vscode.window.showErrorMessage(t("common:errors.delete_api_config"))
 					vscode.window.showErrorMessage(t("common:errors.delete_api_config"))
 				}
 				}
 			}
 			}

+ 86 - 107
src/exports/api.ts

@@ -6,14 +6,7 @@ import * as path from "path"
 import { getWorkspacePath } from "../utils/path"
 import { getWorkspacePath } from "../utils/path"
 import { ClineProvider } from "../core/webview/ClineProvider"
 import { ClineProvider } from "../core/webview/ClineProvider"
 import { openClineInNewTab } from "../activate/registerCommands"
 import { openClineInNewTab } from "../activate/registerCommands"
-import {
-	RooCodeSettings,
-	RooCodeEvents,
-	RooCodeEventName,
-	ProviderSettings,
-	ProviderSettingsEntry,
-	isSecretStateKey,
-} from "../schemas"
+import { RooCodeSettings, RooCodeEvents, RooCodeEventName } from "../schemas"
 import { IpcOrigin, IpcMessageType, TaskCommandName, TaskEvent } from "../schemas/ipc"
 import { IpcOrigin, IpcMessageType, TaskCommandName, TaskEvent } from "../schemas/ipc"
 
 
 import { RooCodeAPI } from "./interface"
 import { RooCodeAPI } from "./interface"
@@ -185,6 +178,91 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
 		await this.sidebarProvider.postMessageToWebview({ type: "invoke", invoke: "secondaryButtonClick" })
 		await this.sidebarProvider.postMessageToWebview({ type: "invoke", invoke: "secondaryButtonClick" })
 	}
 	}
 
 
+	public getConfiguration() {
+		return this.sidebarProvider.getValues()
+	}
+
+	public async setConfiguration(values: RooCodeSettings) {
+		await this.sidebarProvider.setValues(values)
+		await this.sidebarProvider.providerSettingsManager.saveConfig(values.currentApiConfigName || "default", values)
+		await this.sidebarProvider.postStateToWebview()
+	}
+
+	public async createProfile(name: string) {
+		if (!name || !name.trim()) {
+			throw new Error("Profile name cannot be empty")
+		}
+
+		const currentSettings = this.getConfiguration()
+		const profiles = currentSettings.listApiConfigMeta || []
+
+		if (profiles.some((profile) => profile.name === name)) {
+			throw new Error(`A profile with the name "${name}" already exists`)
+		}
+
+		const id = this.sidebarProvider.providerSettingsManager.generateId()
+
+		await this.setConfiguration({
+			...currentSettings,
+			listApiConfigMeta: [
+				...profiles,
+				{
+					id,
+					name: name.trim(),
+					apiProvider: "openai" as const,
+				},
+			],
+		})
+
+		return id
+	}
+
+	public getProfiles() {
+		return (this.getConfiguration().listApiConfigMeta || []).map((profile) => profile.name)
+	}
+
+	public async setActiveProfile(name: string) {
+		const currentSettings = this.getConfiguration()
+		const profiles = currentSettings.listApiConfigMeta || []
+
+		const profile = profiles.find((p) => p.name === name)
+
+		if (!profile) {
+			throw new Error(`Profile with name "${name}" does not exist`)
+		}
+
+		await this.setConfiguration({ ...currentSettings, currentApiConfigName: profile.name })
+	}
+
+	public getActiveProfile() {
+		return this.getConfiguration().currentApiConfigName
+	}
+
+	public async deleteProfile(name: string) {
+		const currentSettings = this.getConfiguration()
+		const profiles = currentSettings.listApiConfigMeta || []
+		const targetIndex = profiles.findIndex((p) => p.name === name)
+
+		if (targetIndex === -1) {
+			throw new Error(`Profile with name "${name}" does not exist`)
+		}
+
+		const profileToDelete = profiles[targetIndex]
+		profiles.splice(targetIndex, 1)
+
+		// If we're deleting the active profile, clear the currentApiConfigName.
+		const newSettings: RooCodeSettings = {
+			...currentSettings,
+			listApiConfigMeta: profiles,
+			currentApiConfigName:
+				currentSettings.currentApiConfigName === profileToDelete.name
+					? undefined
+					: currentSettings.currentApiConfigName,
+		}
+
+		await this.setConfiguration(newSettings)
+	}
+
 	public isReady() {
 	public isReady() {
 		return this.sidebarProvider.viewLaunched
 		return this.sidebarProvider.viewLaunched
 	}
 	}
@@ -250,103 +328,4 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
 			this.logfile = undefined
 			this.logfile = undefined
 		}
 		}
 	}
 	}
-
-	// Global Settings Management
-
-	public getConfiguration(): RooCodeSettings {
-		return Object.fromEntries(
-			Object.entries(this.sidebarProvider.getValues()).filter(([key]) => !isSecretStateKey(key)),
-		)
-	}
-
-	public async setConfiguration(values: RooCodeSettings) {
-		await this.sidebarProvider.contextProxy.setValues(values)
-		await this.sidebarProvider.providerSettingsManager.saveConfig(values.currentApiConfigName || "default", values)
-		await this.sidebarProvider.postStateToWebview()
-	}
-
-	// Provider Profile Management
-
-	public getProfiles(): string[] {
-		return this.sidebarProvider.getProviderProfileEntries().map(({ name }) => name)
-	}
-
-	public getProfileEntry(name: string): ProviderSettingsEntry | undefined {
-		return this.sidebarProvider.getProviderProfileEntry(name)
-	}
-
-	public async createProfile(name: string, profile?: ProviderSettings, activate: boolean = true) {
-		const entry = this.getProfileEntry(name)
-
-		if (entry) {
-			throw new Error(`Profile with name "${name}" already exists`)
-		}
-
-		const id = await this.sidebarProvider.upsertProviderProfile(name, profile ?? {}, activate)
-
-		if (!id) {
-			throw new Error(`Failed to create profile with name "${name}"`)
-		}
-
-		return id
-	}
-
-	public async updateProfile(
-		name: string,
-		profile: ProviderSettings,
-		activate: boolean = true,
-	): Promise<string | undefined> {
-		const entry = this.getProfileEntry(name)
-
-		if (!entry) {
-			throw new Error(`Profile with name "${name}" does not exist`)
-		}
-
-		const id = await this.sidebarProvider.upsertProviderProfile(name, profile, activate)
-
-		if (!id) {
-			throw new Error(`Failed to update profile with name "${name}"`)
-		}
-
-		return id
-	}
-
-	public async upsertProfile(
-		name: string,
-		profile: ProviderSettings,
-		activate: boolean = true,
-	): Promise<string | undefined> {
-		const id = await this.sidebarProvider.upsertProviderProfile(name, profile, activate)
-
-		if (!id) {
-			throw new Error(`Failed to upsert profile with name "${name}"`)
-		}
-
-		return id
-	}
-
-	public async deleteProfile(name: string): Promise<void> {
-		const entry = this.getProfileEntry(name)
-
-		if (!entry) {
-			throw new Error(`Profile with name "${name}" does not exist`)
-		}
-
-		await this.sidebarProvider.deleteProviderProfile(entry)
-	}
-
-	public getActiveProfile(): string | undefined {
-		return this.getConfiguration().currentApiConfigName
-	}
-
-	public async setActiveProfile(name: string): Promise<string | undefined> {
-		const entry = this.getProfileEntry(name)
-
-		if (!entry) {
-			throw new Error(`Profile with name "${name}" does not exist`)
-		}
-
-		await this.sidebarProvider.activateProviderProfile({ name })
-		return this.getActiveProfile()
-	}
 }
 }

+ 16 - 61
src/exports/interface.ts

@@ -1,23 +1,7 @@
 import { EventEmitter } from "events"
 import { EventEmitter } from "events"
 
 
-import type {
-	GlobalSettings,
-	ProviderSettings,
-	ProviderSettingsEntry,
-	ClineMessage,
-	TokenUsage,
-	RooCodeEvents,
-} from "./types"
-
-export type {
-	RooCodeSettings,
-	GlobalSettings,
-	ProviderSettings,
-	ProviderSettingsEntry,
-	ClineMessage,
-	TokenUsage,
-	RooCodeEvents,
-}
+import type { ProviderSettings, GlobalSettings, ClineMessage, TokenUsage, RooCodeEvents } from "./types"
+export type { RooCodeSettings, ProviderSettings, GlobalSettings, ClineMessage, TokenUsage, RooCodeEvents }
 
 
 import { RooCodeEventName } from "../schemas"
 import { RooCodeEventName } from "../schemas"
 export type { RooCodeEventName }
 export type { RooCodeEventName }
@@ -90,11 +74,6 @@ export interface RooCodeAPI extends EventEmitter<RooCodeEvents> {
 	 */
 	 */
 	pressSecondaryButton(): Promise<void>
 	pressSecondaryButton(): Promise<void>
 
 
-	/**
-	 * Returns true if the API is ready to use.
-	 */
-	isReady(): boolean
-
 	/**
 	/**
 	 * Returns the current configuration.
 	 * Returns the current configuration.
 	 * @returns The current configuration.
 	 * @returns The current configuration.
@@ -108,46 +87,30 @@ export interface RooCodeAPI extends EventEmitter<RooCodeEvents> {
 	setConfiguration(values: RooCodeSettings): Promise<void>
 	setConfiguration(values: RooCodeSettings): Promise<void>
 
 
 	/**
 	/**
-	 * Returns a list of all configured profile names
-	 * @returns Array of profile names
-	 */
-	getProfiles(): string[]
-
-	/**
-	 * Returns the profile entry for a given name
+	 * Creates a new API configuration profile
 	 * @param name The name of the profile
 	 * @param name The name of the profile
-	 * @returns The profile entry, or undefined if the profile does not exist
+	 * @returns The ID of the created profile
 	 */
 	 */
-	getProfileEntry(name: string): ProviderSettingsEntry | undefined
+	createProfile(name: string): Promise<string>
 
 
 	/**
 	/**
-	 * Creates a new API configuration profile
-	 * @param name The name of the profile
-	 * @param profile The profile to create; defaults to an empty object
-	 * @param activate Whether to activate the profile after creation; defaults to true
-	 * @returns The ID of the created profile
-	 * @throws Error if the profile already exists
+	 * Returns a list of all configured profile names
+	 * @returns Array of profile names
 	 */
 	 */
-	createProfile(name: string, profile?: ProviderSettings, activate?: boolean): Promise<string>
+	getProfiles(): string[]
 
 
 	/**
 	/**
-	 * Updates an existing API configuration profile
-	 * @param name The name of the profile
-	 * @param profile The profile to update
-	 * @param activate Whether to activate the profile after update; defaults to true
-	 * @returns The ID of the updated profile
+	 * Changes the active API configuration profile
+	 * @param name The name of the profile to activate
 	 * @throws Error if the profile does not exist
 	 * @throws Error if the profile does not exist
 	 */
 	 */
-	updateProfile(name: string, profile: ProviderSettings, activate?: boolean): Promise<string | undefined>
+	setActiveProfile(name: string): Promise<void>
 
 
 	/**
 	/**
-	 * Creates a new API configuration profile or updates an existing one
-	 * @param name The name of the profile
-	 * @param profile The profile to create or update; defaults to an empty object
-	 * @param activate Whether to activate the profile after upsert; defaults to true
-	 * @returns The ID of the upserted profile
+	 * Returns the name of the currently active profile
+	 * @returns The profile name, or undefined if no profile is active
 	 */
 	 */
-	upsertProfile(name: string, profile: ProviderSettings, activate?: boolean): Promise<string | undefined>
+	getActiveProfile(): string | undefined
 
 
 	/**
 	/**
 	 * Deletes a profile by name
 	 * Deletes a profile by name
@@ -157,15 +120,7 @@ export interface RooCodeAPI extends EventEmitter<RooCodeEvents> {
 	deleteProfile(name: string): Promise<void>
 	deleteProfile(name: string): Promise<void>
 
 
 	/**
 	/**
-	 * Returns the name of the currently active profile
-	 * @returns The profile name, or undefined if no profile is active
-	 */
-	getActiveProfile(): string | undefined
-
-	/**
-	 * Changes the active API configuration profile
-	 * @param name The name of the profile to activate
-	 * @throws Error if the profile does not exist
+	 * Returns true if the API is ready to use.
 	 */
 	 */
-	setActiveProfile(name: string): Promise<string | undefined>
+	isReady(): boolean
 }
 }

+ 153 - 210
src/exports/roo-code.d.ts

@@ -1,5 +1,144 @@
 import { EventEmitter } from "events"
 import { EventEmitter } from "events"
 
 
+type ProviderSettings = {
+	apiProvider?:
+		| (
+				| "anthropic"
+				| "glama"
+				| "openrouter"
+				| "bedrock"
+				| "vertex"
+				| "openai"
+				| "ollama"
+				| "vscode-lm"
+				| "lmstudio"
+				| "gemini"
+				| "openai-native"
+				| "mistral"
+				| "deepseek"
+				| "unbound"
+				| "requesty"
+				| "human-relay"
+				| "fake-ai"
+				| "xai"
+				| "groq"
+				| "chutes"
+				| "litellm"
+		  )
+		| undefined
+	apiModelId?: string | undefined
+	apiKey?: string | undefined
+	anthropicBaseUrl?: string | undefined
+	anthropicUseAuthToken?: boolean | undefined
+	glamaModelId?: string | undefined
+	glamaApiKey?: string | undefined
+	openRouterApiKey?: string | undefined
+	openRouterModelId?: string | undefined
+	openRouterBaseUrl?: string | undefined
+	openRouterSpecificProvider?: string | undefined
+	openRouterUseMiddleOutTransform?: boolean | undefined
+	awsAccessKey?: string | undefined
+	awsSecretKey?: string | undefined
+	awsSessionToken?: string | undefined
+	awsRegion?: string | undefined
+	awsUseCrossRegionInference?: boolean | undefined
+	awsUsePromptCache?: boolean | undefined
+	awsProfile?: string | undefined
+	awsUseProfile?: boolean | undefined
+	awsCustomArn?: string | undefined
+	vertexKeyFile?: string | undefined
+	vertexJsonCredentials?: string | undefined
+	vertexProjectId?: string | undefined
+	vertexRegion?: string | undefined
+	openAiBaseUrl?: string | undefined
+	openAiApiKey?: string | undefined
+	openAiLegacyFormat?: boolean | undefined
+	openAiR1FormatEnabled?: boolean | undefined
+	openAiModelId?: string | undefined
+	openAiCustomModelInfo?:
+		| ({
+				maxTokens?: (number | null) | undefined
+				maxThinkingTokens?: (number | null) | undefined
+				contextWindow: number
+				supportsImages?: boolean | undefined
+				supportsComputerUse?: boolean | undefined
+				supportsPromptCache: boolean
+				isPromptCacheOptional?: boolean | undefined
+				inputPrice?: number | undefined
+				outputPrice?: number | undefined
+				cacheWritesPrice?: number | undefined
+				cacheReadsPrice?: number | undefined
+				description?: string | undefined
+				reasoningEffort?: ("low" | "medium" | "high") | undefined
+				thinking?: boolean | undefined
+				minTokensPerCachePoint?: number | undefined
+				maxCachePoints?: number | undefined
+				cachableFields?: string[] | undefined
+				tiers?:
+					| {
+							contextWindow: number
+							inputPrice?: number | undefined
+							outputPrice?: number | undefined
+							cacheWritesPrice?: number | undefined
+							cacheReadsPrice?: number | undefined
+					  }[]
+					| undefined
+		  } | null)
+		| undefined
+	openAiUseAzure?: boolean | undefined
+	azureApiVersion?: string | undefined
+	openAiStreamingEnabled?: boolean | undefined
+	enableReasoningEffort?: boolean | undefined
+	openAiHostHeader?: string | undefined
+	openAiHeaders?:
+		| {
+				[x: string]: string
+		  }
+		| undefined
+	ollamaModelId?: string | undefined
+	ollamaBaseUrl?: string | undefined
+	vsCodeLmModelSelector?:
+		| {
+				vendor?: string | undefined
+				family?: string | undefined
+				version?: string | undefined
+				id?: string | undefined
+		  }
+		| undefined
+	lmStudioModelId?: string | undefined
+	lmStudioBaseUrl?: string | undefined
+	lmStudioDraftModelId?: string | undefined
+	lmStudioSpeculativeDecodingEnabled?: boolean | undefined
+	geminiApiKey?: string | undefined
+	googleGeminiBaseUrl?: string | undefined
+	openAiNativeApiKey?: string | undefined
+	openAiNativeBaseUrl?: string | undefined
+	mistralApiKey?: string | undefined
+	mistralCodestralUrl?: string | undefined
+	deepSeekBaseUrl?: string | undefined
+	deepSeekApiKey?: string | undefined
+	unboundApiKey?: string | undefined
+	unboundModelId?: string | undefined
+	requestyApiKey?: string | undefined
+	requestyModelId?: string | undefined
+	xaiApiKey?: string | undefined
+	groqApiKey?: string | undefined
+	chutesApiKey?: string | undefined
+	litellmBaseUrl?: string | undefined
+	litellmApiKey?: string | undefined
+	litellmModelId?: string | undefined
+	modelMaxTokens?: number | undefined
+	modelMaxThinkingTokens?: number | undefined
+	includeMaxTokens?: boolean | undefined
+	reasoningEffort?: ("low" | "medium" | "high") | undefined
+	promptCachingDisabled?: boolean | undefined
+	diffEnabled?: boolean | undefined
+	fuzzyMatchThreshold?: number | undefined
+	modelTemperature?: (number | null) | undefined
+	rateLimitSeconds?: number | undefined
+	fakeAi?: unknown | undefined
+}
+
 type GlobalSettings = {
 type GlobalSettings = {
 	currentApiConfigName?: string | undefined
 	currentApiConfigName?: string | undefined
 	listApiConfigMeta?:
 	listApiConfigMeta?:
@@ -170,175 +309,6 @@ type GlobalSettings = {
 	historyPreviewCollapsed?: boolean | undefined
 	historyPreviewCollapsed?: boolean | undefined
 }
 }
 
 
-type ProviderSettings = {
-	apiProvider?:
-		| (
-				| "anthropic"
-				| "glama"
-				| "openrouter"
-				| "bedrock"
-				| "vertex"
-				| "openai"
-				| "ollama"
-				| "vscode-lm"
-				| "lmstudio"
-				| "gemini"
-				| "openai-native"
-				| "mistral"
-				| "deepseek"
-				| "unbound"
-				| "requesty"
-				| "human-relay"
-				| "fake-ai"
-				| "xai"
-				| "groq"
-				| "chutes"
-				| "litellm"
-		  )
-		| undefined
-	apiModelId?: string | undefined
-	apiKey?: string | undefined
-	anthropicBaseUrl?: string | undefined
-	anthropicUseAuthToken?: boolean | undefined
-	glamaModelId?: string | undefined
-	glamaApiKey?: string | undefined
-	openRouterApiKey?: string | undefined
-	openRouterModelId?: string | undefined
-	openRouterBaseUrl?: string | undefined
-	openRouterSpecificProvider?: string | undefined
-	openRouterUseMiddleOutTransform?: boolean | undefined
-	awsAccessKey?: string | undefined
-	awsSecretKey?: string | undefined
-	awsSessionToken?: string | undefined
-	awsRegion?: string | undefined
-	awsUseCrossRegionInference?: boolean | undefined
-	awsUsePromptCache?: boolean | undefined
-	awsProfile?: string | undefined
-	awsUseProfile?: boolean | undefined
-	awsCustomArn?: string | undefined
-	vertexKeyFile?: string | undefined
-	vertexJsonCredentials?: string | undefined
-	vertexProjectId?: string | undefined
-	vertexRegion?: string | undefined
-	openAiBaseUrl?: string | undefined
-	openAiApiKey?: string | undefined
-	openAiLegacyFormat?: boolean | undefined
-	openAiR1FormatEnabled?: boolean | undefined
-	openAiModelId?: string | undefined
-	openAiCustomModelInfo?:
-		| ({
-				maxTokens?: (number | null) | undefined
-				maxThinkingTokens?: (number | null) | undefined
-				contextWindow: number
-				supportsImages?: boolean | undefined
-				supportsComputerUse?: boolean | undefined
-				supportsPromptCache: boolean
-				isPromptCacheOptional?: boolean | undefined
-				inputPrice?: number | undefined
-				outputPrice?: number | undefined
-				cacheWritesPrice?: number | undefined
-				cacheReadsPrice?: number | undefined
-				description?: string | undefined
-				reasoningEffort?: ("low" | "medium" | "high") | undefined
-				thinking?: boolean | undefined
-				minTokensPerCachePoint?: number | undefined
-				maxCachePoints?: number | undefined
-				cachableFields?: string[] | undefined
-				tiers?:
-					| {
-							contextWindow: number
-							inputPrice?: number | undefined
-							outputPrice?: number | undefined
-							cacheWritesPrice?: number | undefined
-							cacheReadsPrice?: number | undefined
-					  }[]
-					| undefined
-		  } | null)
-		| undefined
-	openAiUseAzure?: boolean | undefined
-	azureApiVersion?: string | undefined
-	openAiStreamingEnabled?: boolean | undefined
-	enableReasoningEffort?: boolean | undefined
-	openAiHostHeader?: string | undefined
-	openAiHeaders?:
-		| {
-				[x: string]: string
-		  }
-		| undefined
-	ollamaModelId?: string | undefined
-	ollamaBaseUrl?: string | undefined
-	vsCodeLmModelSelector?:
-		| {
-				vendor?: string | undefined
-				family?: string | undefined
-				version?: string | undefined
-				id?: string | undefined
-		  }
-		| undefined
-	lmStudioModelId?: string | undefined
-	lmStudioBaseUrl?: string | undefined
-	lmStudioDraftModelId?: string | undefined
-	lmStudioSpeculativeDecodingEnabled?: boolean | undefined
-	geminiApiKey?: string | undefined
-	googleGeminiBaseUrl?: string | undefined
-	openAiNativeApiKey?: string | undefined
-	openAiNativeBaseUrl?: string | undefined
-	mistralApiKey?: string | undefined
-	mistralCodestralUrl?: string | undefined
-	deepSeekBaseUrl?: string | undefined
-	deepSeekApiKey?: string | undefined
-	unboundApiKey?: string | undefined
-	unboundModelId?: string | undefined
-	requestyApiKey?: string | undefined
-	requestyModelId?: string | undefined
-	fakeAi?: unknown | undefined
-	xaiApiKey?: string | undefined
-	groqApiKey?: string | undefined
-	chutesApiKey?: string | undefined
-	litellmBaseUrl?: string | undefined
-	litellmApiKey?: string | undefined
-	litellmModelId?: string | undefined
-	includeMaxTokens?: boolean | undefined
-	reasoningEffort?: ("low" | "medium" | "high") | undefined
-	promptCachingDisabled?: boolean | undefined
-	diffEnabled?: boolean | undefined
-	fuzzyMatchThreshold?: number | undefined
-	modelTemperature?: (number | null) | undefined
-	rateLimitSeconds?: number | undefined
-	modelMaxTokens?: number | undefined
-	modelMaxThinkingTokens?: number | undefined
-}
-
-type ProviderSettingsEntry = {
-	id: string
-	name: string
-	apiProvider?:
-		| (
-				| "anthropic"
-				| "glama"
-				| "openrouter"
-				| "bedrock"
-				| "vertex"
-				| "openai"
-				| "ollama"
-				| "vscode-lm"
-				| "lmstudio"
-				| "gemini"
-				| "openai-native"
-				| "mistral"
-				| "deepseek"
-				| "unbound"
-				| "requesty"
-				| "human-relay"
-				| "fake-ai"
-				| "xai"
-				| "groq"
-				| "chutes"
-				| "litellm"
-		  )
-		| undefined
-}
-
 type ClineMessage = {
 type ClineMessage = {
 	ts: number
 	ts: number
 	type: "ask" | "say"
 	type: "ask" | "say"
@@ -613,10 +583,6 @@ interface RooCodeAPI extends EventEmitter<RooCodeEvents> {
 	 * Simulates pressing the secondary button in the chat interface.
 	 * Simulates pressing the secondary button in the chat interface.
 	 */
 	 */
 	pressSecondaryButton(): Promise<void>
 	pressSecondaryButton(): Promise<void>
-	/**
-	 * Returns true if the API is ready to use.
-	 */
-	isReady(): boolean
 	/**
 	/**
 	 * Returns the current configuration.
 	 * Returns the current configuration.
 	 * @returns The current configuration.
 	 * @returns The current configuration.
@@ -627,67 +593,44 @@ interface RooCodeAPI extends EventEmitter<RooCodeEvents> {
 	 * @param values An object containing key-value pairs to set.
 	 * @param values An object containing key-value pairs to set.
 	 */
 	 */
 	setConfiguration(values: RooCodeSettings): Promise<void>
 	setConfiguration(values: RooCodeSettings): Promise<void>
-	/**
-	 * Returns a list of all configured profile names
-	 * @returns Array of profile names
-	 */
-	getProfiles(): string[]
-	/**
-	 * Returns the profile entry for a given name
-	 * @param name The name of the profile
-	 * @returns The profile entry, or undefined if the profile does not exist
-	 */
-	getProfileEntry(name: string): ProviderSettingsEntry | undefined
 	/**
 	/**
 	 * Creates a new API configuration profile
 	 * Creates a new API configuration profile
 	 * @param name The name of the profile
 	 * @param name The name of the profile
-	 * @param profile The profile to create; defaults to an empty object
-	 * @param activate Whether to activate the profile after creation; defaults to true
 	 * @returns The ID of the created profile
 	 * @returns The ID of the created profile
-	 * @throws Error if the profile already exists
-	 */
-	createProfile(name: string, profile?: ProviderSettings, activate?: boolean): Promise<string>
-	/**
-	 * Updates an existing API configuration profile
-	 * @param name The name of the profile
-	 * @param profile The profile to update
-	 * @param activate Whether to activate the profile after update; defaults to true
-	 * @returns The ID of the updated profile
-	 * @throws Error if the profile does not exist
 	 */
 	 */
-	updateProfile(name: string, profile: ProviderSettings, activate?: boolean): Promise<string | undefined>
+	createProfile(name: string): Promise<string>
 	/**
 	/**
-	 * Creates a new API configuration profile or updates an existing one
-	 * @param name The name of the profile
-	 * @param profile The profile to create or update; defaults to an empty object
-	 * @param activate Whether to activate the profile after upsert; defaults to true
-	 * @returns The ID of the upserted profile
+	 * Returns a list of all configured profile names
+	 * @returns Array of profile names
 	 */
 	 */
-	upsertProfile(name: string, profile: ProviderSettings, activate?: boolean): Promise<string | undefined>
+	getProfiles(): string[]
 	/**
 	/**
-	 * Deletes a profile by name
-	 * @param name The name of the profile to delete
+	 * Changes the active API configuration profile
+	 * @param name The name of the profile to activate
 	 * @throws Error if the profile does not exist
 	 * @throws Error if the profile does not exist
 	 */
 	 */
-	deleteProfile(name: string): Promise<void>
+	setActiveProfile(name: string): Promise<void>
 	/**
 	/**
 	 * Returns the name of the currently active profile
 	 * Returns the name of the currently active profile
 	 * @returns The profile name, or undefined if no profile is active
 	 * @returns The profile name, or undefined if no profile is active
 	 */
 	 */
 	getActiveProfile(): string | undefined
 	getActiveProfile(): string | undefined
 	/**
 	/**
-	 * Changes the active API configuration profile
-	 * @param name The name of the profile to activate
+	 * Deletes a profile by name
+	 * @param name The name of the profile to delete
 	 * @throws Error if the profile does not exist
 	 * @throws Error if the profile does not exist
 	 */
 	 */
-	setActiveProfile(name: string): Promise<string | undefined>
+	deleteProfile(name: string): Promise<void>
+	/**
+	 * Returns true if the API is ready to use.
+	 */
+	isReady(): boolean
 }
 }
 
 
 export {
 export {
 	type ClineMessage,
 	type ClineMessage,
 	type GlobalSettings,
 	type GlobalSettings,
 	type ProviderSettings,
 	type ProviderSettings,
-	type ProviderSettingsEntry,
 	type RooCodeAPI,
 	type RooCodeAPI,
 	RooCodeEventName,
 	RooCodeEventName,
 	type RooCodeEvents,
 	type RooCodeEvents,

+ 141 - 173
src/exports/types.ts

@@ -1,6 +1,147 @@
 // This file is automatically generated by running `npm run generate-types`
 // This file is automatically generated by running `npm run generate-types`
 // Do not edit it directly.
 // Do not edit it directly.
 
 
+type ProviderSettings = {
+	apiProvider?:
+		| (
+				| "anthropic"
+				| "glama"
+				| "openrouter"
+				| "bedrock"
+				| "vertex"
+				| "openai"
+				| "ollama"
+				| "vscode-lm"
+				| "lmstudio"
+				| "gemini"
+				| "openai-native"
+				| "mistral"
+				| "deepseek"
+				| "unbound"
+				| "requesty"
+				| "human-relay"
+				| "fake-ai"
+				| "xai"
+				| "groq"
+				| "chutes"
+				| "litellm"
+		  )
+		| undefined
+	apiModelId?: string | undefined
+	apiKey?: string | undefined
+	anthropicBaseUrl?: string | undefined
+	anthropicUseAuthToken?: boolean | undefined
+	glamaModelId?: string | undefined
+	glamaApiKey?: string | undefined
+	openRouterApiKey?: string | undefined
+	openRouterModelId?: string | undefined
+	openRouterBaseUrl?: string | undefined
+	openRouterSpecificProvider?: string | undefined
+	openRouterUseMiddleOutTransform?: boolean | undefined
+	awsAccessKey?: string | undefined
+	awsSecretKey?: string | undefined
+	awsSessionToken?: string | undefined
+	awsRegion?: string | undefined
+	awsUseCrossRegionInference?: boolean | undefined
+	awsUsePromptCache?: boolean | undefined
+	awsProfile?: string | undefined
+	awsUseProfile?: boolean | undefined
+	awsCustomArn?: string | undefined
+	vertexKeyFile?: string | undefined
+	vertexJsonCredentials?: string | undefined
+	vertexProjectId?: string | undefined
+	vertexRegion?: string | undefined
+	openAiBaseUrl?: string | undefined
+	openAiApiKey?: string | undefined
+	openAiLegacyFormat?: boolean | undefined
+	openAiR1FormatEnabled?: boolean | undefined
+	openAiModelId?: string | undefined
+	openAiCustomModelInfo?:
+		| ({
+				maxTokens?: (number | null) | undefined
+				maxThinkingTokens?: (number | null) | undefined
+				contextWindow: number
+				supportsImages?: boolean | undefined
+				supportsComputerUse?: boolean | undefined
+				supportsPromptCache: boolean
+				isPromptCacheOptional?: boolean | undefined
+				inputPrice?: number | undefined
+				outputPrice?: number | undefined
+				cacheWritesPrice?: number | undefined
+				cacheReadsPrice?: number | undefined
+				description?: string | undefined
+				reasoningEffort?: ("low" | "medium" | "high") | undefined
+				thinking?: boolean | undefined
+				minTokensPerCachePoint?: number | undefined
+				maxCachePoints?: number | undefined
+				cachableFields?: string[] | undefined
+				tiers?:
+					| {
+							contextWindow: number
+							inputPrice?: number | undefined
+							outputPrice?: number | undefined
+							cacheWritesPrice?: number | undefined
+							cacheReadsPrice?: number | undefined
+					  }[]
+					| undefined
+		  } | null)
+		| undefined
+	openAiUseAzure?: boolean | undefined
+	azureApiVersion?: string | undefined
+	openAiStreamingEnabled?: boolean | undefined
+	enableReasoningEffort?: boolean | undefined
+	openAiHostHeader?: string | undefined
+	openAiHeaders?:
+		| {
+				[x: string]: string
+		  }
+		| undefined
+	ollamaModelId?: string | undefined
+	ollamaBaseUrl?: string | undefined
+	vsCodeLmModelSelector?:
+		| {
+				vendor?: string | undefined
+				family?: string | undefined
+				version?: string | undefined
+				id?: string | undefined
+		  }
+		| undefined
+	lmStudioModelId?: string | undefined
+	lmStudioBaseUrl?: string | undefined
+	lmStudioDraftModelId?: string | undefined
+	lmStudioSpeculativeDecodingEnabled?: boolean | undefined
+	geminiApiKey?: string | undefined
+	googleGeminiBaseUrl?: string | undefined
+	openAiNativeApiKey?: string | undefined
+	openAiNativeBaseUrl?: string | undefined
+	mistralApiKey?: string | undefined
+	mistralCodestralUrl?: string | undefined
+	deepSeekBaseUrl?: string | undefined
+	deepSeekApiKey?: string | undefined
+	unboundApiKey?: string | undefined
+	unboundModelId?: string | undefined
+	requestyApiKey?: string | undefined
+	requestyModelId?: string | undefined
+	xaiApiKey?: string | undefined
+	groqApiKey?: string | undefined
+	chutesApiKey?: string | undefined
+	litellmBaseUrl?: string | undefined
+	litellmApiKey?: string | undefined
+	litellmModelId?: string | undefined
+	modelMaxTokens?: number | undefined
+	modelMaxThinkingTokens?: number | undefined
+	includeMaxTokens?: boolean | undefined
+	reasoningEffort?: ("low" | "medium" | "high") | undefined
+	promptCachingDisabled?: boolean | undefined
+	diffEnabled?: boolean | undefined
+	fuzzyMatchThreshold?: number | undefined
+	modelTemperature?: (number | null) | undefined
+	rateLimitSeconds?: number | undefined
+	fakeAi?: unknown | undefined
+}
+
+export type { ProviderSettings }
+
 type GlobalSettings = {
 type GlobalSettings = {
 	currentApiConfigName?: string | undefined
 	currentApiConfigName?: string | undefined
 	listApiConfigMeta?:
 	listApiConfigMeta?:
@@ -173,179 +314,6 @@ type GlobalSettings = {
 
 
 export type { GlobalSettings }
 export type { GlobalSettings }
 
 
-type ProviderSettings = {
-	apiProvider?:
-		| (
-				| "anthropic"
-				| "glama"
-				| "openrouter"
-				| "bedrock"
-				| "vertex"
-				| "openai"
-				| "ollama"
-				| "vscode-lm"
-				| "lmstudio"
-				| "gemini"
-				| "openai-native"
-				| "mistral"
-				| "deepseek"
-				| "unbound"
-				| "requesty"
-				| "human-relay"
-				| "fake-ai"
-				| "xai"
-				| "groq"
-				| "chutes"
-				| "litellm"
-		  )
-		| undefined
-	apiModelId?: string | undefined
-	apiKey?: string | undefined
-	anthropicBaseUrl?: string | undefined
-	anthropicUseAuthToken?: boolean | undefined
-	glamaModelId?: string | undefined
-	glamaApiKey?: string | undefined
-	openRouterApiKey?: string | undefined
-	openRouterModelId?: string | undefined
-	openRouterBaseUrl?: string | undefined
-	openRouterSpecificProvider?: string | undefined
-	openRouterUseMiddleOutTransform?: boolean | undefined
-	awsAccessKey?: string | undefined
-	awsSecretKey?: string | undefined
-	awsSessionToken?: string | undefined
-	awsRegion?: string | undefined
-	awsUseCrossRegionInference?: boolean | undefined
-	awsUsePromptCache?: boolean | undefined
-	awsProfile?: string | undefined
-	awsUseProfile?: boolean | undefined
-	awsCustomArn?: string | undefined
-	vertexKeyFile?: string | undefined
-	vertexJsonCredentials?: string | undefined
-	vertexProjectId?: string | undefined
-	vertexRegion?: string | undefined
-	openAiBaseUrl?: string | undefined
-	openAiApiKey?: string | undefined
-	openAiLegacyFormat?: boolean | undefined
-	openAiR1FormatEnabled?: boolean | undefined
-	openAiModelId?: string | undefined
-	openAiCustomModelInfo?:
-		| ({
-				maxTokens?: (number | null) | undefined
-				maxThinkingTokens?: (number | null) | undefined
-				contextWindow: number
-				supportsImages?: boolean | undefined
-				supportsComputerUse?: boolean | undefined
-				supportsPromptCache: boolean
-				isPromptCacheOptional?: boolean | undefined
-				inputPrice?: number | undefined
-				outputPrice?: number | undefined
-				cacheWritesPrice?: number | undefined
-				cacheReadsPrice?: number | undefined
-				description?: string | undefined
-				reasoningEffort?: ("low" | "medium" | "high") | undefined
-				thinking?: boolean | undefined
-				minTokensPerCachePoint?: number | undefined
-				maxCachePoints?: number | undefined
-				cachableFields?: string[] | undefined
-				tiers?:
-					| {
-							contextWindow: number
-							inputPrice?: number | undefined
-							outputPrice?: number | undefined
-							cacheWritesPrice?: number | undefined
-							cacheReadsPrice?: number | undefined
-					  }[]
-					| undefined
-		  } | null)
-		| undefined
-	openAiUseAzure?: boolean | undefined
-	azureApiVersion?: string | undefined
-	openAiStreamingEnabled?: boolean | undefined
-	enableReasoningEffort?: boolean | undefined
-	openAiHostHeader?: string | undefined
-	openAiHeaders?:
-		| {
-				[x: string]: string
-		  }
-		| undefined
-	ollamaModelId?: string | undefined
-	ollamaBaseUrl?: string | undefined
-	vsCodeLmModelSelector?:
-		| {
-				vendor?: string | undefined
-				family?: string | undefined
-				version?: string | undefined
-				id?: string | undefined
-		  }
-		| undefined
-	lmStudioModelId?: string | undefined
-	lmStudioBaseUrl?: string | undefined
-	lmStudioDraftModelId?: string | undefined
-	lmStudioSpeculativeDecodingEnabled?: boolean | undefined
-	geminiApiKey?: string | undefined
-	googleGeminiBaseUrl?: string | undefined
-	openAiNativeApiKey?: string | undefined
-	openAiNativeBaseUrl?: string | undefined
-	mistralApiKey?: string | undefined
-	mistralCodestralUrl?: string | undefined
-	deepSeekBaseUrl?: string | undefined
-	deepSeekApiKey?: string | undefined
-	unboundApiKey?: string | undefined
-	unboundModelId?: string | undefined
-	requestyApiKey?: string | undefined
-	requestyModelId?: string | undefined
-	fakeAi?: unknown | undefined
-	xaiApiKey?: string | undefined
-	groqApiKey?: string | undefined
-	chutesApiKey?: string | undefined
-	litellmBaseUrl?: string | undefined
-	litellmApiKey?: string | undefined
-	litellmModelId?: string | undefined
-	includeMaxTokens?: boolean | undefined
-	reasoningEffort?: ("low" | "medium" | "high") | undefined
-	promptCachingDisabled?: boolean | undefined
-	diffEnabled?: boolean | undefined
-	fuzzyMatchThreshold?: number | undefined
-	modelTemperature?: (number | null) | undefined
-	rateLimitSeconds?: number | undefined
-	modelMaxTokens?: number | undefined
-	modelMaxThinkingTokens?: number | undefined
-}
-
-export type { ProviderSettings }
-
-type ProviderSettingsEntry = {
-	id: string
-	name: string
-	apiProvider?:
-		| (
-				| "anthropic"
-				| "glama"
-				| "openrouter"
-				| "bedrock"
-				| "vertex"
-				| "openai"
-				| "ollama"
-				| "vscode-lm"
-				| "lmstudio"
-				| "gemini"
-				| "openai-native"
-				| "mistral"
-				| "deepseek"
-				| "unbound"
-				| "requesty"
-				| "human-relay"
-				| "fake-ai"
-				| "xai"
-				| "groq"
-				| "chutes"
-				| "litellm"
-		  )
-		| undefined
-}
-
-export type { ProviderSettingsEntry }
-
 type ClineMessage = {
 type ClineMessage = {
 	ts: number
 	ts: number
 	type: "ask" | "say"
 	type: "ask" | "say"

+ 49 - 148
src/schemas/index.ts

@@ -135,6 +135,18 @@ export const modelInfoSchema = z.object({
 
 
 export type ModelInfo = z.infer<typeof modelInfoSchema>
 export type ModelInfo = z.infer<typeof modelInfoSchema>
 
 
+/**
+ * ApiConfigMeta
+ */
+
+export const apiConfigMetaSchema = z.object({
+	id: z.string(),
+	name: z.string(),
+	apiProvider: providerNamesSchema.optional(),
+})
+
+export type ApiConfigMeta = z.infer<typeof apiConfigMetaSchema>
+
 /**
 /**
  * HistoryItem
  * HistoryItem
  */
  */
@@ -329,56 +341,27 @@ export type Experiments = z.infer<typeof experimentsSchema>
 
 
 type _AssertExperiments = AssertEqual<Equals<ExperimentId, Keys<Experiments>>>
 type _AssertExperiments = AssertEqual<Equals<ExperimentId, Keys<Experiments>>>
 
 
-/**
- * ProviderSettingsEntry
- */
-
-export const providerSettingsEntrySchema = z.object({
-	id: z.string(),
-	name: z.string(),
-	apiProvider: providerNamesSchema.optional(),
-})
-
-export type ProviderSettingsEntry = z.infer<typeof providerSettingsEntrySchema>
-
 /**
 /**
  * ProviderSettings
  * ProviderSettings
  */
  */
 
 
-const genericProviderSettingsSchema = z.object({
-	includeMaxTokens: z.boolean().optional(),
-	reasoningEffort: reasoningEffortsSchema.optional(),
-	promptCachingDisabled: z.boolean().optional(),
-	diffEnabled: z.boolean().optional(),
-	fuzzyMatchThreshold: z.number().optional(),
-	modelTemperature: z.number().nullish(),
-	rateLimitSeconds: z.number().optional(),
-	// Claude 3.7 Sonnet Thinking
-	modelMaxTokens: z.number().optional(),
-	modelMaxThinkingTokens: z.number().optional(),
-})
-
-const anthropicSchema = z.object({
+export const providerSettingsSchema = z.object({
+	apiProvider: providerNamesSchema.optional(),
+	// Anthropic
 	apiModelId: z.string().optional(),
 	apiModelId: z.string().optional(),
 	apiKey: z.string().optional(),
 	apiKey: z.string().optional(),
 	anthropicBaseUrl: z.string().optional(),
 	anthropicBaseUrl: z.string().optional(),
 	anthropicUseAuthToken: z.boolean().optional(),
 	anthropicUseAuthToken: z.boolean().optional(),
-})
-
-const glamaSchema = z.object({
+	// Glama
 	glamaModelId: z.string().optional(),
 	glamaModelId: z.string().optional(),
 	glamaApiKey: z.string().optional(),
 	glamaApiKey: z.string().optional(),
-})
-
-const openRouterSchema = z.object({
+	// OpenRouter
 	openRouterApiKey: z.string().optional(),
 	openRouterApiKey: z.string().optional(),
 	openRouterModelId: z.string().optional(),
 	openRouterModelId: z.string().optional(),
 	openRouterBaseUrl: z.string().optional(),
 	openRouterBaseUrl: z.string().optional(),
 	openRouterSpecificProvider: z.string().optional(),
 	openRouterSpecificProvider: z.string().optional(),
 	openRouterUseMiddleOutTransform: z.boolean().optional(),
 	openRouterUseMiddleOutTransform: z.boolean().optional(),
-})
-
-const bedrockSchema = z.object({
+	// Amazon Bedrock
 	awsAccessKey: z.string().optional(),
 	awsAccessKey: z.string().optional(),
 	awsSecretKey: z.string().optional(),
 	awsSecretKey: z.string().optional(),
 	awsSessionToken: z.string().optional(),
 	awsSessionToken: z.string().optional(),
@@ -388,16 +371,12 @@ const bedrockSchema = z.object({
 	awsProfile: z.string().optional(),
 	awsProfile: z.string().optional(),
 	awsUseProfile: z.boolean().optional(),
 	awsUseProfile: z.boolean().optional(),
 	awsCustomArn: z.string().optional(),
 	awsCustomArn: z.string().optional(),
-})
-
-const vertexSchema = z.object({
+	// Google Vertex
 	vertexKeyFile: z.string().optional(),
 	vertexKeyFile: z.string().optional(),
 	vertexJsonCredentials: z.string().optional(),
 	vertexJsonCredentials: z.string().optional(),
 	vertexProjectId: z.string().optional(),
 	vertexProjectId: z.string().optional(),
 	vertexRegion: z.string().optional(),
 	vertexRegion: z.string().optional(),
-})
-
-const openAiSchema = z.object({
+	// OpenAI
 	openAiBaseUrl: z.string().optional(),
 	openAiBaseUrl: z.string().optional(),
 	openAiApiKey: z.string().optional(),
 	openAiApiKey: z.string().optional(),
 	openAiLegacyFormat: z.boolean().optional(),
 	openAiLegacyFormat: z.boolean().optional(),
@@ -408,16 +387,12 @@ const openAiSchema = z.object({
 	azureApiVersion: z.string().optional(),
 	azureApiVersion: z.string().optional(),
 	openAiStreamingEnabled: z.boolean().optional(),
 	openAiStreamingEnabled: z.boolean().optional(),
 	enableReasoningEffort: z.boolean().optional(),
 	enableReasoningEffort: z.boolean().optional(),
-	openAiHostHeader: z.string().optional(), // Keep temporarily for backward compatibility during migration.
+	openAiHostHeader: z.string().optional(), // Keep temporarily for backward compatibility during migration
 	openAiHeaders: z.record(z.string(), z.string()).optional(),
 	openAiHeaders: z.record(z.string(), z.string()).optional(),
-})
-
-const ollamaSchema = z.object({
+	// Ollama
 	ollamaModelId: z.string().optional(),
 	ollamaModelId: z.string().optional(),
 	ollamaBaseUrl: z.string().optional(),
 	ollamaBaseUrl: z.string().optional(),
-})
-
-const vsCodeLmSchema = z.object({
+	// VS Code LM
 	vsCodeLmModelSelector: z
 	vsCodeLmModelSelector: z
 		.object({
 		.object({
 			vendor: z.string().optional(),
 			vendor: z.string().optional(),
@@ -426,127 +401,54 @@ const vsCodeLmSchema = z.object({
 			id: z.string().optional(),
 			id: z.string().optional(),
 		})
 		})
 		.optional(),
 		.optional(),
-})
-
-const lmStudioSchema = z.object({
+	// LM Studio
 	lmStudioModelId: z.string().optional(),
 	lmStudioModelId: z.string().optional(),
 	lmStudioBaseUrl: z.string().optional(),
 	lmStudioBaseUrl: z.string().optional(),
 	lmStudioDraftModelId: z.string().optional(),
 	lmStudioDraftModelId: z.string().optional(),
 	lmStudioSpeculativeDecodingEnabled: z.boolean().optional(),
 	lmStudioSpeculativeDecodingEnabled: z.boolean().optional(),
-})
-
-const geminiSchema = z.object({
+	// Gemini
 	geminiApiKey: z.string().optional(),
 	geminiApiKey: z.string().optional(),
 	googleGeminiBaseUrl: z.string().optional(),
 	googleGeminiBaseUrl: z.string().optional(),
-})
-
-const openAiNativeSchema = z.object({
+	// OpenAI Native
 	openAiNativeApiKey: z.string().optional(),
 	openAiNativeApiKey: z.string().optional(),
 	openAiNativeBaseUrl: z.string().optional(),
 	openAiNativeBaseUrl: z.string().optional(),
-})
-
-const mistralSchema = z.object({
+	// Mistral
 	mistralApiKey: z.string().optional(),
 	mistralApiKey: z.string().optional(),
 	mistralCodestralUrl: z.string().optional(),
 	mistralCodestralUrl: z.string().optional(),
-})
-
-const deepSeekSchema = z.object({
+	// DeepSeek
 	deepSeekBaseUrl: z.string().optional(),
 	deepSeekBaseUrl: z.string().optional(),
 	deepSeekApiKey: z.string().optional(),
 	deepSeekApiKey: z.string().optional(),
-})
-
-const unboundSchema = z.object({
+	// Unbound
 	unboundApiKey: z.string().optional(),
 	unboundApiKey: z.string().optional(),
 	unboundModelId: z.string().optional(),
 	unboundModelId: z.string().optional(),
-})
-
-const requestySchema = z.object({
+	// Requesty
 	requestyApiKey: z.string().optional(),
 	requestyApiKey: z.string().optional(),
 	requestyModelId: z.string().optional(),
 	requestyModelId: z.string().optional(),
-})
-
-const humanRelaySchema = z.object({})
-
-const fakeAiSchema = z.object({
-	fakeAi: z.unknown().optional(),
-})
-
-const xaiSchema = z.object({
+	// X.AI (Grok)
 	xaiApiKey: z.string().optional(),
 	xaiApiKey: z.string().optional(),
-})
-
-const groqSchema = z.object({
+	// Groq
 	groqApiKey: z.string().optional(),
 	groqApiKey: z.string().optional(),
-})
-
-const chutesSchema = z.object({
+	// Chutes AI
 	chutesApiKey: z.string().optional(),
 	chutesApiKey: z.string().optional(),
-})
-
-const litellmSchema = z.object({
+	// LiteLLM
 	litellmBaseUrl: z.string().optional(),
 	litellmBaseUrl: z.string().optional(),
 	litellmApiKey: z.string().optional(),
 	litellmApiKey: z.string().optional(),
 	litellmModelId: z.string().optional(),
 	litellmModelId: z.string().optional(),
+	// Claude 3.7 Sonnet Thinking
+	modelMaxTokens: z.number().optional(),
+	modelMaxThinkingTokens: z.number().optional(),
+	// Generic
+	includeMaxTokens: z.boolean().optional(),
+	reasoningEffort: reasoningEffortsSchema.optional(),
+	promptCachingDisabled: z.boolean().optional(),
+	diffEnabled: z.boolean().optional(),
+	fuzzyMatchThreshold: z.number().optional(),
+	modelTemperature: z.number().nullish(),
+	rateLimitSeconds: z.number().optional(),
+	// Fake AI
+	fakeAi: z.unknown().optional(),
 })
 })
 
 
-const defaultSchema = z.object({
-	apiProvider: z.undefined(),
-})
-
-export const providerSettingsSchemaDiscriminated = z
-	.discriminatedUnion("apiProvider", [
-		anthropicSchema.merge(z.object({ apiProvider: z.literal("anthropic") })),
-		glamaSchema.merge(z.object({ apiProvider: z.literal("glama") })),
-		openRouterSchema.merge(z.object({ apiProvider: z.literal("openrouter") })),
-		bedrockSchema.merge(z.object({ apiProvider: z.literal("bedrock") })),
-		vertexSchema.merge(z.object({ apiProvider: z.literal("vertex") })),
-		openAiSchema.merge(z.object({ apiProvider: z.literal("openai") })),
-		ollamaSchema.merge(z.object({ apiProvider: z.literal("ollama") })),
-		vsCodeLmSchema.merge(z.object({ apiProvider: z.literal("vscode-lm") })),
-		lmStudioSchema.merge(z.object({ apiProvider: z.literal("lmstudio") })),
-		geminiSchema.merge(z.object({ apiProvider: z.literal("gemini") })),
-		openAiNativeSchema.merge(z.object({ apiProvider: z.literal("openai-native") })),
-		mistralSchema.merge(z.object({ apiProvider: z.literal("mistral") })),
-		deepSeekSchema.merge(z.object({ apiProvider: z.literal("deepseek") })),
-		unboundSchema.merge(z.object({ apiProvider: z.literal("unbound") })),
-		requestySchema.merge(z.object({ apiProvider: z.literal("requesty") })),
-		humanRelaySchema.merge(z.object({ apiProvider: z.literal("human-relay") })),
-		fakeAiSchema.merge(z.object({ apiProvider: z.literal("fake-ai") })),
-		xaiSchema.merge(z.object({ apiProvider: z.literal("xai") })),
-		groqSchema.merge(z.object({ apiProvider: z.literal("groq") })),
-		chutesSchema.merge(z.object({ apiProvider: z.literal("chutes") })),
-		litellmSchema.merge(z.object({ apiProvider: z.literal("litellm") })),
-		defaultSchema,
-	])
-	.and(genericProviderSettingsSchema)
-
-export const providerSettingsSchema = z
-	.object({
-		apiProvider: providerNamesSchema.optional(),
-	})
-	.merge(anthropicSchema)
-	.merge(glamaSchema)
-	.merge(openRouterSchema)
-	.merge(bedrockSchema)
-	.merge(vertexSchema)
-	.merge(openAiSchema)
-	.merge(ollamaSchema)
-	.merge(vsCodeLmSchema)
-	.merge(lmStudioSchema)
-	.merge(geminiSchema)
-	.merge(openAiNativeSchema)
-	.merge(mistralSchema)
-	.merge(deepSeekSchema)
-	.merge(unboundSchema)
-	.merge(requestySchema)
-	.merge(humanRelaySchema)
-	.merge(fakeAiSchema)
-	.merge(xaiSchema)
-	.merge(groqSchema)
-	.merge(chutesSchema)
-	.merge(litellmSchema)
-	.merge(genericProviderSettingsSchema)
-
 export type ProviderSettings = z.infer<typeof providerSettingsSchema>
 export type ProviderSettings = z.infer<typeof providerSettingsSchema>
 
 
 type ProviderSettingsRecord = Record<Keys<ProviderSettings>, undefined>
 type ProviderSettingsRecord = Record<Keys<ProviderSettings>, undefined>
@@ -655,7 +557,7 @@ export const PROVIDER_SETTINGS_KEYS = Object.keys(providerSettingsRecord) as Key
 
 
 export const globalSettingsSchema = z.object({
 export const globalSettingsSchema = z.object({
 	currentApiConfigName: z.string().optional(),
 	currentApiConfigName: z.string().optional(),
-	listApiConfigMeta: z.array(providerSettingsEntrySchema).optional(),
+	listApiConfigMeta: z.array(apiConfigMetaSchema).optional(),
 	pinnedApiConfigs: z.record(z.string(), z.boolean()).optional(),
 	pinnedApiConfigs: z.record(z.string(), z.boolean()).optional(),
 
 
 	lastShownAnnouncementId: z.string().optional(),
 	lastShownAnnouncementId: z.string().optional(),
@@ -1074,9 +976,8 @@ export type TypeDefinition = {
 }
 }
 
 
 export const typeDefinitions: TypeDefinition[] = [
 export const typeDefinitions: TypeDefinition[] = [
-	{ schema: globalSettingsSchema, identifier: "GlobalSettings" },
 	{ schema: providerSettingsSchema, identifier: "ProviderSettings" },
 	{ schema: providerSettingsSchema, identifier: "ProviderSettings" },
-	{ schema: providerSettingsEntrySchema, identifier: "ProviderSettingsEntry" },
+	{ schema: globalSettingsSchema, identifier: "GlobalSettings" },
 	{ schema: clineMessageSchema, identifier: "ClineMessage" },
 	{ schema: clineMessageSchema, identifier: "ClineMessage" },
 	{ schema: tokenUsageSchema, identifier: "TokenUsage" },
 	{ schema: tokenUsageSchema, identifier: "TokenUsage" },
 	{ schema: rooCodeEventsSchema, identifier: "RooCodeEvents" },
 	{ schema: rooCodeEventsSchema, identifier: "RooCodeEvents" },

+ 5 - 5
src/shared/ExtensionMessage.ts

@@ -2,8 +2,8 @@ import { GitCommit } from "../utils/git"
 
 
 import {
 import {
 	GlobalSettings,
 	GlobalSettings,
-	ProviderSettingsEntry,
-	ProviderSettings,
+	ApiConfigMeta,
+	ProviderSettings as ApiConfiguration,
 	HistoryItem,
 	HistoryItem,
 	ModeConfig,
 	ModeConfig,
 	TelemetrySetting,
 	TelemetrySetting,
@@ -17,7 +17,7 @@ import { McpServer } from "./mcp"
 import { Mode } from "./modes"
 import { Mode } from "./modes"
 import { RouterModels } from "./api"
 import { RouterModels } from "./api"
 
 
-export type { ProviderSettingsEntry, ToolProgressStatus }
+export type { ApiConfigMeta, ToolProgressStatus }
 
 
 export interface LanguageModelChatSelector {
 export interface LanguageModelChatSelector {
 	vendor?: string
 	vendor?: string
@@ -95,7 +95,7 @@ export interface ExtensionMessage {
 	vsCodeLmModels?: { vendor?: string; family?: string; version?: string; id?: string }[]
 	vsCodeLmModels?: { vendor?: string; family?: string; version?: string; id?: string }[]
 	mcpServers?: McpServer[]
 	mcpServers?: McpServer[]
 	commits?: GitCommit[]
 	commits?: GitCommit[]
-	listApiConfig?: ProviderSettingsEntry[]
+	listApiConfig?: ApiConfigMeta[]
 	mode?: Mode
 	mode?: Mode
 	customMode?: ModeConfig
 	customMode?: ModeConfig
 	slug?: string
 	slug?: string
@@ -172,7 +172,7 @@ export type ExtensionState = Pick<
 	version: string
 	version: string
 	clineMessages: ClineMessage[]
 	clineMessages: ClineMessage[]
 	currentTaskItem?: HistoryItem
 	currentTaskItem?: HistoryItem
-	apiConfiguration?: ProviderSettings
+	apiConfiguration?: ApiConfiguration
 	uriScheme?: string
 	uriScheme?: string
 	shouldShowAnnouncement: boolean
 	shouldShowAnnouncement: boolean
 
 

+ 3 - 2
src/shared/WebviewMessage.ts

@@ -1,6 +1,6 @@
 import { z } from "zod"
 import { z } from "zod"
 
 
-import { ProviderSettings } from "./api"
+import { ApiConfiguration } from "./api"
 import { Mode, PromptComponent, ModeConfig } from "./modes"
 import { Mode, PromptComponent, ModeConfig } from "./modes"
 
 
 export type ClineAskResponse = "yesButtonClicked" | "noButtonClicked" | "messageResponse"
 export type ClineAskResponse = "yesButtonClicked" | "noButtonClicked" | "messageResponse"
@@ -11,6 +11,7 @@ export type AudioType = "notification" | "celebration" | "progress_loop"
 
 
 export interface WebviewMessage {
 export interface WebviewMessage {
 	type:
 	type:
+		| "apiConfiguration"
 		| "deleteMultipleTasksWithIds"
 		| "deleteMultipleTasksWithIds"
 		| "currentApiConfigName"
 		| "currentApiConfigName"
 		| "saveApiConfiguration"
 		| "saveApiConfiguration"
@@ -133,7 +134,7 @@ export interface WebviewMessage {
 	text?: string
 	text?: string
 	disabled?: boolean
 	disabled?: boolean
 	askResponse?: ClineAskResponse
 	askResponse?: ClineAskResponse
-	apiConfiguration?: ProviderSettings
+	apiConfiguration?: ApiConfiguration
 	images?: string[]
 	images?: string[]
 	bool?: boolean
 	bool?: boolean
 	value?: number
 	value?: number

+ 6 - 6
src/shared/__tests__/checkExistApiConfig.test.ts

@@ -1,5 +1,5 @@
 import { checkExistKey } from "../checkExistApiConfig"
 import { checkExistKey } from "../checkExistApiConfig"
-import { ProviderSettings } from "../api"
+import { ApiConfiguration } from "../api"
 
 
 describe("checkExistKey", () => {
 describe("checkExistKey", () => {
 	it("should return false for undefined config", () => {
 	it("should return false for undefined config", () => {
@@ -7,19 +7,19 @@ describe("checkExistKey", () => {
 	})
 	})
 
 
 	it("should return false for empty config", () => {
 	it("should return false for empty config", () => {
-		const config: ProviderSettings = {}
+		const config: ApiConfiguration = {}
 		expect(checkExistKey(config)).toBe(false)
 		expect(checkExistKey(config)).toBe(false)
 	})
 	})
 
 
 	it("should return true when one key is defined", () => {
 	it("should return true when one key is defined", () => {
-		const config: ProviderSettings = {
+		const config: ApiConfiguration = {
 			apiKey: "test-key",
 			apiKey: "test-key",
 		}
 		}
 		expect(checkExistKey(config)).toBe(true)
 		expect(checkExistKey(config)).toBe(true)
 	})
 	})
 
 
 	it("should return true when multiple keys are defined", () => {
 	it("should return true when multiple keys are defined", () => {
-		const config: ProviderSettings = {
+		const config: ApiConfiguration = {
 			apiKey: "test-key",
 			apiKey: "test-key",
 			glamaApiKey: "glama-key",
 			glamaApiKey: "glama-key",
 			openRouterApiKey: "openrouter-key",
 			openRouterApiKey: "openrouter-key",
@@ -28,7 +28,7 @@ describe("checkExistKey", () => {
 	})
 	})
 
 
 	it("should return true when only non-key fields are undefined", () => {
 	it("should return true when only non-key fields are undefined", () => {
-		const config: ProviderSettings = {
+		const config: ApiConfiguration = {
 			apiKey: "test-key",
 			apiKey: "test-key",
 			apiProvider: undefined,
 			apiProvider: undefined,
 			anthropicBaseUrl: undefined,
 			anthropicBaseUrl: undefined,
@@ -38,7 +38,7 @@ describe("checkExistKey", () => {
 	})
 	})
 
 
 	it("should return false when all key fields are undefined", () => {
 	it("should return false when all key fields are undefined", () => {
-		const config: ProviderSettings = {
+		const config: ApiConfiguration = {
 			apiKey: undefined,
 			apiKey: undefined,
 			glamaApiKey: undefined,
 			glamaApiKey: undefined,
 			openRouterApiKey: undefined,
 			openRouterApiKey: undefined,

+ 3 - 1
src/shared/api.ts

@@ -1,9 +1,11 @@
 import { ModelInfo, ProviderName, ProviderSettings } from "../schemas"
 import { ModelInfo, ProviderName, ProviderSettings } from "../schemas"
 
 
-export type { ModelInfo, ProviderName, ProviderSettings }
+export type { ModelInfo, ProviderName }
 
 
 export type ApiHandlerOptions = Omit<ProviderSettings, "apiProvider" | "id">
 export type ApiHandlerOptions = Omit<ProviderSettings, "apiProvider" | "id">
 
 
+export type ApiConfiguration = ProviderSettings
+
 // Anthropic
 // Anthropic
 // https://docs.anthropic.com/en/docs/about-claude/models
 // https://docs.anthropic.com/en/docs/about-claude/models
 export type AnthropicModelId = keyof typeof anthropicModels
 export type AnthropicModelId = keyof typeof anthropicModels

+ 4 - 4
src/utils/__tests__/enhance-prompt.test.ts

@@ -1,5 +1,5 @@
 import { singleCompletionHandler } from "../single-completion-handler"
 import { singleCompletionHandler } from "../single-completion-handler"
-import { ProviderSettings } from "../../shared/api"
+import { ApiConfiguration } from "../../shared/api"
 import { buildApiHandler, SingleCompletionHandler } from "../../api"
 import { buildApiHandler, SingleCompletionHandler } from "../../api"
 import { supportPrompt } from "../../shared/support-prompt"
 import { supportPrompt } from "../../shared/support-prompt"
 
 
@@ -9,7 +9,7 @@ jest.mock("../../api", () => ({
 }))
 }))
 
 
 describe("enhancePrompt", () => {
 describe("enhancePrompt", () => {
-	const mockApiConfig: ProviderSettings = {
+	const mockApiConfig: ApiConfiguration = {
 		apiProvider: "openai",
 		apiProvider: "openai",
 		openAiApiKey: "test-key",
 		openAiApiKey: "test-key",
 		openAiBaseUrl: "https://api.openai.com/v1",
 		openAiBaseUrl: "https://api.openai.com/v1",
@@ -69,7 +69,7 @@ describe("enhancePrompt", () => {
 	})
 	})
 
 
 	it("throws error for missing API configuration", async () => {
 	it("throws error for missing API configuration", async () => {
-		await expect(singleCompletionHandler({} as ProviderSettings, "Test prompt")).rejects.toThrow(
+		await expect(singleCompletionHandler({} as ApiConfiguration, "Test prompt")).rejects.toThrow(
 			"No valid API configuration provided",
 			"No valid API configuration provided",
 		)
 		)
 	})
 	})
@@ -94,7 +94,7 @@ describe("enhancePrompt", () => {
 	})
 	})
 
 
 	it("uses appropriate model based on provider", async () => {
 	it("uses appropriate model based on provider", async () => {
-		const openRouterConfig: ProviderSettings = {
+		const openRouterConfig: ApiConfiguration = {
 			apiProvider: "openrouter",
 			apiProvider: "openrouter",
 			openRouterApiKey: "test-key",
 			openRouterApiKey: "test-key",
 			openRouterModelId: "test-model",
 			openRouterModelId: "test-model",

+ 2 - 2
src/utils/single-completion-handler.ts

@@ -1,11 +1,11 @@
-import { ProviderSettings } from "../shared/api"
+import { ApiConfiguration } from "../shared/api"
 import { buildApiHandler, SingleCompletionHandler } from "../api"
 import { buildApiHandler, SingleCompletionHandler } from "../api"
 
 
 /**
 /**
  * Enhances a prompt using the configured API without creating a full Cline instance or task history.
  * Enhances a prompt using the configured API without creating a full Cline instance or task history.
  * This is a lightweight alternative that only uses the API's completion functionality.
  * This is a lightweight alternative that only uses the API's completion functionality.
  */
  */
-export async function singleCompletionHandler(apiConfiguration: ProviderSettings, promptText: string): Promise<string> {
+export async function singleCompletionHandler(apiConfiguration: ApiConfiguration, promptText: string): Promise<string> {
 	if (!promptText) {
 	if (!promptText) {
 		throw new Error("No prompt text provided")
 		throw new Error("No prompt text provided")
 	}
 	}

+ 2 - 2
webview-ui/src/components/chat/__tests__/TaskHeader.test.tsx

@@ -4,7 +4,7 @@ import React from "react"
 import { render, screen } from "@testing-library/react"
 import { render, screen } from "@testing-library/react"
 import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
 import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
 
 
-import { ProviderSettings } from "@roo/shared/api"
+import { ApiConfiguration } from "@roo/shared/api"
 
 
 import TaskHeader, { TaskHeaderProps } from "../TaskHeader"
 import TaskHeader, { TaskHeaderProps } from "../TaskHeader"
 
 
@@ -27,7 +27,7 @@ jest.mock("@src/context/ExtensionStateContext", () => ({
 			apiProvider: "anthropic",
 			apiProvider: "anthropic",
 			apiKey: "test-api-key", // Add relevant fields
 			apiKey: "test-api-key", // Add relevant fields
 			apiModelId: "claude-3-opus-20240229", // Add relevant fields
 			apiModelId: "claude-3-opus-20240229", // Add relevant fields
-		} as ProviderSettings, // Optional: Add type assertion if ProviderSettings is imported
+		} as ApiConfiguration, // Optional: Add type assertion if ApiConfiguration is imported
 		currentTaskItem: null,
 		currentTaskItem: null,
 	}),
 	}),
 }))
 }))

+ 2 - 2
webview-ui/src/components/settings/ApiConfigManager.tsx

@@ -2,7 +2,7 @@ import { memo, useEffect, useRef, useState } from "react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { ChevronsUpDown, Check, X } from "lucide-react"
 import { ChevronsUpDown, Check, X } from "lucide-react"
 
 
-import { ProviderSettingsEntry } from "@roo/shared/ExtensionMessage"
+import { ApiConfigMeta } from "@roo/shared/ExtensionMessage"
 
 
 import { useAppTranslation } from "@/i18n/TranslationContext"
 import { useAppTranslation } from "@/i18n/TranslationContext"
 import { cn } from "@/lib/utils"
 import { cn } from "@/lib/utils"
@@ -25,7 +25,7 @@ import {
 
 
 interface ApiConfigManagerProps {
 interface ApiConfigManagerProps {
 	currentApiConfigName?: string
 	currentApiConfigName?: string
-	listApiConfigMeta?: ProviderSettingsEntry[]
+	listApiConfigMeta?: ApiConfigMeta[]
 	onSelectConfig: (configName: string) => void
 	onSelectConfig: (configName: string) => void
 	onDeleteConfig: (configName: string) => void
 	onDeleteConfig: (configName: string) => void
 	onRenameConfig: (oldName: string, newName: string) => void
 	onRenameConfig: (oldName: string, newName: string) => void

+ 5 - 5
webview-ui/src/components/settings/ApiOptions.tsx

@@ -5,7 +5,7 @@ import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"
 
 
 import {
 import {
 	type ProviderName,
 	type ProviderName,
-	type ProviderSettings,
+	type ApiConfiguration,
 	openRouterDefaultModelId,
 	openRouterDefaultModelId,
 	requestyDefaultModelId,
 	requestyDefaultModelId,
 	glamaDefaultModelId,
 	glamaDefaultModelId,
@@ -56,8 +56,8 @@ import { BedrockCustomArn } from "./providers/BedrockCustomArn"
 
 
 export interface ApiOptionsProps {
 export interface ApiOptionsProps {
 	uriScheme: string | undefined
 	uriScheme: string | undefined
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: <K extends keyof ProviderSettings>(field: K, value: ProviderSettings[K]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: <K extends keyof ApiConfiguration>(field: K, value: ApiConfiguration[K]) => void
 	fromWelcomeView?: boolean
 	fromWelcomeView?: boolean
 	errorMessage: string | undefined
 	errorMessage: string | undefined
 	setErrorMessage: React.Dispatch<React.SetStateAction<string | undefined>>
 	setErrorMessage: React.Dispatch<React.SetStateAction<string | undefined>>
@@ -107,9 +107,9 @@ const ApiOptions = ({
 	const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false)
 	const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false)
 
 
 	const handleInputChange = useCallback(
 	const handleInputChange = useCallback(
-		<K extends keyof ProviderSettings, E>(
+		<K extends keyof ApiConfiguration, E>(
 			field: K,
 			field: K,
-			transform: (event: E) => ProviderSettings[K] = inputEventTransform,
+			transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
 		) =>
 		) =>
 			(event: E | Event) => {
 			(event: E | Event) => {
 				setApiConfigurationField(field, transform(event as E))
 				setApiConfigurationField(field, transform(event as E))

+ 3 - 3
webview-ui/src/components/settings/PromptCachingControl.tsx

@@ -1,12 +1,12 @@
 import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
 
 
-import { ProviderSettings } from "@roo/shared/api"
+import { ApiConfiguration } from "@roo/shared/api"
 
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 
 
 interface PromptCachingControlProps {
 interface PromptCachingControlProps {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: <K extends keyof ProviderSettings>(field: K, value: ProviderSettings[K]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: <K extends keyof ApiConfiguration>(field: K, value: ApiConfiguration[K]) => void
 }
 }
 
 
 export const PromptCachingControl = ({ apiConfiguration, setApiConfigurationField }: PromptCachingControlProps) => {
 export const PromptCachingControl = ({ apiConfiguration, setApiConfigurationField }: PromptCachingControlProps) => {

+ 3 - 3
webview-ui/src/components/settings/ReasoningEffort.tsx

@@ -2,12 +2,12 @@ import { useAppTranslation } from "@/i18n/TranslationContext"
 
 
 import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui"
 import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui"
 
 
-import { ProviderSettings } from "@roo/shared/api"
+import { ApiConfiguration } from "@roo/shared/api"
 import { reasoningEfforts, ReasoningEffort as ReasoningEffortType } from "@roo/schemas"
 import { reasoningEfforts, ReasoningEffort as ReasoningEffortType } from "@roo/schemas"
 
 
 interface ReasoningEffortProps {
 interface ReasoningEffortProps {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: <K extends keyof ProviderSettings>(field: K, value: ProviderSettings[K]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: <K extends keyof ApiConfiguration>(field: K, value: ApiConfiguration[K]) => void
 }
 }
 
 
 export const ReasoningEffort = ({ apiConfiguration, setApiConfigurationField }: ReasoningEffortProps) => {
 export const ReasoningEffort = ({ apiConfiguration, setApiConfigurationField }: ReasoningEffortProps) => {

+ 2 - 2
webview-ui/src/components/settings/SettingsView.tsx

@@ -27,7 +27,7 @@ import {
 
 
 import { ExperimentId } from "@roo/shared/experiments"
 import { ExperimentId } from "@roo/shared/experiments"
 import { TelemetrySetting } from "@roo/shared/TelemetrySetting"
 import { TelemetrySetting } from "@roo/shared/TelemetrySetting"
-import { ProviderSettings } from "@roo/shared/api"
+import { ApiConfiguration } from "@roo/shared/api"
 
 
 import { vscode } from "@/utils/vscode"
 import { vscode } from "@/utils/vscode"
 import { ExtensionStateContextType, useExtensionState } from "@/context/ExtensionStateContext"
 import { ExtensionStateContextType, useExtensionState } from "@/context/ExtensionStateContext"
@@ -195,7 +195,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 	}, [])
 	}, [])
 
 
 	const setApiConfigurationField = useCallback(
 	const setApiConfigurationField = useCallback(
-		<K extends keyof ProviderSettings>(field: K, value: ProviderSettings[K]) => {
+		<K extends keyof ApiConfiguration>(field: K, value: ApiConfiguration[K]) => {
 			setCachedState((prevState) => {
 			setCachedState((prevState) => {
 				if (prevState.apiConfiguration?.[field] === value) {
 				if (prevState.apiConfiguration?.[field] === value) {
 					return prevState
 					return prevState

+ 3 - 3
webview-ui/src/components/settings/ThinkingBudget.tsx

@@ -3,14 +3,14 @@ import { useAppTranslation } from "@/i18n/TranslationContext"
 
 
 import { Slider } from "@/components/ui"
 import { Slider } from "@/components/ui"
 
 
-import { ProviderSettings, ModelInfo } from "@roo/shared/api"
+import { ApiConfiguration, ModelInfo } from "@roo/shared/api"
 
 
 const DEFAULT_MAX_OUTPUT_TOKENS = 16_384
 const DEFAULT_MAX_OUTPUT_TOKENS = 16_384
 const DEFAULT_MAX_THINKING_TOKENS = 8_192
 const DEFAULT_MAX_THINKING_TOKENS = 8_192
 
 
 interface ThinkingBudgetProps {
 interface ThinkingBudgetProps {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: <K extends keyof ProviderSettings>(field: K, value: ProviderSettings[K]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: <K extends keyof ApiConfiguration>(field: K, value: ApiConfiguration[K]) => void
 	modelInfo?: ModelInfo
 	modelInfo?: ModelInfo
 }
 }
 
 

+ 2 - 2
webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx

@@ -3,7 +3,7 @@
 import { render, screen, fireEvent } from "@testing-library/react"
 import { render, screen, fireEvent } from "@testing-library/react"
 import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
 import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
 
 
-import { ProviderSettings } from "@roo/shared/api"
+import { ApiConfiguration } from "@roo/shared/api"
 
 
 import { ExtensionStateContextProvider } from "@/context/ExtensionStateContext"
 import { ExtensionStateContextProvider } from "@/context/ExtensionStateContext"
 import { openAiModelInfoSaneDefaults } from "@roo/shared/api"
 import { openAiModelInfoSaneDefaults } from "@roo/shared/api"
@@ -148,7 +148,7 @@ jest.mock("../DiffSettingsControl", () => ({
 }))
 }))
 
 
 jest.mock("@src/components/ui/hooks/useSelectedModel", () => ({
 jest.mock("@src/components/ui/hooks/useSelectedModel", () => ({
-	useSelectedModel: jest.fn((apiConfiguration: ProviderSettings) => {
+	useSelectedModel: jest.fn((apiConfiguration: ApiConfiguration) => {
 		if (apiConfiguration.apiModelId?.includes("thinking")) {
 		if (apiConfiguration.apiModelId?.includes("thinking")) {
 			return {
 			return {
 				provider: apiConfiguration.apiProvider,
 				provider: apiConfiguration.apiProvider,

+ 5 - 5
webview-ui/src/components/settings/providers/Anthropic.tsx

@@ -2,7 +2,7 @@ import { useCallback, useState } from "react"
 import { Checkbox } from "vscrui"
 import { Checkbox } from "vscrui"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 
 
-import { ProviderSettings } from "@roo/shared/api"
+import { ApiConfiguration } from "@roo/shared/api"
 
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
 import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
@@ -10,8 +10,8 @@ import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
 import { inputEventTransform, noTransform } from "../transforms"
 import { inputEventTransform, noTransform } from "../transforms"
 
 
 type AnthropicProps = {
 type AnthropicProps = {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
 }
 }
 
 
 export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: AnthropicProps) => {
 export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: AnthropicProps) => {
@@ -20,9 +20,9 @@ export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: Anthro
 	const [anthropicBaseUrlSelected, setAnthropicBaseUrlSelected] = useState(!!apiConfiguration?.anthropicBaseUrl)
 	const [anthropicBaseUrlSelected, setAnthropicBaseUrlSelected] = useState(!!apiConfiguration?.anthropicBaseUrl)
 
 
 	const handleInputChange = useCallback(
 	const handleInputChange = useCallback(
-		<K extends keyof ProviderSettings, E>(
+		<K extends keyof ApiConfiguration, E>(
 			field: K,
 			field: K,
-			transform: (event: E) => ProviderSettings[K] = inputEventTransform,
+			transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
 		) =>
 		) =>
 			(event: E | Event) => {
 			(event: E | Event) => {
 				setApiConfigurationField(field, transform(event as E))
 				setApiConfigurationField(field, transform(event as E))

+ 5 - 5
webview-ui/src/components/settings/providers/Bedrock.tsx

@@ -2,7 +2,7 @@ import { useCallback } from "react"
 import { Checkbox } from "vscrui"
 import { Checkbox } from "vscrui"
 import { VSCodeTextField, VSCodeRadio, VSCodeRadioGroup } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeTextField, VSCodeRadio, VSCodeRadioGroup } from "@vscode/webview-ui-toolkit/react"
 
 
-import { ProviderSettings, ModelInfo } from "@roo/shared/api"
+import { ApiConfiguration, ModelInfo } from "@roo/shared/api"
 
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui"
 import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui"
@@ -11,8 +11,8 @@ import { AWS_REGIONS } from "../constants"
 import { inputEventTransform, noTransform } from "../transforms"
 import { inputEventTransform, noTransform } from "../transforms"
 
 
 type BedrockProps = {
 type BedrockProps = {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
 	selectedModelInfo?: ModelInfo
 	selectedModelInfo?: ModelInfo
 }
 }
 
 
@@ -20,9 +20,9 @@ export const Bedrock = ({ apiConfiguration, setApiConfigurationField, selectedMo
 	const { t } = useAppTranslation()
 	const { t } = useAppTranslation()
 
 
 	const handleInputChange = useCallback(
 	const handleInputChange = useCallback(
-		<K extends keyof ProviderSettings, E>(
+		<K extends keyof ApiConfiguration, E>(
 			field: K,
 			field: K,
-			transform: (event: E) => ProviderSettings[K] = inputEventTransform,
+			transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
 		) =>
 		) =>
 			(event: E | Event) => {
 			(event: E | Event) => {
 				setApiConfigurationField(field, transform(event as E))
 				setApiConfigurationField(field, transform(event as E))

+ 3 - 3
webview-ui/src/components/settings/providers/BedrockCustomArn.tsx

@@ -1,14 +1,14 @@
 import { useMemo } from "react"
 import { useMemo } from "react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 
 
-import { ProviderSettings } from "@roo/shared/api"
+import { ApiConfiguration } from "@roo/shared/api"
 
 
 import { validateBedrockArn } from "@src/utils/validate"
 import { validateBedrockArn } from "@src/utils/validate"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 
 
 type BedrockCustomArnProps = {
 type BedrockCustomArnProps = {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
 }
 }
 
 
 export const BedrockCustomArn = ({ apiConfiguration, setApiConfigurationField }: BedrockCustomArnProps) => {
 export const BedrockCustomArn = ({ apiConfiguration, setApiConfigurationField }: BedrockCustomArnProps) => {

+ 5 - 5
webview-ui/src/components/settings/providers/Chutes.tsx

@@ -1,7 +1,7 @@
 import { useCallback } from "react"
 import { useCallback } from "react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 
 
-import { ProviderSettings } from "@roo/shared/api"
+import { ApiConfiguration } from "@roo/shared/api"
 
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
 import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
@@ -9,17 +9,17 @@ import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
 import { inputEventTransform } from "../transforms"
 import { inputEventTransform } from "../transforms"
 
 
 type ChutesProps = {
 type ChutesProps = {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
 }
 }
 
 
 export const Chutes = ({ apiConfiguration, setApiConfigurationField }: ChutesProps) => {
 export const Chutes = ({ apiConfiguration, setApiConfigurationField }: ChutesProps) => {
 	const { t } = useAppTranslation()
 	const { t } = useAppTranslation()
 
 
 	const handleInputChange = useCallback(
 	const handleInputChange = useCallback(
-		<K extends keyof ProviderSettings, E>(
+		<K extends keyof ApiConfiguration, E>(
 			field: K,
 			field: K,
-			transform: (event: E) => ProviderSettings[K] = inputEventTransform,
+			transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
 		) =>
 		) =>
 			(event: E | Event) => {
 			(event: E | Event) => {
 				setApiConfigurationField(field, transform(event as E))
 				setApiConfigurationField(field, transform(event as E))

+ 5 - 5
webview-ui/src/components/settings/providers/DeepSeek.tsx

@@ -1,7 +1,7 @@
 import { useCallback } from "react"
 import { useCallback } from "react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 
 
-import { ProviderSettings } from "@roo/shared/api"
+import { ApiConfiguration } from "@roo/shared/api"
 
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
 import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
@@ -9,17 +9,17 @@ import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
 import { inputEventTransform } from "../transforms"
 import { inputEventTransform } from "../transforms"
 
 
 type DeepSeekProps = {
 type DeepSeekProps = {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
 }
 }
 
 
 export const DeepSeek = ({ apiConfiguration, setApiConfigurationField }: DeepSeekProps) => {
 export const DeepSeek = ({ apiConfiguration, setApiConfigurationField }: DeepSeekProps) => {
 	const { t } = useAppTranslation()
 	const { t } = useAppTranslation()
 
 
 	const handleInputChange = useCallback(
 	const handleInputChange = useCallback(
-		<K extends keyof ProviderSettings, E>(
+		<K extends keyof ApiConfiguration, E>(
 			field: K,
 			field: K,
-			transform: (event: E) => ProviderSettings[K] = inputEventTransform,
+			transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
 		) =>
 		) =>
 			(event: E | Event) => {
 			(event: E | Event) => {
 				setApiConfigurationField(field, transform(event as E))
 				setApiConfigurationField(field, transform(event as E))

+ 5 - 5
webview-ui/src/components/settings/providers/Gemini.tsx

@@ -2,7 +2,7 @@ import { useCallback, useState } from "react"
 import { Checkbox } from "vscrui"
 import { Checkbox } from "vscrui"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 
 
-import { ProviderSettings } from "@roo/shared/api"
+import { ApiConfiguration } from "@roo/shared/api"
 
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
 import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
@@ -10,8 +10,8 @@ import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
 import { inputEventTransform } from "../transforms"
 import { inputEventTransform } from "../transforms"
 
 
 type GeminiProps = {
 type GeminiProps = {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
 }
 }
 
 
 export const Gemini = ({ apiConfiguration, setApiConfigurationField }: GeminiProps) => {
 export const Gemini = ({ apiConfiguration, setApiConfigurationField }: GeminiProps) => {
@@ -22,9 +22,9 @@ export const Gemini = ({ apiConfiguration, setApiConfigurationField }: GeminiPro
 	)
 	)
 
 
 	const handleInputChange = useCallback(
 	const handleInputChange = useCallback(
-		<K extends keyof ProviderSettings, E>(
+		<K extends keyof ApiConfiguration, E>(
 			field: K,
 			field: K,
-			transform: (event: E) => ProviderSettings[K] = inputEventTransform,
+			transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
 		) =>
 		) =>
 			(event: E | Event) => {
 			(event: E | Event) => {
 				setApiConfigurationField(field, transform(event as E))
 				setApiConfigurationField(field, transform(event as E))

+ 5 - 5
webview-ui/src/components/settings/providers/Glama.tsx

@@ -1,7 +1,7 @@
 import { useCallback } from "react"
 import { useCallback } from "react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 
 
-import { ProviderSettings, RouterModels, glamaDefaultModelId } from "@roo/shared/api"
+import { ApiConfiguration, RouterModels, glamaDefaultModelId } from "@roo/shared/api"
 
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { getGlamaAuthUrl } from "@src/oauth/urls"
 import { getGlamaAuthUrl } from "@src/oauth/urls"
@@ -11,8 +11,8 @@ import { inputEventTransform } from "../transforms"
 import { ModelPicker } from "../ModelPicker"
 import { ModelPicker } from "../ModelPicker"
 
 
 type GlamaProps = {
 type GlamaProps = {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
 	routerModels?: RouterModels
 	routerModels?: RouterModels
 	uriScheme?: string
 	uriScheme?: string
 }
 }
@@ -21,9 +21,9 @@ export const Glama = ({ apiConfiguration, setApiConfigurationField, routerModels
 	const { t } = useAppTranslation()
 	const { t } = useAppTranslation()
 
 
 	const handleInputChange = useCallback(
 	const handleInputChange = useCallback(
-		<K extends keyof ProviderSettings, E>(
+		<K extends keyof ApiConfiguration, E>(
 			field: K,
 			field: K,
-			transform: (event: E) => ProviderSettings[K] = inputEventTransform,
+			transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
 		) =>
 		) =>
 			(event: E | Event) => {
 			(event: E | Event) => {
 				setApiConfigurationField(field, transform(event as E))
 				setApiConfigurationField(field, transform(event as E))

+ 5 - 5
webview-ui/src/components/settings/providers/Groq.tsx

@@ -1,7 +1,7 @@
 import { useCallback } from "react"
 import { useCallback } from "react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 
 
-import { ProviderSettings } from "@roo/shared/api"
+import { ApiConfiguration } from "@roo/shared/api"
 
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
 import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
@@ -9,17 +9,17 @@ import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
 import { inputEventTransform } from "../transforms"
 import { inputEventTransform } from "../transforms"
 
 
 type GroqProps = {
 type GroqProps = {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
 }
 }
 
 
 export const Groq = ({ apiConfiguration, setApiConfigurationField }: GroqProps) => {
 export const Groq = ({ apiConfiguration, setApiConfigurationField }: GroqProps) => {
 	const { t } = useAppTranslation()
 	const { t } = useAppTranslation()
 
 
 	const handleInputChange = useCallback(
 	const handleInputChange = useCallback(
-		<K extends keyof ProviderSettings, E>(
+		<K extends keyof ApiConfiguration, E>(
 			field: K,
 			field: K,
-			transform: (event: E) => ProviderSettings[K] = inputEventTransform,
+			transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
 		) =>
 		) =>
 			(event: E | Event) => {
 			(event: E | Event) => {
 				setApiConfigurationField(field, transform(event as E))
 				setApiConfigurationField(field, transform(event as E))

+ 5 - 5
webview-ui/src/components/settings/providers/LMStudio.tsx

@@ -4,7 +4,7 @@ import { Trans } from "react-i18next"
 import { Checkbox } from "vscrui"
 import { Checkbox } from "vscrui"
 import { VSCodeLink, VSCodeRadio, VSCodeRadioGroup, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeLink, VSCodeRadio, VSCodeRadioGroup, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 
 
-import { ProviderSettings } from "@roo/shared/api"
+import { ApiConfiguration } from "@roo/shared/api"
 
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { ExtensionMessage } from "@roo/shared/ExtensionMessage"
 import { ExtensionMessage } from "@roo/shared/ExtensionMessage"
@@ -12,8 +12,8 @@ import { ExtensionMessage } from "@roo/shared/ExtensionMessage"
 import { inputEventTransform } from "../transforms"
 import { inputEventTransform } from "../transforms"
 
 
 type LMStudioProps = {
 type LMStudioProps = {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
 }
 }
 
 
 export const LMStudio = ({ apiConfiguration, setApiConfigurationField }: LMStudioProps) => {
 export const LMStudio = ({ apiConfiguration, setApiConfigurationField }: LMStudioProps) => {
@@ -22,9 +22,9 @@ export const LMStudio = ({ apiConfiguration, setApiConfigurationField }: LMStudi
 	const [lmStudioModels, setLmStudioModels] = useState<string[]>([])
 	const [lmStudioModels, setLmStudioModels] = useState<string[]>([])
 
 
 	const handleInputChange = useCallback(
 	const handleInputChange = useCallback(
-		<K extends keyof ProviderSettings, E>(
+		<K extends keyof ApiConfiguration, E>(
 			field: K,
 			field: K,
-			transform: (event: E) => ProviderSettings[K] = inputEventTransform,
+			transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
 		) =>
 		) =>
 			(event: E | Event) => {
 			(event: E | Event) => {
 				setApiConfigurationField(field, transform(event as E))
 				setApiConfigurationField(field, transform(event as E))

+ 5 - 6
webview-ui/src/components/settings/providers/LiteLLM.tsx

@@ -1,7 +1,7 @@
 import { useCallback } from "react"
 import { useCallback } from "react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 
 
-import { ProviderSettings, RouterModels, litellmDefaultModelId } from "@roo/shared/api"
+import { ApiConfiguration, RouterModels, litellmDefaultModelId } from "@roo/shared/api"
 
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 
 
@@ -9,8 +9,8 @@ import { inputEventTransform } from "../transforms"
 import { ModelPicker } from "../ModelPicker"
 import { ModelPicker } from "../ModelPicker"
 
 
 type LiteLLMProps = {
 type LiteLLMProps = {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
 	routerModels?: RouterModels
 	routerModels?: RouterModels
 }
 }
 
 
@@ -18,9 +18,9 @@ export const LiteLLM = ({ apiConfiguration, setApiConfigurationField, routerMode
 	const { t } = useAppTranslation()
 	const { t } = useAppTranslation()
 
 
 	const handleInputChange = useCallback(
 	const handleInputChange = useCallback(
-		<K extends keyof ProviderSettings, E>(
+		<K extends keyof ApiConfiguration, E>(
 			field: K,
 			field: K,
-			transform: (event: E) => ProviderSettings[K] = inputEventTransform,
+			transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
 		) =>
 		) =>
 			(event: E | Event) => {
 			(event: E | Event) => {
 				setApiConfigurationField(field, transform(event as E))
 				setApiConfigurationField(field, transform(event as E))
@@ -46,7 +46,6 @@ export const LiteLLM = ({ apiConfiguration, setApiConfigurationField, routerMode
 				className="w-full">
 				className="w-full">
 				<label className="block font-medium mb-1">{t("settings:providers.litellmApiKey")}</label>
 				<label className="block font-medium mb-1">{t("settings:providers.litellmApiKey")}</label>
 			</VSCodeTextField>
 			</VSCodeTextField>
-
 			<div className="text-sm text-vscode-descriptionForeground -mt-2">
 			<div className="text-sm text-vscode-descriptionForeground -mt-2">
 				{t("settings:providers.apiKeyStorageNotice")}
 				{t("settings:providers.apiKeyStorageNotice")}
 			</div>
 			</div>

+ 5 - 5
webview-ui/src/components/settings/providers/Mistral.tsx

@@ -1,7 +1,7 @@
 import { useCallback } from "react"
 import { useCallback } from "react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 
 
-import { ProviderSettings, RouterModels, mistralDefaultModelId } from "@roo/shared/api"
+import { ApiConfiguration, RouterModels, mistralDefaultModelId } from "@roo/shared/api"
 
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
 import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
@@ -9,8 +9,8 @@ import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
 import { inputEventTransform } from "../transforms"
 import { inputEventTransform } from "../transforms"
 
 
 type MistralProps = {
 type MistralProps = {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
 	routerModels?: RouterModels
 	routerModels?: RouterModels
 }
 }
 
 
@@ -18,9 +18,9 @@ export const Mistral = ({ apiConfiguration, setApiConfigurationField }: MistralP
 	const { t } = useAppTranslation()
 	const { t } = useAppTranslation()
 
 
 	const handleInputChange = useCallback(
 	const handleInputChange = useCallback(
-		<K extends keyof ProviderSettings, E>(
+		<K extends keyof ApiConfiguration, E>(
 			field: K,
 			field: K,
-			transform: (event: E) => ProviderSettings[K] = inputEventTransform,
+			transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
 		) =>
 		) =>
 			(event: E | Event) => {
 			(event: E | Event) => {
 				setApiConfigurationField(field, transform(event as E))
 				setApiConfigurationField(field, transform(event as E))

+ 5 - 5
webview-ui/src/components/settings/providers/Ollama.tsx

@@ -2,7 +2,7 @@ import { useState, useCallback } from "react"
 import { useEvent } from "react-use"
 import { useEvent } from "react-use"
 import { VSCodeTextField, VSCodeRadioGroup, VSCodeRadio } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeTextField, VSCodeRadioGroup, VSCodeRadio } from "@vscode/webview-ui-toolkit/react"
 
 
-import { ProviderSettings } from "@roo/shared/api"
+import { ApiConfiguration } from "@roo/shared/api"
 import { ExtensionMessage } from "@roo/shared/ExtensionMessage"
 import { ExtensionMessage } from "@roo/shared/ExtensionMessage"
 
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
@@ -10,8 +10,8 @@ import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { inputEventTransform } from "../transforms"
 import { inputEventTransform } from "../transforms"
 
 
 type OllamaProps = {
 type OllamaProps = {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
 }
 }
 
 
 export const Ollama = ({ apiConfiguration, setApiConfigurationField }: OllamaProps) => {
 export const Ollama = ({ apiConfiguration, setApiConfigurationField }: OllamaProps) => {
@@ -20,9 +20,9 @@ export const Ollama = ({ apiConfiguration, setApiConfigurationField }: OllamaPro
 	const [ollamaModels, setOllamaModels] = useState<string[]>([])
 	const [ollamaModels, setOllamaModels] = useState<string[]>([])
 
 
 	const handleInputChange = useCallback(
 	const handleInputChange = useCallback(
-		<K extends keyof ProviderSettings, E>(
+		<K extends keyof ApiConfiguration, E>(
 			field: K,
 			field: K,
-			transform: (event: E) => ProviderSettings[K] = inputEventTransform,
+			transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
 		) =>
 		) =>
 			(event: E | Event) => {
 			(event: E | Event) => {
 				setApiConfigurationField(field, transform(event as E))
 				setApiConfigurationField(field, transform(event as E))

+ 5 - 5
webview-ui/src/components/settings/providers/OpenAI.tsx

@@ -2,7 +2,7 @@ import { useCallback, useState } from "react"
 import { Checkbox } from "vscrui"
 import { Checkbox } from "vscrui"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 
 
-import { ProviderSettings } from "@roo/shared/api"
+import { ApiConfiguration } from "@roo/shared/api"
 
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
 import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
@@ -10,8 +10,8 @@ import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
 import { inputEventTransform } from "../transforms"
 import { inputEventTransform } from "../transforms"
 
 
 type OpenAIProps = {
 type OpenAIProps = {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
 }
 }
 
 
 export const OpenAI = ({ apiConfiguration, setApiConfigurationField }: OpenAIProps) => {
 export const OpenAI = ({ apiConfiguration, setApiConfigurationField }: OpenAIProps) => {
@@ -22,9 +22,9 @@ export const OpenAI = ({ apiConfiguration, setApiConfigurationField }: OpenAIPro
 	)
 	)
 
 
 	const handleInputChange = useCallback(
 	const handleInputChange = useCallback(
-		<K extends keyof ProviderSettings, E>(
+		<K extends keyof ApiConfiguration, E>(
 			field: K,
 			field: K,
-			transform: (event: E) => ProviderSettings[K] = inputEventTransform,
+			transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
 		) =>
 		) =>
 			(event: E | Event) => {
 			(event: E | Event) => {
 				setApiConfigurationField(field, transform(event as E))
 				setApiConfigurationField(field, transform(event as E))

+ 5 - 5
webview-ui/src/components/settings/providers/OpenAICompatible.tsx

@@ -5,7 +5,7 @@ import { VSCodeButton, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { convertHeadersToObject } from "../utils/headers"
 import { convertHeadersToObject } from "../utils/headers"
 
 
 import { ModelInfo, ReasoningEffort as ReasoningEffortType } from "@roo/schemas"
 import { ModelInfo, ReasoningEffort as ReasoningEffortType } from "@roo/schemas"
-import { ProviderSettings, azureOpenAiDefaultApiVersion, openAiModelInfoSaneDefaults } from "@roo/shared/api"
+import { ApiConfiguration, azureOpenAiDefaultApiVersion, openAiModelInfoSaneDefaults } from "@roo/shared/api"
 import { ExtensionMessage } from "@roo/shared/ExtensionMessage"
 import { ExtensionMessage } from "@roo/shared/ExtensionMessage"
 
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
@@ -17,8 +17,8 @@ import { R1FormatSetting } from "../R1FormatSetting"
 import { ReasoningEffort } from "../ReasoningEffort"
 import { ReasoningEffort } from "../ReasoningEffort"
 
 
 type OpenAICompatibleProps = {
 type OpenAICompatibleProps = {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
 }
 }
 
 
 export const OpenAICompatible = ({ apiConfiguration, setApiConfigurationField }: OpenAICompatibleProps) => {
 export const OpenAICompatible = ({ apiConfiguration, setApiConfigurationField }: OpenAICompatibleProps) => {
@@ -81,9 +81,9 @@ export const OpenAICompatible = ({ apiConfiguration, setApiConfigurationField }:
 	}, [customHeaders, setApiConfigurationField])
 	}, [customHeaders, setApiConfigurationField])
 
 
 	const handleInputChange = useCallback(
 	const handleInputChange = useCallback(
-		<K extends keyof ProviderSettings, E>(
+		<K extends keyof ApiConfiguration, E>(
 			field: K,
 			field: K,
-			transform: (event: E) => ProviderSettings[K] = inputEventTransform,
+			transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
 		) =>
 		) =>
 			(event: E | Event) => {
 			(event: E | Event) => {
 				setApiConfigurationField(field, transform(event as E))
 				setApiConfigurationField(field, transform(event as E))

+ 5 - 5
webview-ui/src/components/settings/providers/OpenRouter.tsx

@@ -4,7 +4,7 @@ import { Checkbox } from "vscrui"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { ExternalLinkIcon } from "@radix-ui/react-icons"
 import { ExternalLinkIcon } from "@radix-ui/react-icons"
 
 
-import { ProviderSettings, RouterModels, openRouterDefaultModelId } from "@roo/shared/api"
+import { ApiConfiguration, RouterModels, openRouterDefaultModelId } from "@roo/shared/api"
 
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { getOpenRouterAuthUrl } from "@src/oauth/urls"
 import { getOpenRouterAuthUrl } from "@src/oauth/urls"
@@ -21,8 +21,8 @@ import { ModelPicker } from "../ModelPicker"
 import { OpenRouterBalanceDisplay } from "./OpenRouterBalanceDisplay"
 import { OpenRouterBalanceDisplay } from "./OpenRouterBalanceDisplay"
 
 
 type OpenRouterProps = {
 type OpenRouterProps = {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
 	routerModels?: RouterModels
 	routerModels?: RouterModels
 	selectedModelId: string
 	selectedModelId: string
 	uriScheme: string | undefined
 	uriScheme: string | undefined
@@ -42,9 +42,9 @@ export const OpenRouter = ({
 	const [openRouterBaseUrlSelected, setOpenRouterBaseUrlSelected] = useState(!!apiConfiguration?.openRouterBaseUrl)
 	const [openRouterBaseUrlSelected, setOpenRouterBaseUrlSelected] = useState(!!apiConfiguration?.openRouterBaseUrl)
 
 
 	const handleInputChange = useCallback(
 	const handleInputChange = useCallback(
-		<K extends keyof ProviderSettings, E>(
+		<K extends keyof ApiConfiguration, E>(
 			field: K,
 			field: K,
-			transform: (event: E) => ProviderSettings[K] = inputEventTransform,
+			transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
 		) =>
 		) =>
 			(event: E | Event) => {
 			(event: E | Event) => {
 				setApiConfigurationField(field, transform(event as E))
 				setApiConfigurationField(field, transform(event as E))

+ 5 - 5
webview-ui/src/components/settings/providers/Requesty.tsx

@@ -1,7 +1,7 @@
 import { useCallback, useState } from "react"
 import { useCallback, useState } from "react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 
 
-import { ProviderSettings, RouterModels, requestyDefaultModelId } from "@roo/shared/api"
+import { ApiConfiguration, RouterModels, requestyDefaultModelId } from "@roo/shared/api"
 
 
 import { vscode } from "@src/utils/vscode"
 import { vscode } from "@src/utils/vscode"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
@@ -13,8 +13,8 @@ import { ModelPicker } from "../ModelPicker"
 import { RequestyBalanceDisplay } from "./RequestyBalanceDisplay"
 import { RequestyBalanceDisplay } from "./RequestyBalanceDisplay"
 
 
 type RequestyProps = {
 type RequestyProps = {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
 	routerModels?: RouterModels
 	routerModels?: RouterModels
 	refetchRouterModels: () => void
 	refetchRouterModels: () => void
 }
 }
@@ -30,9 +30,9 @@ export const Requesty = ({
 	const [didRefetch, setDidRefetch] = useState<boolean>()
 	const [didRefetch, setDidRefetch] = useState<boolean>()
 
 
 	const handleInputChange = useCallback(
 	const handleInputChange = useCallback(
-		<K extends keyof ProviderSettings, E>(
+		<K extends keyof ApiConfiguration, E>(
 			field: K,
 			field: K,
-			transform: (event: E) => ProviderSettings[K] = inputEventTransform,
+			transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
 		) =>
 		) =>
 			(event: E | Event) => {
 			(event: E | Event) => {
 				setApiConfigurationField(field, transform(event as E))
 				setApiConfigurationField(field, transform(event as E))

+ 5 - 5
webview-ui/src/components/settings/providers/Unbound.tsx

@@ -1,7 +1,7 @@
 import { useCallback } from "react"
 import { useCallback } from "react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 
 
-import { ProviderSettings, RouterModels, unboundDefaultModelId } from "@roo/shared/api"
+import { ApiConfiguration, RouterModels, unboundDefaultModelId } from "@roo/shared/api"
 
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
 import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
@@ -10,8 +10,8 @@ import { inputEventTransform } from "../transforms"
 import { ModelPicker } from "../ModelPicker"
 import { ModelPicker } from "../ModelPicker"
 
 
 type UnboundProps = {
 type UnboundProps = {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
 	routerModels?: RouterModels
 	routerModels?: RouterModels
 }
 }
 
 
@@ -19,9 +19,9 @@ export const Unbound = ({ apiConfiguration, setApiConfigurationField, routerMode
 	const { t } = useAppTranslation()
 	const { t } = useAppTranslation()
 
 
 	const handleInputChange = useCallback(
 	const handleInputChange = useCallback(
-		<K extends keyof ProviderSettings, E>(
+		<K extends keyof ApiConfiguration, E>(
 			field: K,
 			field: K,
-			transform: (event: E) => ProviderSettings[K] = inputEventTransform,
+			transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
 		) =>
 		) =>
 			(event: E | Event) => {
 			(event: E | Event) => {
 				setApiConfigurationField(field, transform(event as E))
 				setApiConfigurationField(field, transform(event as E))

+ 5 - 5
webview-ui/src/components/settings/providers/VSCodeLM.tsx

@@ -2,7 +2,7 @@ import { useState, useCallback } from "react"
 import { useEvent } from "react-use"
 import { useEvent } from "react-use"
 import { LanguageModelChatSelector } from "vscode"
 import { LanguageModelChatSelector } from "vscode"
 
 
-import { ProviderSettings } from "@roo/shared/api"
+import { ApiConfiguration } from "@roo/shared/api"
 import { ExtensionMessage } from "@roo/shared/ExtensionMessage"
 import { ExtensionMessage } from "@roo/shared/ExtensionMessage"
 
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
@@ -11,8 +11,8 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
 import { inputEventTransform } from "../transforms"
 import { inputEventTransform } from "../transforms"
 
 
 type VSCodeLMProps = {
 type VSCodeLMProps = {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
 }
 }
 
 
 export const VSCodeLM = ({ apiConfiguration, setApiConfigurationField }: VSCodeLMProps) => {
 export const VSCodeLM = ({ apiConfiguration, setApiConfigurationField }: VSCodeLMProps) => {
@@ -21,9 +21,9 @@ export const VSCodeLM = ({ apiConfiguration, setApiConfigurationField }: VSCodeL
 	const [vsCodeLmModels, setVsCodeLmModels] = useState<LanguageModelChatSelector[]>([])
 	const [vsCodeLmModels, setVsCodeLmModels] = useState<LanguageModelChatSelector[]>([])
 
 
 	const handleInputChange = useCallback(
 	const handleInputChange = useCallback(
-		<K extends keyof ProviderSettings, E>(
+		<K extends keyof ApiConfiguration, E>(
 			field: K,
 			field: K,
-			transform: (event: E) => ProviderSettings[K] = inputEventTransform,
+			transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
 		) =>
 		) =>
 			(event: E | Event) => {
 			(event: E | Event) => {
 				setApiConfigurationField(field, transform(event as E))
 				setApiConfigurationField(field, transform(event as E))

+ 5 - 5
webview-ui/src/components/settings/providers/Vertex.tsx

@@ -1,7 +1,7 @@
 import { useCallback } from "react"
 import { useCallback } from "react"
 import { VSCodeLink, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeLink, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 
 
-import { ProviderSettings } from "@roo/shared/api"
+import { ApiConfiguration } from "@roo/shared/api"
 
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui"
 import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui"
@@ -10,17 +10,17 @@ import { inputEventTransform } from "../transforms"
 import { VERTEX_REGIONS } from "../constants"
 import { VERTEX_REGIONS } from "../constants"
 
 
 type VertexProps = {
 type VertexProps = {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
 }
 }
 
 
 export const Vertex = ({ apiConfiguration, setApiConfigurationField }: VertexProps) => {
 export const Vertex = ({ apiConfiguration, setApiConfigurationField }: VertexProps) => {
 	const { t } = useAppTranslation()
 	const { t } = useAppTranslation()
 
 
 	const handleInputChange = useCallback(
 	const handleInputChange = useCallback(
-		<K extends keyof ProviderSettings, E>(
+		<K extends keyof ApiConfiguration, E>(
 			field: K,
 			field: K,
-			transform: (event: E) => ProviderSettings[K] = inputEventTransform,
+			transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
 		) =>
 		) =>
 			(event: E | Event) => {
 			(event: E | Event) => {
 				setApiConfigurationField(field, transform(event as E))
 				setApiConfigurationField(field, transform(event as E))

+ 5 - 5
webview-ui/src/components/settings/providers/XAI.tsx

@@ -1,7 +1,7 @@
 import { useCallback } from "react"
 import { useCallback } from "react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
 
 
-import { ProviderSettings } from "@roo/shared/api"
+import { ApiConfiguration } from "@roo/shared/api"
 
 
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
 import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
@@ -9,17 +9,17 @@ import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"
 import { inputEventTransform } from "../transforms"
 import { inputEventTransform } from "../transforms"
 
 
 type XAIProps = {
 type XAIProps = {
-	apiConfiguration: ProviderSettings
-	setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
+	apiConfiguration: ApiConfiguration
+	setApiConfigurationField: (field: keyof ApiConfiguration, value: ApiConfiguration[keyof ApiConfiguration]) => void
 }
 }
 
 
 export const XAI = ({ apiConfiguration, setApiConfigurationField }: XAIProps) => {
 export const XAI = ({ apiConfiguration, setApiConfigurationField }: XAIProps) => {
 	const { t } = useAppTranslation()
 	const { t } = useAppTranslation()
 
 
 	const handleInputChange = useCallback(
 	const handleInputChange = useCallback(
-		<K extends keyof ProviderSettings, E>(
+		<K extends keyof ApiConfiguration, E>(
 			field: K,
 			field: K,
-			transform: (event: E) => ProviderSettings[K] = inputEventTransform,
+			transform: (event: E) => ApiConfiguration[K] = inputEventTransform,
 		) =>
 		) =>
 			(event: E | Event) => {
 			(event: E | Event) => {
 				setApiConfigurationField(field, transform(event as E))
 				setApiConfigurationField(field, transform(event as E))

+ 3 - 3
webview-ui/src/components/ui/hooks/useSelectedModel.ts

@@ -1,6 +1,6 @@
 import {
 import {
 	type ProviderName,
 	type ProviderName,
-	type ProviderSettings,
+	type ApiConfiguration,
 	type RouterModels,
 	type RouterModels,
 	type ModelInfo,
 	type ModelInfo,
 	anthropicDefaultModelId,
 	anthropicDefaultModelId,
@@ -35,7 +35,7 @@ import {
 
 
 import { useRouterModels } from "./useRouterModels"
 import { useRouterModels } from "./useRouterModels"
 
 
-export const useSelectedModel = (apiConfiguration?: ProviderSettings) => {
+export const useSelectedModel = (apiConfiguration?: ApiConfiguration) => {
 	const { data: routerModels, isLoading, isError } = useRouterModels()
 	const { data: routerModels, isLoading, isError } = useRouterModels()
 	const provider = apiConfiguration?.apiProvider || "anthropic"
 	const provider = apiConfiguration?.apiProvider || "anthropic"
 
 
@@ -53,7 +53,7 @@ function getSelectedModel({
 	routerModels,
 	routerModels,
 }: {
 }: {
 	provider: ProviderName
 	provider: ProviderName
-	apiConfiguration: ProviderSettings
+	apiConfiguration: ApiConfiguration
 	routerModels: RouterModels
 	routerModels: RouterModels
 }): { id: string; info: ModelInfo } {
 }): { id: string; info: ModelInfo } {
 	switch (provider) {
 	switch (provider) {

+ 7 - 9
webview-ui/src/context/ExtensionStateContext.tsx

@@ -1,8 +1,9 @@
 import React, { createContext, useCallback, useContext, useEffect, useState } from "react"
 import React, { createContext, useCallback, useContext, useEffect, useState } from "react"
 import { useEvent } from "react-use"
 import { useEvent } from "react-use"
-
-import { ProviderSettingsEntry, ExtensionMessage, ExtensionState } from "@roo/shared/ExtensionMessage"
-import { ProviderSettings } from "@roo/shared/api"
+import { ApiConfigMeta, ExtensionMessage, ExtensionState } from "@roo/shared/ExtensionMessage"
+import { ApiConfiguration } from "@roo/shared/api"
+import { vscode } from "@src/utils/vscode"
+import { convertTextMateToHljs } from "@src/utils/textMateToHljs"
 import { findLastIndex } from "@roo/shared/array"
 import { findLastIndex } from "@roo/shared/array"
 import { McpServer } from "@roo/shared/mcp"
 import { McpServer } from "@roo/shared/mcp"
 import { checkExistKey } from "@roo/shared/checkExistApiConfig"
 import { checkExistKey } from "@roo/shared/checkExistApiConfig"
@@ -11,9 +12,6 @@ import { CustomSupportPrompts } from "@roo/shared/support-prompt"
 import { experimentDefault, ExperimentId } from "@roo/shared/experiments"
 import { experimentDefault, ExperimentId } from "@roo/shared/experiments"
 import { TelemetrySetting } from "@roo/shared/TelemetrySetting"
 import { TelemetrySetting } from "@roo/shared/TelemetrySetting"
 
 
-import { vscode } from "@src/utils/vscode"
-import { convertTextMateToHljs } from "@src/utils/textMateToHljs"
-
 export interface ExtensionStateContextType extends ExtensionState {
 export interface ExtensionStateContextType extends ExtensionState {
 	historyPreviewCollapsed?: boolean // Add the new state property
 	historyPreviewCollapsed?: boolean // Add the new state property
 	didHydrateState: boolean
 	didHydrateState: boolean
@@ -24,7 +22,7 @@ export interface ExtensionStateContextType extends ExtensionState {
 	currentCheckpoint?: string
 	currentCheckpoint?: string
 	filePaths: string[]
 	filePaths: string[]
 	openedTabs: Array<{ label: string; isActive: boolean; path?: string }>
 	openedTabs: Array<{ label: string; isActive: boolean; path?: string }>
-	setApiConfiguration: (config: ProviderSettings) => void
+	setApiConfiguration: (config: ApiConfiguration) => void
 	setCustomInstructions: (value?: string) => void
 	setCustomInstructions: (value?: string) => void
 	setAlwaysAllowReadOnly: (value: boolean) => void
 	setAlwaysAllowReadOnly: (value: boolean) => void
 	setAlwaysAllowReadOnlyOutsideWorkspace: (value: boolean) => void
 	setAlwaysAllowReadOnlyOutsideWorkspace: (value: boolean) => void
@@ -67,7 +65,7 @@ export interface ExtensionStateContextType extends ExtensionState {
 	requestDelaySeconds: number
 	requestDelaySeconds: number
 	setRequestDelaySeconds: (value: number) => void
 	setRequestDelaySeconds: (value: number) => void
 	setCurrentApiConfigName: (value: string) => void
 	setCurrentApiConfigName: (value: string) => void
-	setListApiConfigMeta: (value: ProviderSettingsEntry[]) => void
+	setListApiConfigMeta: (value: ApiConfigMeta[]) => void
 	mode: Mode
 	mode: Mode
 	setMode: (value: Mode) => void
 	setMode: (value: Mode) => void
 	setCustomModePrompts: (value: CustomModePrompts) => void
 	setCustomModePrompts: (value: CustomModePrompts) => void
@@ -184,7 +182,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 	const [currentCheckpoint, setCurrentCheckpoint] = useState<string>()
 	const [currentCheckpoint, setCurrentCheckpoint] = useState<string>()
 
 
 	const setListApiConfigMeta = useCallback(
 	const setListApiConfigMeta = useCallback(
-		(value: ProviderSettingsEntry[]) => setState((prevState) => ({ ...prevState, listApiConfigMeta: value })),
+		(value: ApiConfigMeta[]) => setState((prevState) => ({ ...prevState, listApiConfigMeta: value })),
 		[],
 		[],
 	)
 	)
 
 

+ 5 - 5
webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx

@@ -1,16 +1,16 @@
-// npx jest src/context/__tests__/ExtensionStateContext.test.tsx
+// cd webview-ui && npx jest src/context/__tests__/ExtensionStateContext.test.tsx
 
 
 import { render, screen, act } from "@testing-library/react"
 import { render, screen, act } from "@testing-library/react"
 
 
 import { ExtensionState } from "@roo/shared/ExtensionMessage"
 import { ExtensionState } from "@roo/shared/ExtensionMessage"
 import { ExtensionStateContextProvider, useExtensionState, mergeExtensionState } from "../ExtensionStateContext"
 import { ExtensionStateContextProvider, useExtensionState, mergeExtensionState } from "../ExtensionStateContext"
 import { ExperimentId } from "@roo/shared/experiments"
 import { ExperimentId } from "@roo/shared/experiments"
-import { ProviderSettings } from "@roo/shared/api"
+import { ApiConfiguration } from "@roo/shared/api"
 
 
+// Test component that consumes the context
 const TestComponent = () => {
 const TestComponent = () => {
 	const { allowedCommands, setAllowedCommands, soundEnabled, showRooIgnoredFiles, setShowRooIgnoredFiles } =
 	const { allowedCommands, setAllowedCommands, soundEnabled, showRooIgnoredFiles, setShowRooIgnoredFiles } =
 		useExtensionState()
 		useExtensionState()
-
 	return (
 	return (
 		<div>
 		<div>
 			<div data-testid="allowed-commands">{JSON.stringify(allowedCommands)}</div>
 			<div data-testid="allowed-commands">{JSON.stringify(allowedCommands)}</div>
@@ -26,9 +26,9 @@ const TestComponent = () => {
 	)
 	)
 }
 }
 
 
+// Test component for API configuration
 const ApiConfigTestComponent = () => {
 const ApiConfigTestComponent = () => {
 	const { apiConfiguration, setApiConfiguration } = useExtensionState()
 	const { apiConfiguration, setApiConfiguration } = useExtensionState()
-
 	return (
 	return (
 		<div>
 		<div>
 			<div data-testid="api-configuration">{JSON.stringify(apiConfiguration)}</div>
 			<div data-testid="api-configuration">{JSON.stringify(apiConfiguration)}</div>
@@ -197,7 +197,7 @@ describe("mergeExtensionState", () => {
 			customModes: [],
 			customModes: [],
 			maxOpenTabsContext: 20,
 			maxOpenTabsContext: 20,
 			maxWorkspaceFiles: 100,
 			maxWorkspaceFiles: 100,
-			apiConfiguration: { providerId: "openrouter" } as ProviderSettings,
+			apiConfiguration: { providerId: "openrouter" } as ApiConfiguration,
 			telemetrySetting: "unset",
 			telemetrySetting: "unset",
 			showRooIgnoredFiles: true,
 			showRooIgnoredFiles: true,
 			renderContext: "sidebar",
 			renderContext: "sidebar",

+ 3 - 3
webview-ui/src/utils/validate.ts

@@ -1,8 +1,8 @@
 import i18next from "i18next"
 import i18next from "i18next"
 
 
-import { ProviderSettings, isRouterName, RouterModels } from "@roo/shared/api"
+import { ApiConfiguration, isRouterName, RouterModels } from "@roo/shared/api"
 
 
-export function validateApiConfiguration(apiConfiguration: ProviderSettings): string | undefined {
+export function validateApiConfiguration(apiConfiguration: ApiConfiguration): string | undefined {
 	switch (apiConfiguration.apiProvider) {
 	switch (apiConfiguration.apiProvider) {
 		case "openrouter":
 		case "openrouter":
 			if (!apiConfiguration.openRouterApiKey) {
 			if (!apiConfiguration.openRouterApiKey) {
@@ -118,7 +118,7 @@ export function validateBedrockArn(arn: string, region?: string) {
 	return { isValid: true, arnRegion, errorMessage: undefined }
 	return { isValid: true, arnRegion, errorMessage: undefined }
 }
 }
 
 
-export function validateModelId(apiConfiguration: ProviderSettings, routerModels?: RouterModels): string | undefined {
+export function validateModelId(apiConfiguration: ApiConfiguration, routerModels?: RouterModels): string | undefined {
 	const provider = apiConfiguration.apiProvider ?? ""
 	const provider = apiConfiguration.apiProvider ?? ""
 
 
 	if (!isRouterName(provider)) {
 	if (!isRouterName(provider)) {