diff --git a/.changeset/eleven-bulldogs-live.md b/.changeset/eleven-bulldogs-live.md deleted file mode 100644 index 0b02ae8d369..00000000000 --- a/.changeset/eleven-bulldogs-live.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -OpenAI Codex: Add ChatGPT subscription usage limits dashboard diff --git a/.changeset/happy-adults-study.md b/.changeset/happy-adults-study.md deleted file mode 100644 index 290c7255391..00000000000 --- a/.changeset/happy-adults-study.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": minor ---- - -Improve idea box during onboarding experience diff --git a/.changeset/long-grapes-sip.md b/.changeset/long-grapes-sip.md deleted file mode 100644 index e28c9f4cb43..00000000000 --- a/.changeset/long-grapes-sip.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@roo-code/vscode-webview": patch -"@roo-code/types": patch ---- - -feat(moonshot): add new Kimi models and coding API endpoint diff --git a/.changeset/nasty-kiwis-laugh.md b/.changeset/nasty-kiwis-laugh.md new file mode 100644 index 00000000000..42dd4cf22dd --- /dev/null +++ b/.changeset/nasty-kiwis-laugh.md @@ -0,0 +1,5 @@ +--- +"kilo-code": patch +--- + +Show sign in prompt when trying paid model when not logged in diff --git a/.changeset/update-welcome-model-names.md b/.changeset/update-welcome-model-names.md deleted file mode 100644 index e5ca2da646c..00000000000 --- a/.changeset/update-welcome-model-names.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"kilo-code": patch ---- - -Updated welcome screen model names in all translations diff --git a/CHANGELOG.md b/CHANGELOG.md index d62820b3d40..9e0ab01eb9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # kilo-code +## 5.2.1 + +### Patch Changes + +- [#5501](https://github.com/Kilo-Org/kilocode/pull/5501) [`cecefc1`](https://github.com/Kilo-Org/kilocode/commit/cecefc1dd660100631eecf8517f2c0c918f6cdb3) Thanks [@Neonsy](https://github.com/Neonsy)! - Adding Kimi K2.5 + +## 5.2.0 + +### Minor Changes + +- [#5477](https://github.com/Kilo-Org/kilocode/pull/5477) [`59a792e`](https://github.com/Kilo-Org/kilocode/commit/59a792eeb461497fe2968ca17e2858389c55894a) Thanks [@chrarnoldus](https://github.com/chrarnoldus)! - Improve idea box during onboarding experience + +### Patch Changes + +- [#5503](https://github.com/Kilo-Org/kilocode/pull/5503) [`e53f086`](https://github.com/Kilo-Org/kilocode/commit/e53f0865d32296cb5e4db5f853466f5fa7671371) Thanks [@lambertjosh](https://github.com/lambertjosh)! - Fix mode selection after anonymous usage + +- [#5426](https://github.com/Kilo-Org/kilocode/pull/5426) [`56d086b`](https://github.com/Kilo-Org/kilocode/commit/56d086b4853abfeebff6b1afb6c8d0431c232542) Thanks [@lambertjosh](https://github.com/lambertjosh)! - OpenAI Codex: Add ChatGPT subscription usage limits dashboard + +- [#4947](https://github.com/Kilo-Org/kilocode/pull/4947) [`53080fd`](https://github.com/Kilo-Org/kilocode/commit/53080fddfc62a171ebae09fe38629aec8b0e6098) Thanks [@CaiDingxian](https://github.com/CaiDingxian)! - feat(moonshot): add new Kimi models and coding API endpoint + +- [#5451](https://github.com/Kilo-Org/kilocode/pull/5451) [`af25644`](https://github.com/Kilo-Org/kilocode/commit/af25644f8482bd1a10e6645ed3061421ac23045e) Thanks [@kiloconnect](https://github.com/apps/kiloconnect)! - Updated welcome screen model names in all translations + ## 5.1.0 ### Minor Changes diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 56ee24ebb64..88774790125 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Welcome to the Kilocode Project -See [the Documentation for details on contributing](https://kilo.ai/docs/extending/contributing-to-kilo) +See [the Documentation for details on contributing](https://kilo.ai/docs/contributing) ## TL;DR diff --git a/apps/kilocode-docs/pages/getting-started/adding-credits.md b/apps/kilocode-docs/pages/getting-started/adding-credits.md index 45a0615f5da..1c82111b11c 100644 --- a/apps/kilocode-docs/pages/getting-started/adding-credits.md +++ b/apps/kilocode-docs/pages/getting-started/adding-credits.md @@ -33,5 +33,5 @@ We're continuously working to improve Kilo Code and expand our offerings: - More payment options and other plans are under development {% callout type="tip" title="Need Help?" %} -If you have any questions about pricing or tokens, please reach out to our [support team](mailto:hi@kilo.ai) or ask in our Discord community. +If you have any questions about pricing or tokens, please reach out to our [support team](mailto:hi@kilo.ai) or ask in our [Discord community](https://kilo.ai/discord). {% /callout %} diff --git a/apps/kilocode-docs/pages/getting-started/faq.md b/apps/kilocode-docs/pages/getting-started/faq.md index 224d9603692..b3906e604ad 100644 --- a/apps/kilocode-docs/pages/getting-started/faq.md +++ b/apps/kilocode-docs/pages/getting-started/faq.md @@ -53,7 +53,7 @@ Model usage is metered by the providers in terms of different kinds of tokens. W You can use any models you like as long as you have credits in your account. When you run out of credits, you can add more. It's that simple! -If you're looking to earn some credits, you could join our Discord where we sometimes have promotional offers! +If you're looking to earn some credits, you could join our [Discord](https://kilo.ai/discord) where we sometimes have promotional offers! ### What are the risks of using Kilo Code? diff --git a/packages/types/src/message.ts b/packages/types/src/message.ts index 31bb827c874..a8dd7d88ffb 100644 --- a/packages/types/src/message.ts +++ b/packages/types/src/message.ts @@ -41,6 +41,7 @@ export const clineAsks = [ "auto_approval_max_req_reached", // kilocode_change start "payment_required_prompt", // Added for the low credits dialog + "unauthorized_prompt", // Added for unauthorized error when using paid models "invalid_model", "report_bug", "condense", @@ -60,6 +61,7 @@ export type ClineAsk = z.infer export const idleAsks = [ // kilocode_change start "payment_required_prompt", + "unauthorized_prompt", "invalid_model", // kilocode_change end "completion_result", diff --git a/packages/types/src/providers/moonshot.ts b/packages/types/src/providers/moonshot.ts index 03857af9281..ddf96b96aed 100644 --- a/packages/types/src/providers/moonshot.ts +++ b/packages/types/src/providers/moonshot.ts @@ -6,6 +6,26 @@ export type MoonshotModelId = keyof typeof moonshotModels export const moonshotDefaultModelId: MoonshotModelId = "kimi-k2-thinking" export const moonshotModels = { + // kilocode_change start + "kimi-k2.5": { + maxTokens: 32_000, + contextWindow: 262_144, // 256K + supportsImages: true, // Native multimodal + supportsPromptCache: true, + supportsNativeTools: true, + defaultToolProtocol: "native", + supportsTemperature: false, // Based on API specs + defaultTemperature: 1.0, // Default for thinking mode + supportsReasoningBudget: true, + supportsReasoningEffort: true, + preserveReasoning: true, + inputPrice: 0.6, // $0.60 per million (cache miss) + outputPrice: 3.0, // $3.00 per million + cacheWritesPrice: 0, + cacheReadsPrice: 0.1, // $0.10 per million (cache hit) + description: `Kimi K2.5 is Kimi's most versatile multimodal model with native vision support. Supports both thinking mode (default, temp=1.0) and instant mode (thinking disabled, temp=0.6). Features 256K context, vision understanding, and agent capabilities.`, + }, + // kilocode_change end "kimi-for-coding": { maxTokens: 32_000, contextWindow: 131_072, diff --git a/src/activate/handleUri.ts b/src/activate/handleUri.ts index 999ddbe5c12..5eb76f6e7a2 100644 --- a/src/activate/handleUri.ts +++ b/src/activate/handleUri.ts @@ -3,10 +3,31 @@ import * as vscode from "vscode" import { CloudService } from "@roo-code/cloud" import { ClineProvider } from "../core/webview/ClineProvider" +import { Package } from "../shared/package" export const handleUri = async (uri: vscode.Uri) => { const path = uri.path const query = new URLSearchParams(uri.query.replace(/\+/g, "%2B")) + + // kilocode_change start: Handle /kilocode/chat path specially - it needs to open the extension first + // before we can get a provider instance + if (path === "/kilocode/chat") { + // Focus the sidebar first to open the Kilo Code extension + await vscode.commands.executeCommand(`${Package.name}.SidebarProvider.focus`) + // Use getInstance() which waits for the provider to become visible after focusing + const provider = await ClineProvider.getInstance() + if (!provider) { + return + } + // Open a fresh chat (same as clicking the + button) + await provider.removeClineFromStack() + await provider.refreshWorkspace() + await provider.postMessageToWebview({ type: "action", action: "chatButtonClicked" }) + await provider.postMessageToWebview({ type: "action", action: "focusInput" }) + return + } + // kilocode_change end + const visibleProvider = ClineProvider.getVisibleInstance() if (!visibleProvider) { @@ -39,6 +60,8 @@ export const handleUri = async (uri: vscode.Uri) => { } // kilocode_change start case "/kilocode/profile": { + // Focus the sidebar first so users can see the profile + await vscode.commands.executeCommand(`${Package.name}.SidebarProvider.focus`) await visibleProvider.postMessageToWebview({ type: "action", action: "profileButtonClicked", @@ -51,6 +74,8 @@ export const handleUri = async (uri: vscode.Uri) => { case "/kilocode/fork": { const id = query.get("id") if (id) { + // Focus the sidebar first so users can see the fork + await vscode.commands.executeCommand(`${Package.name}.SidebarProvider.focus`) await visibleProvider.postMessageToWebview({ type: "invoke", invoke: "setChatBoxMessage", diff --git a/src/core/kilocode/webview/webviewMessageHandlerUtils.ts b/src/core/kilocode/webview/webviewMessageHandlerUtils.ts index ca2b8362769..ef6454cf752 100644 --- a/src/core/kilocode/webview/webviewMessageHandlerUtils.ts +++ b/src/core/kilocode/webview/webviewMessageHandlerUtils.ts @@ -7,9 +7,153 @@ import { Task } from "../../task/Task" import axios from "axios" import { getKiloUrlFromToken } from "@roo-code/types" import { buildApiHandler } from "../../../api" +import { ContextProxy } from "../../config/ContextProxy" const shownNativeNotificationIds = new Set() +/** + * Replaces vscode:// protocol in URLs with the appropriate protocol for the current IDE. + * For example, in Cursor it becomes cursor://, in VSCodium it becomes vscodium://, etc. + * @param url The URL to transform + * @returns The URL with the appropriate protocol for the current IDE + */ +export function replaceVscodeProtocol(url: string): string { + if (!url.startsWith("vscode://")) { + return url + } + const currentScheme = vscode.env.uriScheme + return url.replace(/^vscode:\/\//, `${currentScheme}://`) +} + +interface KilocodeNotification { + id: string + title: string + message: string + showIn?: string[] + action?: { + actionText: string + actionURL: string + } +} + +interface FetchNotificationsResult { + notifications: KilocodeNotification[] + nativeNotifications: KilocodeNotification[] +} + +/** + * Core function to fetch Kilocode notifications from the API + * Can be used both from webview handler and standalone startup + */ +export async function fetchKilocodeNotificationsCore( + kilocodeToken: string, + dismissedNotificationIds: string[] = [], + kilocodeTesterWarningsDisabledUntil?: number, + log?: (message: string) => void, +): Promise { + const headers: Record = { + Authorization: `Bearer ${kilocodeToken}`, + "Content-Type": "application/json", + } + + // Add X-KILOCODE-TESTER: SUPPRESS header if the setting is enabled + if (kilocodeTesterWarningsDisabledUntil && kilocodeTesterWarningsDisabledUntil > Date.now()) { + headers["X-KILOCODE-TESTER"] = "SUPPRESS" + } + + const url = getKiloUrlFromToken("https://api.kilo.ai/api/users/notifications", kilocodeToken) + const response = await axios.get(url, { + headers, + timeout: 5000, + }) + + const notifications: KilocodeNotification[] = response.data?.notifications || [] + + // Filter notifications for native display (not dismissed and not already shown) + const nativeNotifications = notifications.filter( + (notification) => + !dismissedNotificationIds.includes(notification.id) && + !shownNativeNotificationIds.has(notification.id) && + (notification.showIn ?? []).includes("extension-native"), + ) + + // Filter notifications for webview display + const webviewNotifications = notifications.filter(({ showIn }) => !showIn || showIn.includes("extension")) + + return { + notifications: webviewNotifications, + nativeNotifications, + } +} + +/** + * Display native VSCode notifications for Kilocode notifications + */ +export async function displayNativeNotifications( + notifications: KilocodeNotification[], + log?: (message: string) => void, +): Promise { + for (const notification of notifications) { + try { + const message = `${notification.title}: ${notification.message}` + const actionButton = notification.action?.actionText + const selection = await vscode.window.showInformationMessage( + message, + ...(actionButton ? [actionButton] : []), + ) + if (selection === actionButton) { + if (notification.action?.actionURL) { + // Replace vscode:// protocol with the appropriate protocol for the current IDE + const actionUrl = replaceVscodeProtocol(notification.action.actionURL) + await vscode.env.openExternal(vscode.Uri.parse(actionUrl)) + } + } + + shownNativeNotificationIds.add(notification.id) + } catch (error: any) { + log?.(`Error displaying notification ${notification.id}: ${error.message}`) + } + } +} + +/** + * Fetch and display Kilocode notifications on extension startup + * This function works without requiring the webview to be open + */ +export async function fetchKilocodeNotificationsOnStartup( + contextProxy: ContextProxy, + log?: (message: string) => void, +): Promise { + try { + const apiConfiguration = contextProxy.getProviderSettings() + const dismissedNotificationIds = contextProxy.getValue("dismissedNotificationIds") || [] + const kilocodeToken = apiConfiguration?.kilocodeToken + + if (!kilocodeToken || apiConfiguration?.apiProvider !== "kilocode") { + log?.("[Notifications] Skipping notification fetch: not using kilocode provider") + return + } + + log?.("[Notifications] Fetching notifications on startup...") + + const { nativeNotifications } = await fetchKilocodeNotificationsCore( + kilocodeToken, + dismissedNotificationIds, + apiConfiguration.kilocodeTesterWarningsDisabledUntil, + log, + ) + + if (nativeNotifications.length > 0) { + log?.(`[Notifications] Displaying ${nativeNotifications.length} native notification(s)`) + await displayNativeNotifications(nativeNotifications, log) + } else { + log?.("[Notifications] No new notifications to display") + } + } catch (error: any) { + log?.(`[Notifications] Error fetching notifications on startup: ${error.message}`) + } +} + // Helper function to delete messages for resending const deleteMessagesForResend = async (cline: Task, originalMessageIndex: number, originalMessageTs: number) => { // Delete UI messages after the edited message @@ -85,72 +229,19 @@ export const fetchKilocodeNotificationsHandler = async (provider: ClineProvider) return } - const headers: Record = { - Authorization: `Bearer ${kilocodeToken}`, - "Content-Type": "application/json", - } - - // Add X-KILOCODE-TESTER: SUPPRESS header if the setting is enabled - if ( - apiConfiguration.kilocodeTesterWarningsDisabledUntil && - apiConfiguration.kilocodeTesterWarningsDisabledUntil > Date.now() - ) { - headers["X-KILOCODE-TESTER"] = "SUPPRESS" - } - - const url = getKiloUrlFromToken("https://api.kilo.ai/api/users/notifications", kilocodeToken) - const response = await axios.get(url, { - headers, - timeout: 5000, - }) - - const notifications = response.data?.notifications || [] - const dismissedIds = dismissedNotificationIds || [] - - // Filter notifications to only show new ones - const notificationsToShowAsNative = notifications.filter( - (notification: any) => - !dismissedIds.includes(notification.id) && - !shownNativeNotificationIds.has(notification.id) && - (notification.showIn ?? []).includes("extension-native"), + const { notifications, nativeNotifications } = await fetchKilocodeNotificationsCore( + kilocodeToken, + dismissedNotificationIds || [], + apiConfiguration.kilocodeTesterWarningsDisabledUntil, + provider.log.bind(provider), ) provider.postMessageToWebview({ type: "kilocodeNotificationsResponse", - notifications: (response.data?.notifications || []).filter( - ({ showIn }: { showIn?: string[] }) => !showIn || showIn.includes("extension"), - ), + notifications, }) - for (const notification of notificationsToShowAsNative) { - try { - const message = `${notification.title}: ${notification.message}` - const actionButton = notification.action?.actionText - const dismissButton = "Do not show again" - const selection = await vscode.window.showInformationMessage( - message, - ...(actionButton ? [actionButton, dismissButton] : [dismissButton]), - ) - if (selection) { - const currentDismissedIds = dismissedNotificationIds || [] - if (!currentDismissedIds.includes(notification.id)) { - await provider.contextProxy.setValue("dismissedNotificationIds", [ - ...currentDismissedIds, - notification.id, - ]) - } - } - if (selection === actionButton) { - if (notification.action?.actionURL) { - await vscode.env.openExternal(vscode.Uri.parse(notification.action.actionURL)) - } - } - - shownNativeNotificationIds.add(notification.id) - } catch (error: any) { - provider.log(`Error displaying notification ${notification.id}: ${error.message}`) - } - } + await displayNativeNotifications(nativeNotifications, provider.log.bind(provider)) } catch (error: any) { provider.log(`Error fetching Kilocode notifications: ${error.message}`) provider.postMessageToWebview({ diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 38bfaf78d46..21f0d6c5434 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -142,7 +142,11 @@ import { processUserContentMentions } from "../mentions/processUserContentMentio import { getMessagesSinceLastSummary, summarizeConversation, getEffectiveApiHistory } from "../condense" import { MessageQueueService } from "../message-queue/MessageQueueService" -import { isAnyRecognizedKiloCodeError, isPaymentRequiredError } from "../../shared/kilocode/errorUtils" +import { + isAnyRecognizedKiloCodeError, + isPaymentRequiredError, + isUnauthorizedError, +} from "../../shared/kilocode/errorUtils" import { getAppUrl } from "@roo-code/types" import { getKilocodeDefaultModel } from "../../api/providers/kilocode/getKilocodeDefaultModel" // kilocode_change import { addOrMergeUserContent } from "./kilocode" @@ -1986,7 +1990,7 @@ export class Task extends EventEmitter implements TaskLike { switch (toolName) { case "apply_diff": return t("kilocode:task.disableApplyDiff") + " " - case "edit_file": + case "fast_edit_file": return t("kilocode:task.disableEditFile") + " " default: return "" @@ -4620,16 +4624,23 @@ export class Task extends EventEmitter implements TaskLike { defaultFreeModel, }), ) - : this.ask( - "invalid_model", - JSON.stringify({ - modelId: apiConfiguration.kilocodeModel, - error: { - status: error.status, - message: error.message, - }, - }), - )) + : isUnauthorizedError(error) + ? this.ask( + "unauthorized_prompt", + JSON.stringify({ + modelId: apiConfiguration.kilocodeModel, + }), + ) + : this.ask( + "invalid_model", + JSON.stringify({ + modelId: apiConfiguration.kilocodeModel, + error: { + status: error.status, + message: error.message, + }, + }), + )) this.currentRequestAbortController = undefined const isContextWindowExceededError = checkContextWindowExceededError(error) diff --git a/src/core/webview/__tests__/webviewMessageHandler.autoSwitch.spec.ts b/src/core/webview/__tests__/webviewMessageHandler.autoSwitch.spec.ts index 036cd307f58..d76565d339f 100644 --- a/src/core/webview/__tests__/webviewMessageHandler.autoSwitch.spec.ts +++ b/src/core/webview/__tests__/webviewMessageHandler.autoSwitch.spec.ts @@ -145,7 +145,7 @@ describe("webviewMessageHandler - Automatic Organization Switching", () => { kilocodeToken: "test-token", kilocodeOrganizationId: "org-1", }, - false, + true, // Changed: Now correctly activates the profile (fix for PR #5415 bug) ) // Verify flag was set to true after the recursive call @@ -407,7 +407,7 @@ describe("webviewMessageHandler - Automatic Organization Switching", () => { kilocodeToken: "new-token", kilocodeOrganizationId: undefined, }, - false, + true, // Changed: Now correctly activates the profile (fix for PR #5415 bug) ) }) @@ -569,7 +569,7 @@ describe("webviewMessageHandler - Automatic Organization Switching", () => { kilocodeToken: "test-token", kilocodeOrganizationId: "org-1", }, - false, + true, // Changed: Now correctly activates the profile (fix for PR #5415 bug) ) // Verify log message mentions the correct organization diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 9aa7dfc64ba..13dd8ef5edc 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -2305,7 +2305,7 @@ export const webviewMessageHandler = async ( } // kilocode_change start: If we're updating the active profile, we need to activate it to ensure it's persisted - const currentApiConfigName = getGlobalState("currentApiConfigName") + const currentApiConfigName = getGlobalState("currentApiConfigName") || "default" const isActiveProfile = message.text === currentApiConfigName await provider.upsertProviderProfile(message.text, configToSave, isActiveProfile) // Activate if it's the current active profile vscode.commands.executeCommand("kilo-code.ghost.reload") diff --git a/src/extension.ts b/src/extension.ts index b6938b4a098..99a8aca29fa 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -54,6 +54,7 @@ import { SettingsSyncService } from "./services/settings-sync/SettingsSyncServic import { ManagedIndexer } from "./services/code-index/managed/ManagedIndexer" // kilocode_change import { flushModels, getModels, initializeModelCacheRefresh, refreshModels } from "./api/providers/fetchers/modelCache" import { kilo_initializeSessionManager } from "./shared/kilocode/cli-sessions/extension/session-manager-utils" // kilocode_change +import { fetchKilocodeNotificationsOnStartup } from "./core/kilocode/webview/webviewMessageHandlerUtils" // kilocode_change // kilocode_change start async function findKilocodeTokenFromAnyProfile(provider: ClineProvider): Promise { @@ -426,6 +427,16 @@ export async function activate(context: vscode.ExtensionContext) { ) } + // kilocode_change start: Fetch Kilo Code notifications on startup + try { + void fetchKilocodeNotificationsOnStartup(contextProxy, outputChannel.appendLine.bind(outputChannel)) + } catch (error) { + outputChannel.appendLine( + `[Notifications] Error fetching notifications on startup: ${error instanceof Error ? error.message : String(error)}`, + ) + } + // kilocode_change end + // kilocode_change start // Check for env var conflicts that might confuse users try { diff --git a/src/package.json b/src/package.json index 280d88913bc..2eb7acb09b8 100644 --- a/src/package.json +++ b/src/package.json @@ -3,7 +3,7 @@ "displayName": "%extension.displayName%", "description": "%extension.description%", "publisher": "kilocode", - "version": "5.1.0", + "version": "5.2.1", "icon": "assets/icons/logo-outline-black.png", "galleryBanner": { "color": "#FFFFFF", diff --git a/src/shared/kilocode/errorUtils.ts b/src/shared/kilocode/errorUtils.ts index 07688bd25a8..b5d566c0082 100644 --- a/src/shared/kilocode/errorUtils.ts +++ b/src/shared/kilocode/errorUtils.ts @@ -11,6 +11,10 @@ export function isPaymentRequiredError(error: any) { return !!(error && error.status === 402) } +export function isUnauthorizedError(error: any) { + return !!(error && error.status === 401) +} + export function isAlphaPeriodEndedError(error: any) { return !!( error && @@ -32,6 +36,7 @@ export function isModelNotAllowedForTeamError(error: any) { export function isAnyRecognizedKiloCodeError(error: any) { return ( isPaymentRequiredError(error) || + isUnauthorizedError(error) || isOpenRouterInvalidModelError(error) || isAlphaPeriodEndedError(error) || isModelNotAllowedForTeamError(error) diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 7478a56766a..a880d1ce0ed 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -82,6 +82,7 @@ import { KiloChatRowGutterBar } from "../kilocode/chat/KiloChatRowGutterBar" import { StandardTooltip } from "../ui" import { FastApplyChatDisplay } from "./kilocode/FastApplyChatDisplay" import { InvalidModelWarning } from "../kilocode/chat/InvalidModelWarning" +import { UnauthorizedWarning } from "../kilocode/chat/UnauthorizedWarning" import { formatFileSize } from "@/lib/formatting-utils" import ChatTimestamps from "./ChatTimestamps" import { removeLeadingNonAlphanumeric } from "@/utils/removeLeadingNonAlphanumeric" @@ -1872,6 +1873,9 @@ export const ChatRowContent = ({ case "payment_required_prompt": { return } + case "unauthorized_prompt": { + return + } case "invalid_model": { return } diff --git a/webview-ui/src/components/kilocode/chat/UnauthorizedWarning.tsx b/webview-ui/src/components/kilocode/chat/UnauthorizedWarning.tsx new file mode 100644 index 00000000000..2d7881fb56c --- /dev/null +++ b/webview-ui/src/components/kilocode/chat/UnauthorizedWarning.tsx @@ -0,0 +1,87 @@ +import { useEffect, useRef, useCallback } from "react" +import { ClineMessage } from "@roo-code/types" +import { vscode } from "@src/utils/vscode" +import { Button } from "@src/components/ui" +import { useTranslation } from "react-i18next" +import { safeJsonParse } from "@roo/safeJsonParse" + +type UnauthorizedWarningProps = { + message: ClineMessage +} + +type UnauthorizedWarningData = { + modelId?: string +} + +export const UnauthorizedWarning = ({ message }: UnauthorizedWarningProps) => { + const { t } = useTranslation() + const hasRetried = useRef(false) + + const data = safeJsonParse(message.text) + + const handleRetry = useCallback(() => { + if (hasRetried.current) { + return + } + hasRetried.current = true + vscode.postMessage({ + type: "askResponse", + askResponse: "retry_clicked", + text: message.text, + }) + }, [message.text]) + + // Listen for successful authentication and automatically retry + useEffect(() => { + let retryTimeoutId: ReturnType | undefined + + const handleMessage = (event: MessageEvent) => { + // Validate message shape and type before acting + const msg = event.data + if (typeof msg !== "object" || msg === null || msg.type !== "deviceAuthComplete") { + return + } + // Auth succeeded - wait briefly for token to be saved, then retry + retryTimeoutId = setTimeout(() => { + handleRetry() + }, 500) + } + + window.addEventListener("message", handleMessage) + return () => { + window.removeEventListener("message", handleMessage) + if (retryTimeoutId !== undefined) { + clearTimeout(retryTimeoutId) + } + } + }, [handleRetry]) + + const modelId = data?.modelId || "(chosen)" + + return ( +
+
+ + + {t("kilocode:unauthorizedError.title", { modelId })} + +
+

+ {t("kilocode:unauthorizedError.message")} +

+ +
+ ) +} diff --git a/webview-ui/src/i18n/locales/ar/kilocode.json b/webview-ui/src/i18n/locales/ar/kilocode.json index fe9b8e0e58b..aebe07a2a78 100644 --- a/webview-ui/src/i18n/locales/ar/kilocode.json +++ b/webview-ui/src/i18n/locales/ar/kilocode.json @@ -22,6 +22,11 @@ "next": "التالي", "page": "صفحة {{page}} / {{count}}" }, + "unauthorizedError": { + "title": "يجب عليك تسجيل الدخول لاستخدام نموذج {{modelId}}", + "message": "قم بتسجيل الدخول أو إنشاء حساب للوصول إلى أكثر من 500 نموذج، واستخدم الأرصدة بسعر التكلفة، أو أحضر مفتاحك الخاص.", + "loginButton": "تسجيل الدخول" + }, "pricing": { "costUnknown": "التكلفة غير معروفة", "costUnknownDescription": "لم يقدم مزود واجهة برمجة التطبيقات أي بيانات تكلفة أو تم إلغاء الطلب.", diff --git a/webview-ui/src/i18n/locales/ca/kilocode.json b/webview-ui/src/i18n/locales/ca/kilocode.json index 760919efe9d..1793eaffc58 100644 --- a/webview-ui/src/i18n/locales/ca/kilocode.json +++ b/webview-ui/src/i18n/locales/ca/kilocode.json @@ -19,6 +19,11 @@ "next": "Següent", "page": "Pàgina {{page}} / {{count}}" }, + "unauthorizedError": { + "title": "Has d'iniciar sessió per utilitzar el model {{modelId}}", + "message": "Inicia sessió o crea un compte per accedir a més de 500 models, utilitzar crèdits a preu de cost o portar la teva pròpia clau.", + "loginButton": "Iniciar Sessió" + }, "pricing": { "costUnknown": "cost desconegut", "costUnknownDescription": "El proveïdor de l'API no va proporcionar cap dada de cost o la sol·licitud va ser cancel·lada.", diff --git a/webview-ui/src/i18n/locales/cs/kilocode.json b/webview-ui/src/i18n/locales/cs/kilocode.json index 20e1acc29a7..a45ce534c76 100644 --- a/webview-ui/src/i18n/locales/cs/kilocode.json +++ b/webview-ui/src/i18n/locales/cs/kilocode.json @@ -26,6 +26,11 @@ "continueWith": "Pokračovat s {{model}}", "continue": "Pokračovat" }, + "unauthorizedError": { + "title": "Musíš se přihlásit, abys mohl použít model {{modelId}}", + "message": "Přihlaš se nebo si vytvoř účet pro přístup k více než 500 modelům, použij kredity za pořizovací cenu nebo přines svůj vlastní klíč.", + "loginButton": "Přihlásit se" + }, "pricing": { "costUnknown": "neznámé náklady", "costUnknownDescription": "Poskytovatel API neposkytl žádné údaje o nákladech nebo byl požadavek zrušen.", diff --git a/webview-ui/src/i18n/locales/de/kilocode.json b/webview-ui/src/i18n/locales/de/kilocode.json index d8531212177..6126544f3bd 100644 --- a/webview-ui/src/i18n/locales/de/kilocode.json +++ b/webview-ui/src/i18n/locales/de/kilocode.json @@ -26,6 +26,11 @@ "continueWith": "Weiter mit {{model}}", "continue": "Weiter" }, + "unauthorizedError": { + "title": "Du musst dich anmelden, um das {{modelId}} Modell zu nutzen", + "message": "Melde dich an oder erstelle ein Konto, um auf über 500 Modelle zuzugreifen, Credits zu Selbstkosten zu nutzen oder deinen eigenen Schlüssel mitzubringen.", + "loginButton": "Anmelden" + }, "pricing": { "costUnknown": "Kosten unbekannt", "costUnknownDescription": "Der API-Anbieter hat keine Kostendaten bereitgestellt oder die Anfrage wurde abgebrochen.", diff --git a/webview-ui/src/i18n/locales/en/kilocode.json b/webview-ui/src/i18n/locales/en/kilocode.json index eda6735635f..f63b2754950 100644 --- a/webview-ui/src/i18n/locales/en/kilocode.json +++ b/webview-ui/src/i18n/locales/en/kilocode.json @@ -29,6 +29,11 @@ "continueWith": "Continue with {{model}}", "continue": "Continue" }, + "unauthorizedError": { + "title": "You need to sign in to use the {{modelId}} model", + "message": "Sign in or create an account to access over 500 models, use credits at cost, or bring your own key.", + "loginButton": "Sign In" + }, "pricing": { "costUnknown": "cost unknown", "costUnknownDescription": "The API Provider did not provide any cost data or the request was canceled.", diff --git a/webview-ui/src/i18n/locales/es/kilocode.json b/webview-ui/src/i18n/locales/es/kilocode.json index de85993b0aa..40cfcb8a470 100644 --- a/webview-ui/src/i18n/locales/es/kilocode.json +++ b/webview-ui/src/i18n/locales/es/kilocode.json @@ -19,6 +19,11 @@ "next": "Siguiente", "page": "Página {{page}} / {{count}}" }, + "unauthorizedError": { + "title": "Necesitas iniciar sesión para usar el modelo {{modelId}}", + "message": "Inicia sesión o crea una cuenta para acceder a más de 500 modelos, usar créditos a precio de coste o traer tu propia clave.", + "loginButton": "Iniciar Sesión" + }, "pricing": { "costUnknown": "costo desconocido", "costUnknownDescription": "El proveedor de API no proporcionó datos de costo o la solicitud fue cancelada.", diff --git a/webview-ui/src/i18n/locales/fr/kilocode.json b/webview-ui/src/i18n/locales/fr/kilocode.json index 61788e80273..fb487b1d272 100644 --- a/webview-ui/src/i18n/locales/fr/kilocode.json +++ b/webview-ui/src/i18n/locales/fr/kilocode.json @@ -26,6 +26,11 @@ "continueWith": "Continuer avec {{model}}", "continue": "Continuer" }, + "unauthorizedError": { + "title": "Tu dois te connecter pour utiliser le modèle {{modelId}}", + "message": "Connecte-toi ou crée un compte pour accéder à plus de 500 modèles, utiliser des crédits au prix coûtant ou apporter ta propre clé.", + "loginButton": "Se Connecter" + }, "pricing": { "costUnknown": "coût inconnu", "costUnknownDescription": "Le fournisseur d'API n'a pas fourni de données de coût ou la demande a été annulée.", diff --git a/webview-ui/src/i18n/locales/hi/kilocode.json b/webview-ui/src/i18n/locales/hi/kilocode.json index 7a95767d370..77affcafd22 100644 --- a/webview-ui/src/i18n/locales/hi/kilocode.json +++ b/webview-ui/src/i18n/locales/hi/kilocode.json @@ -19,6 +19,11 @@ "next": "अगला", "page": "पृष्ठ {{page}} / {{count}}" }, + "unauthorizedError": { + "title": "आपको {{modelId}} मॉडल का उपयोग करने के लिए साइन इन करना होगा", + "message": "500+ मॉडलों तक पहुंच प्राप्त करने, लागत मूल्य पर क्रेडिट का उपयोग करने, या अपनी खुद की कुंजी लाने के लिए साइन इन करें या खाता बनाएं।", + "loginButton": "साइन इन करें" + }, "pricing": { "costUnknown": "लागत अज्ञात", "costUnknownDescription": "API प्रदाता ने कोई लागत डेटा प्रदान नहीं किया या अनुरोध रद्द कर दिया गया।", diff --git a/webview-ui/src/i18n/locales/id/kilocode.json b/webview-ui/src/i18n/locales/id/kilocode.json index c2ed3e29715..baa3b8ca4fb 100644 --- a/webview-ui/src/i18n/locales/id/kilocode.json +++ b/webview-ui/src/i18n/locales/id/kilocode.json @@ -19,6 +19,11 @@ "next": "Selanjutnya", "page": "Halaman {{page}} / {{count}}" }, + "unauthorizedError": { + "title": "Kamu perlu masuk untuk menggunakan model {{modelId}}", + "message": "Masuk atau buat akun untuk mengakses lebih dari 500 model, gunakan kredit dengan harga biaya, atau bawa kunci milikmu sendiri.", + "loginButton": "Masuk" + }, "pricing": { "costUnknown": "biaya tidak diketahui", "costUnknownDescription": "Penyedia API tidak memberikan data biaya atau permintaan dibatalkan.", diff --git a/webview-ui/src/i18n/locales/it/kilocode.json b/webview-ui/src/i18n/locales/it/kilocode.json index 2ace7b59de8..e69c07d3d5d 100644 --- a/webview-ui/src/i18n/locales/it/kilocode.json +++ b/webview-ui/src/i18n/locales/it/kilocode.json @@ -26,6 +26,11 @@ "continueWith": "Continua con {{model}}", "continue": "Continua" }, + "unauthorizedError": { + "title": "Devi accedere per usare il modello {{modelId}}", + "message": "Accedi o crea un account per accedere a oltre 500 modelli, utilizzare crediti al costo effettivo o portare la tua chiave.", + "loginButton": "Accedi" + }, "pricing": { "costUnknown": "costo sconosciuto", "costUnknownDescription": "Il Provider API non ha fornito dati sui costi o la richiesta è stata annullata.", diff --git a/webview-ui/src/i18n/locales/ja/kilocode.json b/webview-ui/src/i18n/locales/ja/kilocode.json index 7140496613e..0f908b0c24d 100644 --- a/webview-ui/src/i18n/locales/ja/kilocode.json +++ b/webview-ui/src/i18n/locales/ja/kilocode.json @@ -26,6 +26,11 @@ "continueWith": "{{model}}で続行", "continue": "続行" }, + "unauthorizedError": { + "title": "{{modelId}}モデルを使用するにはサインインが必要です", + "message": "サインインまたはアカウントを作成して、500以上のモデルにアクセスし、原価でクレジットを使用するか、独自のキーを使用できます。", + "loginButton": "サインイン" + }, "pricing": { "costUnknown": "コスト不明", "costUnknownDescription": "APIプロバイダーがコストデータを提供しなかったか、リクエストがキャンセルされました。", diff --git a/webview-ui/src/i18n/locales/ko/kilocode.json b/webview-ui/src/i18n/locales/ko/kilocode.json index cb8001071b2..01148bafe83 100644 --- a/webview-ui/src/i18n/locales/ko/kilocode.json +++ b/webview-ui/src/i18n/locales/ko/kilocode.json @@ -26,6 +26,11 @@ "continueWith": "{{model}}로 계속", "continue": "계속" }, + "unauthorizedError": { + "title": "{{modelId}} 모델을 사용하려면 로그인이 필요합니다", + "message": "로그인하거나 계정을 만들어 500개 이상의 모델에 액세스하고, 원가로 크레딧을 사용하거나, 자신의 키를 사용하세요.", + "loginButton": "로그인" + }, "pricing": { "costUnknown": "비용 알 수 없음", "costUnknownDescription": "API 제공자가 비용 데이터를 제공하지 않았거나 요청이 취소되었습니다.", diff --git a/webview-ui/src/i18n/locales/nl/kilocode.json b/webview-ui/src/i18n/locales/nl/kilocode.json index 747a604bfae..9b0ccaeea1a 100644 --- a/webview-ui/src/i18n/locales/nl/kilocode.json +++ b/webview-ui/src/i18n/locales/nl/kilocode.json @@ -26,6 +26,11 @@ "continueWith": "Doorgaan met {{model}}", "continue": "Doorgaan" }, + "unauthorizedError": { + "title": "Je moet inloggen om het {{modelId}} model te gebruiken", + "message": "Log in of maak een account aan om toegang te krijgen tot meer dan 500 modellen, gebruik credits tegen kostprijs of breng je eigen sleutel mee.", + "loginButton": "Inloggen" + }, "pricing": { "costUnknown": "kosten onbekend", "costUnknownDescription": "De API-provider heeft geen kostengegevens verstrekt of het verzoek is geannuleerd.", diff --git a/webview-ui/src/i18n/locales/pl/kilocode.json b/webview-ui/src/i18n/locales/pl/kilocode.json index c3558dd48a7..b0fb589e6a0 100644 --- a/webview-ui/src/i18n/locales/pl/kilocode.json +++ b/webview-ui/src/i18n/locales/pl/kilocode.json @@ -19,6 +19,11 @@ "next": "Następna", "page": "Strona {{page}} / {{count}}" }, + "unauthorizedError": { + "title": "Musisz się zalogować, aby użyć modelu {{modelId}}", + "message": "Zaloguj się lub utwórz konto, aby uzyskać dostęp do ponad 500 modeli, korzystać z kredytów po cenie kosztowej lub przynieść własny klucz.", + "loginButton": "Zaloguj się" + }, "pricing": { "costUnknown": "koszt nieznany", "costUnknownDescription": "Dostawca API nie podał żadnych danych o kosztach lub żądanie zostało anulowane.", diff --git a/webview-ui/src/i18n/locales/pt-BR/kilocode.json b/webview-ui/src/i18n/locales/pt-BR/kilocode.json index 2e875f482d4..22f344b2dae 100644 --- a/webview-ui/src/i18n/locales/pt-BR/kilocode.json +++ b/webview-ui/src/i18n/locales/pt-BR/kilocode.json @@ -26,6 +26,11 @@ "continueWith": "Continuar com {{model}}", "continue": "Continuar" }, + "unauthorizedError": { + "title": "Você precisa fazer login para usar o modelo {{modelId}}", + "message": "Faça login ou crie uma conta para acessar mais de 500 modelos, usar créditos a preço de custo ou trazer sua própria chave.", + "loginButton": "Fazer Login" + }, "pricing": { "costUnknown": "custo desconhecido", "costUnknownDescription": "O Provedor de API não forneceu dados de custo ou a solicitação foi cancelada.", diff --git a/webview-ui/src/i18n/locales/ru/kilocode.json b/webview-ui/src/i18n/locales/ru/kilocode.json index 5c25402009b..60607d116c7 100644 --- a/webview-ui/src/i18n/locales/ru/kilocode.json +++ b/webview-ui/src/i18n/locales/ru/kilocode.json @@ -26,6 +26,11 @@ "continueWith": "Продолжить с {{model}}", "continue": "Продолжить" }, + "unauthorizedError": { + "title": "Вам нужно войти, чтобы использовать модель {{modelId}}", + "message": "Войдите или создайте учетную запись, чтобы получить доступ к более чем 500 моделям, использовать кредиты по себестоимости или использовать собственный ключ.", + "loginButton": "Войти" + }, "pricing": { "costUnknown": "стоимость неизвестна", "costUnknownDescription": "API-провайдер не предоставил данные о стоимости или запрос был отменен.", diff --git a/webview-ui/src/i18n/locales/th/kilocode.json b/webview-ui/src/i18n/locales/th/kilocode.json index 3b3eb9fd4d9..5ec3373a3df 100644 --- a/webview-ui/src/i18n/locales/th/kilocode.json +++ b/webview-ui/src/i18n/locales/th/kilocode.json @@ -26,6 +26,11 @@ "continueWith": "ดำเนินการต่อด้วย {{model}}", "continue": "ดำเนินการต่อ" }, + "unauthorizedError": { + "title": "คุณต้องเข้าสู่ระบบเพื่อใช้โมเดล {{modelId}}", + "message": "เข้าสู่ระบบหรือสร้างบัญชีเพื่อเข้าถึงโมเดลกว่า 500 โมเดล ใช้เครดิตในราคาต้นทุน หรือใช้คีย์ของคุณเอง", + "loginButton": "เข้าสู่ระบบ" + }, "pricing": { "costUnknown": "ค่าใช้จ่ายไม่ทราบ", "costUnknownDescription": "ผู้ให้บริการ API ไม่ได้ให้ข้อมูลค่าใช้จ่าย หรือคำขอถูกยกเลิก", diff --git a/webview-ui/src/i18n/locales/tr/kilocode.json b/webview-ui/src/i18n/locales/tr/kilocode.json index c592e7c96e1..100a137cda1 100644 --- a/webview-ui/src/i18n/locales/tr/kilocode.json +++ b/webview-ui/src/i18n/locales/tr/kilocode.json @@ -19,6 +19,11 @@ "next": "Sonraki", "page": "Sayfa {{page}} / {{count}}" }, + "unauthorizedError": { + "title": "{{modelId}} modelini kullanmak için giriş yapman gerekiyor", + "message": "500'den fazla modele erişmek, kredileri maliyet fiyatından kullanmak veya kendi anahtarını kullanmak için giriş yap veya hesap oluştur.", + "loginButton": "Giriş Yap" + }, "pricing": { "costUnknown": "maliyet bilinmiyor", "costUnknownDescription": "API Sağlayıcısı herhangi bir maliyet verisi sağlamadı veya istek iptal edildi.", diff --git a/webview-ui/src/i18n/locales/uk/kilocode.json b/webview-ui/src/i18n/locales/uk/kilocode.json index 9ef9a89bd96..4d034a155aa 100644 --- a/webview-ui/src/i18n/locales/uk/kilocode.json +++ b/webview-ui/src/i18n/locales/uk/kilocode.json @@ -26,6 +26,11 @@ "continueWith": "Продовжити з {{model}}", "continue": "Продовжити" }, + "unauthorizedError": { + "title": "Вам потрібно увійти, щоб використовувати модель {{modelId}}", + "message": "Увійдіть або створіть обліковий запис, щоб отримати доступ до понад 500 моделей, використовувати кредити за собівартістю або використовувати власний ключ.", + "loginButton": "Увійти" + }, "pricing": { "costUnknown": "вартість невідома", "costUnknownDescription": "API провайдер не надав дані про вартість або запит було скасовано.", diff --git a/webview-ui/src/i18n/locales/vi/kilocode.json b/webview-ui/src/i18n/locales/vi/kilocode.json index 902f8344a2f..f65b36e4ab6 100644 --- a/webview-ui/src/i18n/locales/vi/kilocode.json +++ b/webview-ui/src/i18n/locales/vi/kilocode.json @@ -26,6 +26,11 @@ "continueWith": "Tiếp tục với {{model}}", "continue": "Tiếp tục" }, + "unauthorizedError": { + "title": "Bạn cần đăng nhập để sử dụng mô hình {{modelId}}", + "message": "Đăng nhập hoặc tạo tài khoản để truy cập hơn 500 mô hình, sử dụng tín dụng với giá gốc hoặc sử dụng khóa của riêng bạn.", + "loginButton": "Đăng Nhập" + }, "pricing": { "costUnknown": "chi phí không xác định", "costUnknownDescription": "Nhà cung cấp API không cung cấp dữ liệu chi phí hoặc yêu cầu đã bị hủy.", diff --git a/webview-ui/src/i18n/locales/zh-CN/kilocode.json b/webview-ui/src/i18n/locales/zh-CN/kilocode.json index dc344c29573..fe0582446f1 100644 --- a/webview-ui/src/i18n/locales/zh-CN/kilocode.json +++ b/webview-ui/src/i18n/locales/zh-CN/kilocode.json @@ -26,6 +26,11 @@ "continueWith": "继续使用 {{model}}", "continue": "继续" }, + "unauthorizedError": { + "title": "你需要登录才能使用 {{modelId}} 模型", + "message": "登录或创建账号即可访问 500+ 模型,按成本价使用额度,或使用自己的 API 密钥。", + "loginButton": "登录" + }, "pricing": { "costUnknown": "费用未知", "costUnknownDescription": "API 提供商未提供任何费用数据或请求已取消。", diff --git a/webview-ui/src/i18n/locales/zh-TW/kilocode.json b/webview-ui/src/i18n/locales/zh-TW/kilocode.json index 97e2ee96d3b..2345390ffe3 100644 --- a/webview-ui/src/i18n/locales/zh-TW/kilocode.json +++ b/webview-ui/src/i18n/locales/zh-TW/kilocode.json @@ -21,6 +21,11 @@ "continueWith": "繼續使用 {{model}}", "continue": "繼續" }, + "unauthorizedError": { + "title": "你需要登入才能使用 {{modelId}} 模型", + "message": "登入或建立帳號即可存取 500+ 模型、按成本價使用額度,或使用自己的 API 金鑰。", + "loginButton": "登入" + }, "pricing": { "costUnknown": "費用未知", "costUnknownDescription": "API 提供商未提供任何費用資料或請求已取消。",