Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions packages/types/src/providers/roo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { ModelInfo } from "../model.js"

export type RooModelId =
| "xai/grok-code-fast-1"
| "roo/code-supernova"
| "roo/code-supernova-1-million"
| "xai/grok-4-fast"
| "deepseek/deepseek-chat-v3.1"

Expand All @@ -19,15 +19,15 @@ export const rooModels = {
description:
"A reasoning model that is blazing fast and excels at agentic coding, accessible for free through Roo Code Cloud for a limited time. (Note: the free prompts and completions are logged by xAI and used to improve the model.)",
},
"roo/code-supernova": {
maxTokens: 16_384,
contextWindow: 200_000,
"roo/code-supernova-1-million": {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: New ID and limits look good. Ensure any user-facing docs/help listing model IDs are updated to 'roo/code-supernova-1-million' to avoid confusion.

maxTokens: 30_000,
contextWindow: 1_000_000,
supportsImages: true,
supportsPromptCache: true,
inputPrice: 0,
outputPrice: 0,
description:
"A versatile agentic coding stealth model that supports image inputs, accessible for free through Roo Code Cloud for a limited time. (Note: the free prompts and completions are logged by the model provider and used to improve the model.)",
"A versatile agentic coding stealth model with a 1M token context window that supports image inputs, accessible for free through Roo Code Cloud for a limited time. (Note: the free prompts and completions are logged by the model provider and used to improve the model.)",
},
"xai/grok-4-fast": {
maxTokens: 30_000,
Expand Down
56 changes: 56 additions & 0 deletions src/core/config/ProviderSettingsManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,24 @@ import {
ProviderSettingsEntry,
DEFAULT_CONSECUTIVE_MISTAKE_LIMIT,
getModelId,
type ProviderName,
type RooModelId,
} from "@roo-code/types"
import { TelemetryService } from "@roo-code/telemetry"

import { Mode, modes } from "../../shared/modes"

// Type-safe model migrations mapping
type ModelMigrations = {
[K in ProviderName]?: Record<string, string>
}

const MODEL_MIGRATIONS: ModelMigrations = {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The migration map is typed as Record<string, string>. Narrow to RooModelId keys/values to catch typos at compile time.

Suggested change
const MODEL_MIGRATIONS: ModelMigrations = {
type ModelMigrations = {
[K in ProviderName]?: Partial<Record<RooModelId, RooModelId>>
}
const MODEL_MIGRATIONS: ModelMigrations = {
roo: {
'roo/code-supernova': 'roo/code-supernova-1-million',
},
} as const

roo: {
"roo/code-supernova": "roo/code-supernova-1-million" as RooModelId,
},
} as const satisfies ModelMigrations

export interface SyncCloudProfilesResult {
hasChanges: boolean
activeProfileChanged: boolean
Expand Down Expand Up @@ -108,6 +121,11 @@ export class ProviderSettingsManager {
isDirty = true
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Migrations run on initialize(). Consider applying the same normalization step wherever providerProfiles are mutated (e.g., save/import) to keep configs normalized after startup.

}

// Apply model migrations for all providers
if (this.applyModelMigrations(providerProfiles)) {
isDirty = true
}

// Ensure all configs have IDs.
for (const [_name, apiConfig] of Object.entries(providerProfiles.apiConfigs)) {
if (!apiConfig.id) {
Expand Down Expand Up @@ -275,6 +293,44 @@ export class ProviderSettingsManager {
}
}

/**
* Apply model migrations for all providers
* Returns true if any migrations were applied
*/
private applyModelMigrations(providerProfiles: ProviderProfiles): boolean {
let migrated = false

try {
for (const [_name, apiConfig] of Object.entries(providerProfiles.apiConfigs)) {
// Skip configs without provider or model ID
if (!apiConfig.apiProvider || !apiConfig.apiModelId) {
continue
}

// Check if this provider has migrations (with type safety)
const provider = apiConfig.apiProvider as ProviderName
const providerMigrations = MODEL_MIGRATIONS[provider]
if (!providerMigrations) {
continue
}

// Check if the current model ID needs migration
const newModelId = providerMigrations[apiConfig.apiModelId]
if (newModelId && newModelId !== apiConfig.apiModelId) {
console.log(
`[ModelMigration] Migrating ${apiConfig.apiProvider} model from ${apiConfig.apiModelId} to ${newModelId}`,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Prefer project logger/telemetry over console.log/console.error to reduce noise and centralize diagnostics. Consider emitting a dedicated event for model migrations.

)
apiConfig.apiModelId = newModelId
migrated = true
}
}
} catch (error) {
console.error(`[ModelMigration] Failed to apply model migrations:`, error)
}

return migrated
}

/**
* Clean model ID by removing prefix before "/"
*/
Expand Down
115 changes: 115 additions & 0 deletions src/core/config/__tests__/ProviderSettingsManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,121 @@ describe("ProviderSettingsManager", () => {
expect(storedConfig.migrations.todoListEnabledMigrated).toEqual(true)
})

it("should apply model migrations for all providers", async () => {
mockSecrets.get.mockResolvedValue(
JSON.stringify({
currentApiConfigName: "default",
apiConfigs: {
default: {
config: {},
id: "default",
apiProvider: "roo",
apiModelId: "roo/code-supernova", // Old model ID
},
test: {
apiProvider: "roo",
apiModelId: "roo/code-supernova", // Old model ID
},
existing: {
apiProvider: "roo",
apiModelId: "roo/code-supernova-1-million", // Already migrated
},
otherProvider: {
apiProvider: "anthropic",
apiModelId: "roo/code-supernova", // Should not be migrated (different provider)
},
noProvider: {
id: "no-provider",
apiModelId: "roo/code-supernova", // Should not be migrated (no provider)
},
},
migrations: {
rateLimitSecondsMigrated: true,
diffSettingsMigrated: true,
openAiHeadersMigrated: true,
consecutiveMistakeLimitMigrated: true,
todoListEnabledMigrated: true,
},
}),
)

await providerSettingsManager.initialize()

// Get the last call to store, which should contain the migrated config
const calls = mockSecrets.store.mock.calls
const storedConfig = JSON.parse(calls[calls.length - 1][1])

// Roo provider configs should be migrated
expect(storedConfig.apiConfigs.default.apiModelId).toEqual("roo/code-supernova-1-million")
expect(storedConfig.apiConfigs.test.apiModelId).toEqual("roo/code-supernova-1-million")
expect(storedConfig.apiConfigs.existing.apiModelId).toEqual("roo/code-supernova-1-million")

// Non-roo provider configs should not be migrated
expect(storedConfig.apiConfigs.otherProvider.apiModelId).toEqual("roo/code-supernova")
expect(storedConfig.apiConfigs.noProvider.apiModelId).toEqual("roo/code-supernova")
})

it("should apply model migrations every time, not just once", async () => {
// First load with old model
mockSecrets.get.mockResolvedValue(
JSON.stringify({
currentApiConfigName: "default",
apiConfigs: {
default: {
apiProvider: "roo",
apiModelId: "roo/code-supernova",
id: "default",
},
},
migrations: {
rateLimitSecondsMigrated: true,
diffSettingsMigrated: true,
openAiHeadersMigrated: true,
consecutiveMistakeLimitMigrated: true,
todoListEnabledMigrated: true,
},
}),
)

await providerSettingsManager.initialize()

// Verify migration happened
let calls = mockSecrets.store.mock.calls
let storedConfig = JSON.parse(calls[calls.length - 1][1])
expect(storedConfig.apiConfigs.default.apiModelId).toEqual("roo/code-supernova-1-million")

// Create a new instance to simulate another load
const newManager = new ProviderSettingsManager(mockContext)

// Somehow the model got reverted (e.g., manual edit, sync issue)
mockSecrets.get.mockResolvedValue(
JSON.stringify({
currentApiConfigName: "default",
apiConfigs: {
default: {
apiProvider: "roo",
apiModelId: "roo/code-supernova", // Old model again
id: "default",
},
},
migrations: {
rateLimitSecondsMigrated: true,
diffSettingsMigrated: true,
openAiHeadersMigrated: true,
consecutiveMistakeLimitMigrated: true,
todoListEnabledMigrated: true,
},
}),
)

await newManager.initialize()

// Verify migration happened again
calls = mockSecrets.store.mock.calls
storedConfig = JSON.parse(calls[calls.length - 1][1])
expect(storedConfig.apiConfigs.default.apiModelId).toEqual("roo/code-supernova-1-million")
})

it("should throw error if secrets storage fails", async () => {
mockSecrets.get.mockRejectedValue(new Error("Storage failed"))

Expand Down
2 changes: 1 addition & 1 deletion src/core/webview/ClineProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export class ClineProvider

public isViewLaunched = false
public settingsImportedAt?: number
public readonly latestAnnouncementId = "sep-2025-code-supernova" // Code Supernova stealth model announcement
public readonly latestAnnouncementId = "sep-2025-code-supernova-1m" // Code Supernova 1M context window announcement
public readonly providerSettingsManager: ProviderSettingsManager
public readonly customModesManager: CustomModesManager

Expand Down
2 changes: 1 addition & 1 deletion webview-ui/src/i18n/locales/ca/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion webview-ui/src/i18n/locales/de/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion webview-ui/src/i18n/locales/en/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@
"announcement": {
"title": "🎉 Roo Code {{version}} Released",
"stealthModel": {
"feature": "<bold>Limited-time FREE stealth model</bold> - Code Supernova: A versatile agentic coding model that supports image inputs, accessible through Roo Code Cloud.",
"feature": "<bold>Limited-time FREE stealth model</bold> - Code Supernova: Now upgraded with a <bold>1M token context window</bold>! A versatile agentic coding model that supports image inputs, accessible through Roo Code Cloud.",
"note": "(Note: prompts and completions are logged by the model creator and used to improve the model)",
"connectButton": "Connect to Roo Code Cloud",
"selectModel": "Select <code>roo/code-supernova</code> from the Roo Code Cloud provider in Settings to get started.",
Expand Down
2 changes: 1 addition & 1 deletion webview-ui/src/i18n/locales/es/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion webview-ui/src/i18n/locales/fr/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion webview-ui/src/i18n/locales/hi/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion webview-ui/src/i18n/locales/id/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion webview-ui/src/i18n/locales/it/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion webview-ui/src/i18n/locales/ja/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion webview-ui/src/i18n/locales/ko/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion webview-ui/src/i18n/locales/nl/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion webview-ui/src/i18n/locales/pl/chat.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading