From e168cbdc15bfb7c4b44462e52eb6489fc70c1c62 Mon Sep 17 00:00:00 2001 From: cte Date: Tue, 22 Apr 2025 16:07:40 -0700 Subject: [PATCH 01/20] Throttle calls to calculate task folder size --- src/core/Cline.ts | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 6e42b450aaf..e16de27fb54 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -307,9 +307,11 @@ export class Cline extends EventEmitter { } private async addToApiConversationHistory(message: Anthropic.MessageParam) { - const messageWithTs = { ...message, ts: Date.now() } + const ts = Date.now() + const messageWithTs = { ...message, ts } this.apiConversationHistory.push(messageWithTs) await this.saveApiConversationHistory() + console.log(`addToApiConversationHistory: ${Date.now() - ts}ms`) } async overwriteApiConversationHistory(newHistory: Anthropic.MessageParam[]) { @@ -362,7 +364,13 @@ export class Cline extends EventEmitter { this.emit("message", { action: "updated", message: partialMessage }) } + private taskDirSize = 0 + private taskDirSizeCheckedAt = 0 + private readonly taskDirSizeCheckInterval = 30_000 + private async saveClineMessages() { + const ts = Date.now() + try { const taskDir = await this.ensureTaskDirectoryExists() const filePath = path.join(taskDir, GlobalFileNames.uiMessages) @@ -381,14 +389,15 @@ export class Cline extends EventEmitter { ) ] - let taskDirSize = 0 - - try { - taskDirSize = await getFolderSize.loose(taskDir) - } catch (err) { - console.error( - `[saveClineMessages] failed to get task directory size (${taskDir}): ${err instanceof Error ? err.message : String(err)}`, - ) + if (Date.now() - this.taskDirSizeCheckedAt > this.taskDirSizeCheckInterval) { + try { + this.taskDirSize = await getFolderSize.loose(taskDir) + this.taskDirSizeCheckedAt = Date.now() + } catch (err) { + console.error( + `[saveClineMessages] failed to get task directory size (${taskDir}): ${err instanceof Error ? err.message : String(err)}`, + ) + } } await this.providerRef.deref()?.updateTaskHistory({ @@ -401,12 +410,14 @@ export class Cline extends EventEmitter { cacheWrites: tokenUsage.totalCacheWrites, cacheReads: tokenUsage.totalCacheReads, totalCost: tokenUsage.totalCost, - size: taskDirSize, + size: this.taskDirSize, workspace: this.cwd, }) } catch (error) { console.error("Failed to save cline messages:", error) } + + console.log(`saveClineMessages: ${Date.now() - ts}ms`) } // Communicate with webview @@ -1885,11 +1896,13 @@ export class Cline extends EventEmitter { // now add to apiconversationhistory // need to save assistant responses to file before proceeding to tool use since user can exit at any moment and we wouldn't be able to save the assistant's response let didEndLoop = false + if (assistantMessage.length > 0) { await this.addToApiConversationHistory({ role: "assistant", content: [{ type: "text", text: assistantMessage }], }) + telemetryService.captureConversationMessage(this.taskId, "assistant") // NOTE: this comment is here for future reference - this was a workaround for userMessageContent not getting set to true. It was due to it not recursively calling for partial blocks when didRejectTool, so it would get stuck waiting for a partial block to complete before it could continue. From e72208f6d9cd402974221cce12006f5fdefc3126 Mon Sep 17 00:00:00 2001 From: Daniel <57051444+daniel-lxs@users.noreply.github.com> Date: Wed, 23 Apr 2025 00:09:14 -0500 Subject: [PATCH 02/20] feat: allow variable interpolation into the custom system prompt (#2863) * feat: allow variable interpolation into the custom system prompt * fix: allow the test to pass on windows by using the path module --- .../__tests__/custom-system-prompt.test.ts | 131 ++++++++++++++++++ .../prompts/sections/custom-system-prompt.ts | 27 +++- src/core/prompts/system.ts | 7 +- 3 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 src/core/prompts/sections/__tests__/custom-system-prompt.test.ts diff --git a/src/core/prompts/sections/__tests__/custom-system-prompt.test.ts b/src/core/prompts/sections/__tests__/custom-system-prompt.test.ts new file mode 100644 index 00000000000..9fc538860ae --- /dev/null +++ b/src/core/prompts/sections/__tests__/custom-system-prompt.test.ts @@ -0,0 +1,131 @@ +import path from "path" +import { readFile } from "fs/promises" +import { Mode } from "../../../../shared/modes" // Adjusted import path +import { loadSystemPromptFile, PromptVariables } from "../custom-system-prompt" + +// Mock the fs/promises module +jest.mock("fs/promises") + +// Cast the mocked readFile to the correct Jest mock type +const mockedReadFile = readFile as jest.MockedFunction + +describe("loadSystemPromptFile", () => { + // Corrected PromptVariables type and added mockMode + const mockVariables: PromptVariables = { + workspace: "/path/to/workspace", + } + const mockCwd = "/mock/cwd" + const mockMode: Mode = "test" // Use Mode type, e.g., 'test' + // Corrected expected file path format + const expectedFilePath = path.join(mockCwd, ".roo", `system-prompt-${mockMode}`) + + beforeEach(() => { + // Clear mocks before each test + mockedReadFile.mockClear() + }) + + it("should return an empty string if the file does not exist (ENOENT)", async () => { + const error: NodeJS.ErrnoException = new Error("File not found") + error.code = "ENOENT" + mockedReadFile.mockRejectedValue(error) + + // Added mockMode argument + const result = await loadSystemPromptFile(mockCwd, mockMode, mockVariables) + + expect(result).toBe("") + expect(mockedReadFile).toHaveBeenCalledTimes(1) + expect(mockedReadFile).toHaveBeenCalledWith(expectedFilePath, "utf-8") + }) + + // Updated test: should re-throw unexpected errors + it("should re-throw unexpected errors from readFile", async () => { + const expectedError = new Error("Some other error") + mockedReadFile.mockRejectedValue(expectedError) + + // Assert that the promise rejects with the specific error + await expect(loadSystemPromptFile(mockCwd, mockMode, mockVariables)).rejects.toThrow(expectedError) + + // Verify readFile was still called correctly + expect(mockedReadFile).toHaveBeenCalledTimes(1) + expect(mockedReadFile).toHaveBeenCalledWith(expectedFilePath, "utf-8") + }) + + it("should return an empty string if the file content is empty", async () => { + mockedReadFile.mockResolvedValue("") + + // Added mockMode argument + const result = await loadSystemPromptFile(mockCwd, mockMode, mockVariables) + + expect(result).toBe("") + expect(mockedReadFile).toHaveBeenCalledTimes(1) + expect(mockedReadFile).toHaveBeenCalledWith(expectedFilePath, "utf-8") + }) + + // Updated test to only check workspace interpolation + it("should correctly interpolate workspace variable", async () => { + const template = "Workspace is: {{workspace}}" + mockedReadFile.mockResolvedValue(template) + + // Added mockMode argument + const result = await loadSystemPromptFile(mockCwd, mockMode, mockVariables) + + expect(result).toBe("Workspace is: /path/to/workspace") + expect(mockedReadFile).toHaveBeenCalledTimes(1) + expect(mockedReadFile).toHaveBeenCalledWith(expectedFilePath, "utf-8") + }) + + // Updated test for multiple occurrences of workspace + it("should handle multiple occurrences of the workspace variable", async () => { + const template = "Path: {{workspace}}/{{workspace}}" + mockedReadFile.mockResolvedValue(template) + + // Added mockMode argument + const result = await loadSystemPromptFile(mockCwd, mockMode, mockVariables) + + expect(result).toBe("Path: /path/to/workspace//path/to/workspace") + expect(mockedReadFile).toHaveBeenCalledTimes(1) + expect(mockedReadFile).toHaveBeenCalledWith(expectedFilePath, "utf-8") + }) + + // Updated test for mixed used/unused + it("should handle mixed used workspace and unused variables", async () => { + const template = "Workspace: {{workspace}}, Unused: {{unusedVar}}, Another: {{another}}" + mockedReadFile.mockResolvedValue(template) + + // Added mockMode argument + const result = await loadSystemPromptFile(mockCwd, mockMode, mockVariables) + + // Unused variables should remain untouched + expect(result).toBe("Workspace: /path/to/workspace, Unused: {{unusedVar}}, Another: {{another}}") + expect(mockedReadFile).toHaveBeenCalledTimes(1) + expect(mockedReadFile).toHaveBeenCalledWith(expectedFilePath, "utf-8") + }) + + // Test remains valid, just needs the mode argument and updated template + it("should handle templates with placeholders not present in variables", async () => { + const template = "Workspace: {{workspace}}, Missing: {{missingPlaceholder}}" + mockedReadFile.mockResolvedValue(template) + + // Added mockMode argument + const result = await loadSystemPromptFile(mockCwd, mockMode, mockVariables) + + expect(result).toBe("Workspace: /path/to/workspace, Missing: {{missingPlaceholder}}") + expect(mockedReadFile).toHaveBeenCalledTimes(1) + expect(mockedReadFile).toHaveBeenCalledWith(expectedFilePath, "utf-8") + }) + + // Removed the test for extra keys as PromptVariables is simple now + + // Test remains valid, just needs the mode argument + it("should handle template with no variables", async () => { + const template = "This is a static prompt." + mockedReadFile.mockResolvedValue(template) + + // Added mockMode argument + const result = await loadSystemPromptFile(mockCwd, mockMode, mockVariables) + + expect(result).toBe("This is a static prompt.") + expect(mockedReadFile).toHaveBeenCalledTimes(1) + expect(mockedReadFile).toHaveBeenCalledWith(expectedFilePath, "utf-8") + }) +}) diff --git a/src/core/prompts/sections/custom-system-prompt.ts b/src/core/prompts/sections/custom-system-prompt.ts index eca2b98b8d8..b8353e2fd82 100644 --- a/src/core/prompts/sections/custom-system-prompt.ts +++ b/src/core/prompts/sections/custom-system-prompt.ts @@ -3,6 +3,24 @@ import path from "path" import { Mode } from "../../../shared/modes" import { fileExistsAtPath } from "../../../utils/fs" +export type PromptVariables = { + workspace?: string +} + +function interpolatePromptContent(content: string, variables: PromptVariables): string { + let interpolatedContent = content + for (const key in variables) { + if ( + Object.prototype.hasOwnProperty.call(variables, key) && + variables[key as keyof PromptVariables] !== undefined + ) { + const placeholder = new RegExp(`\\{\\{${key}\\}\\}`, "g") + interpolatedContent = interpolatedContent.replace(placeholder, variables[key as keyof PromptVariables]!) + } + } + return interpolatedContent +} + /** * Safely reads a file, returning an empty string if the file doesn't exist */ @@ -31,9 +49,14 @@ export function getSystemPromptFilePath(cwd: string, mode: Mode): string { * Loads custom system prompt from a file at .roo/system-prompt-[mode slug] * If the file doesn't exist, returns an empty string */ -export async function loadSystemPromptFile(cwd: string, mode: Mode): Promise { +export async function loadSystemPromptFile(cwd: string, mode: Mode, variables: PromptVariables): Promise { const filePath = getSystemPromptFilePath(cwd, mode) - return safeReadFile(filePath) + const rawContent = await safeReadFile(filePath) + if (!rawContent) { + return "" + } + const interpolatedContent = interpolatePromptContent(rawContent, variables) + return interpolatedContent } /** diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index 22b406e8351..077016fd55d 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -9,6 +9,7 @@ import { getModeBySlug, getGroupName, } from "../../shared/modes" +import { PromptVariables } from "./sections/custom-system-prompt" import { DiffStrategy } from "../../shared/tools" import { McpHub } from "../../services/mcp/McpHub" import { getToolDescriptionsForMode } from "./tools" @@ -125,7 +126,10 @@ export const SYSTEM_PROMPT = async ( } // Try to load custom system prompt from file - const fileCustomSystemPrompt = await loadSystemPromptFile(cwd, mode) + const variablesForPrompt: PromptVariables = { + workspace: cwd, + } + const fileCustomSystemPrompt = await loadSystemPromptFile(cwd, mode, variablesForPrompt) // Check if it's a custom mode const promptComponent = getPromptComponent(customModePrompts?.[mode]) @@ -143,6 +147,7 @@ export const SYSTEM_PROMPT = async ( mode, { language: language ?? formatLanguage(vscode.env.language), rooIgnoreInstructions }, ) + // For file-based prompts, don't include the tool sections return `${roleDefinition} From 6f26cb59dc92d707bd28fdd100adce64ace31df7 Mon Sep 17 00:00:00 2001 From: Hannes Rudolph Date: Wed, 23 Apr 2025 00:35:10 -0600 Subject: [PATCH 03/20] fix: allow opening files without workspace root (#1054) * fix: allow opening files without workspace root The openFile function in open-file.ts was requiring a workspace root to be present, which prevented opening global files (like MCP settings) when no workspace was open. Modified the function to handle absolute paths without this requirement. Previously, trying to open MCP settings in a new window without a workspace would error with "Could not open file: No workspace root found". Now the function properly handles both workspace-relative and absolute paths, allowing global settings files to be accessed in any context. Changes: - Removed workspace root requirement in openFile - Added fallback for relative paths when no workspace is present * fix: update openFile function to use provided path without modification --------- Co-authored-by: Roo Code Co-authored-by: Matt Rubens --- src/integrations/misc/open-file.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/integrations/misc/open-file.ts b/src/integrations/misc/open-file.ts index 5698e919de1..b3724068b62 100644 --- a/src/integrations/misc/open-file.ts +++ b/src/integrations/misc/open-file.ts @@ -29,12 +29,14 @@ export async function openFile(filePath: string, options: OpenFileOptions = {}) try { // Get workspace root const workspaceRoot = getWorkspacePath() - if (!workspaceRoot) { - throw new Error("No workspace root found") - } - // If path starts with ./, resolve it relative to workspace root - const fullPath = filePath.startsWith("./") ? path.join(workspaceRoot, filePath.slice(2)) : filePath + // If path starts with ./, resolve it relative to workspace root if available + // Otherwise, use the path as provided without modification + const fullPath = filePath.startsWith("./") + ? workspaceRoot + ? path.join(workspaceRoot, filePath.slice(2)) + : filePath + : filePath const uri = vscode.Uri.file(fullPath) From 92171feb39166abe8e6ca75440589bc151059947 Mon Sep 17 00:00:00 2001 From: seedlord Date: Wed, 23 Apr 2025 08:45:35 +0200 Subject: [PATCH 04/20] Fix: Preserve editor state and prevent tab unpinning during diffs (#2857) - Maintains editor view column state when closing and reopening files during diff operations, ensuring tabs stay opened in their original position. - Prevents closing the original editor tab when opening the diff view, preserving pinned status when applying changes via write_to_file or apply_diff. - Updates VSCode workspace launch flag from -n to -W for compatibility. --- evals/apps/cli/src/index.ts | 2 +- src/integrations/editor/DiffViewProvider.ts | 69 ++++++++++++++++++--- 2 files changed, 61 insertions(+), 10 deletions(-) diff --git a/evals/apps/cli/src/index.ts b/evals/apps/cli/src/index.ts index 6b287042b08..c2213140766 100644 --- a/evals/apps/cli/src/index.ts +++ b/evals/apps/cli/src/index.ts @@ -190,7 +190,7 @@ const runExercise = async ({ run, task, server }: { run: Run; task: Task; server ROO_CODE_IPC_SOCKET_PATH: taskSocketPath, }, shell: "/bin/bash", - })`code --disable-workspace-trust -n ${workspacePath}` + })`code --disable-workspace-trust -W ${workspacePath}` // Give VSCode some time to spawn before connecting to its unix socket. await new Promise((resolve) => setTimeout(resolve, 3_000)) diff --git a/src/integrations/editor/DiffViewProvider.ts b/src/integrations/editor/DiffViewProvider.ts index 0bf494854a4..e225230e0b8 100644 --- a/src/integrations/editor/DiffViewProvider.ts +++ b/src/integrations/editor/DiffViewProvider.ts @@ -17,6 +17,7 @@ export class DiffViewProvider { originalContent: string | undefined private createdDirs: string[] = [] private documentWasOpen = false + private originalViewColumn?: vscode.ViewColumn // Store the original view column private relPath?: string private newContent?: string private activeDiffEditor?: vscode.TextEditor @@ -65,11 +66,22 @@ export class DiffViewProvider { .filter( (tab) => tab.input instanceof vscode.TabInputText && arePathsEqual(tab.input.uri.fsPath, absolutePath), ) + // Check if the document is already open and store its state + // DO NOT close the original tab to preserve pin status for (const tab of tabs) { - if (!tab.isDirty) { - await vscode.window.tabGroups.close(tab) + if (tab.input instanceof vscode.TabInputText && arePathsEqual(tab.input.uri.fsPath, absolutePath)) { + this.originalViewColumn = tab.group.viewColumn + this.documentWasOpen = true + // Ensure the tab is not dirty before proceeding, but don't close it + if (tab.isDirty) { + // Find the document associated with the tab and save it + const doc = vscode.workspace.textDocuments.find((d) => arePathsEqual(d.uri.fsPath, absolutePath)) + if (doc) { + await doc.save() + } + } + break // Found the relevant tab, no need to check others } - this.documentWasOpen = true } this.activeDiffEditor = await this.openDiffEditor() this.fadedOverlayController = new DecorationController("fadedOverlay", this.activeDiffEditor) @@ -156,9 +168,31 @@ export class DiffViewProvider { await updatedDocument.save() } - await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { preview: false }) + // Close the diff view first await this.closeAllDiffViews() + // If the original document was open, try to focus it. + // VS Code should handle showing the updated content automatically since the file was saved. + if (this.documentWasOpen && this.originalViewColumn) { + // Find the editor for the original document and reveal it + const originalEditor = vscode.window.visibleTextEditors.find( + (editor) => + arePathsEqual(editor.document.uri.fsPath, absolutePath) && + editor.viewColumn === this.originalViewColumn, + ) + if (originalEditor) { + // Reveal a range (e.g., the start) to ensure focus + const position = new vscode.Position(0, 0) + originalEditor.revealRange(new vscode.Range(position, position), vscode.TextEditorRevealType.AtTop) + } else { + // Fallback if editor not found (shouldn't happen often if documentWasOpen is true) + await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { + preview: false, + viewColumn: this.originalViewColumn, + }) + } + } + /* Getting diagnostics before and after the file edit is a better approach than automatically tracking problems in real-time. This method ensures we only @@ -237,12 +271,28 @@ export class DiffViewProvider { await vscode.workspace.applyEdit(edit) await updatedDocument.save() console.log(`File ${absolutePath} has been reverted to its original content.`) - if (this.documentWasOpen) { - await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { - preview: false, - }) - } + // Close the diff view first await this.closeAllDiffViews() + + // If the document was originally open, ensure it's focused. + // The revert logic already applied the original content and saved. + if (this.documentWasOpen && this.originalViewColumn) { + const originalEditor = vscode.window.visibleTextEditors.find( + (editor) => + arePathsEqual(editor.document.uri.fsPath, absolutePath) && + editor.viewColumn === this.originalViewColumn, + ) + if (originalEditor) { + const position = new vscode.Position(0, 0) + originalEditor.revealRange(new vscode.Range(position, position), vscode.TextEditorRevealType.AtTop) + } else { + // Fallback + await vscode.window.showTextDocument(vscode.Uri.file(absolutePath), { + preview: false, + viewColumn: this.originalViewColumn, + }) + } + } } // edit is done @@ -358,6 +408,7 @@ export class DiffViewProvider { this.originalContent = undefined this.createdDirs = [] this.documentWasOpen = false + this.originalViewColumn = undefined // Reset stored view column this.activeDiffEditor = undefined this.fadedOverlayController = undefined this.activeLineController = undefined From 40d17080fd007a71fb485eee00777329ae1cb17c Mon Sep 17 00:00:00 2001 From: Alfredo Medrano Date: Wed, 23 Apr 2025 01:05:47 -0600 Subject: [PATCH 05/20] Bugfix/fix vscodellm model information (#2832) * feat: initialize VS Code Language Model client in constructor * feat: add VS Code LLM models and configuration * feat: integrate VS Code LLM models into API configuration normalization * Fix tests --------- Co-authored-by: Matt Rubens --- src/api/providers/__tests__/vscode-lm.test.ts | 24 ++- src/api/providers/vscode-lm.ts | 26 ++- src/shared/api.ts | 183 ++++++++++++++++++ .../src/components/settings/ApiOptions.tsx | 14 +- 4 files changed, 232 insertions(+), 15 deletions(-) diff --git a/src/api/providers/__tests__/vscode-lm.test.ts b/src/api/providers/__tests__/vscode-lm.test.ts index 34e0d60b1d6..d7e674d0450 100644 --- a/src/api/providers/__tests__/vscode-lm.test.ts +++ b/src/api/providers/__tests__/vscode-lm.test.ts @@ -134,6 +134,9 @@ describe("VsCodeLmHandler", () => { const mockModel = { ...mockLanguageModelChat } ;(vscode.lm.selectChatModels as jest.Mock).mockResolvedValueOnce([mockModel]) mockLanguageModelChat.countTokens.mockResolvedValue(10) + + // Override the default client with our test client + handler["client"] = mockLanguageModelChat }) it("should stream text responses", async () => { @@ -229,12 +232,7 @@ describe("VsCodeLmHandler", () => { mockLanguageModelChat.sendRequest.mockRejectedValueOnce(new Error("API Error")) - await expect(async () => { - const stream = handler.createMessage(systemPrompt, messages) - for await (const _ of stream) { - // consume stream - } - }).rejects.toThrow("API Error") + await expect(handler.createMessage(systemPrompt, messages).next()).rejects.toThrow("API Error") }) }) @@ -253,6 +251,8 @@ describe("VsCodeLmHandler", () => { }) it("should return fallback model info when no client exists", () => { + // Clear the client first + handler["client"] = null const model = handler.getModel() expect(model.id).toBe("test-vendor/test-family") expect(model.info).toBeDefined() @@ -276,6 +276,10 @@ describe("VsCodeLmHandler", () => { })(), }) + // Override the default client with our test client to ensure it uses + // the mock implementation rather than the default fallback + handler["client"] = mockLanguageModelChat + const result = await handler.completePrompt("Test prompt") expect(result).toBe(responseText) expect(mockLanguageModelChat.sendRequest).toHaveBeenCalled() @@ -287,9 +291,11 @@ describe("VsCodeLmHandler", () => { mockLanguageModelChat.sendRequest.mockRejectedValueOnce(new Error("Completion failed")) - await expect(handler.completePrompt("Test prompt")).rejects.toThrow( - "VSCode LM completion error: Completion failed", - ) + // Make sure we're using the mock client + handler["client"] = mockLanguageModelChat + + const promise = handler.completePrompt("Test prompt") + await expect(promise).rejects.toThrow("VSCode LM completion error: Completion failed") }) }) }) diff --git a/src/api/providers/vscode-lm.ts b/src/api/providers/vscode-lm.ts index 1b5f5736375..af998389c27 100644 --- a/src/api/providers/vscode-lm.ts +++ b/src/api/providers/vscode-lm.ts @@ -61,6 +61,7 @@ export class VsCodeLmHandler extends BaseProvider implements SingleCompletionHan } } }) + this.initializeClient() } catch (error) { // Ensure cleanup if constructor fails this.dispose() @@ -70,7 +71,30 @@ export class VsCodeLmHandler extends BaseProvider implements SingleCompletionHan ) } } - + /** + * Initializes the VS Code Language Model client. + * This method is called during the constructor to set up the client. + * This useful when the client is not created yet and call getModel() before the client is created. + * @returns Promise + * @throws Error when client initialization fails + */ + async initializeClient(): Promise { + try { + // Check if the client is already initialized + if (this.client) { + console.debug("Roo Code : Client already initialized") + return + } + // Create a new client instance + this.client = await this.createClient(this.options.vsCodeLmModelSelector || {}) + console.debug("Roo Code : Client initialized successfully") + } catch (error) { + // Handle errors during client initialization + const errorMessage = error instanceof Error ? error.message : "Unknown error" + console.error("Roo Code : Client initialization failed:", errorMessage) + throw new Error(`Roo Code : Failed to initialize client: ${errorMessage}`) + } + } /** * Creates a language model chat client based on the provided selector. * diff --git a/src/shared/api.ts b/src/shared/api.ts index 825a95bc768..34ec801187f 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -1179,3 +1179,186 @@ export const xaiModels = { description: "xAI's Grok Beta model (legacy) with 131K context window", }, } as const satisfies Record + +export type VscodeLlmModelId = keyof typeof vscodeLlmModels +export const vscodeLlmDefaultModelId: VscodeLlmModelId = "claude-3.5-sonnet" +export const vscodeLlmModels = { + "gpt-3.5-turbo": { + contextWindow: 12114, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + family: "gpt-3.5-turbo", + version: "gpt-3.5-turbo-0613", + name: "GPT 3.5 Turbo", + supportsToolCalling: true, + maxInputTokens: 12114, + }, + "gpt-4o-mini": { + contextWindow: 12115, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + family: "gpt-4o-mini", + version: "gpt-4o-mini-2024-07-18", + name: "GPT-4o mini", + supportsToolCalling: true, + maxInputTokens: 12115, + }, + "gpt-4": { + contextWindow: 28501, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + family: "gpt-4", + version: "gpt-4-0613", + name: "GPT 4", + supportsToolCalling: true, + maxInputTokens: 28501, + }, + "gpt-4-0125-preview": { + contextWindow: 63826, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + family: "gpt-4-turbo", + version: "gpt-4-0125-preview", + name: "GPT 4 Turbo", + supportsToolCalling: true, + maxInputTokens: 63826, + }, + "gpt-4o": { + contextWindow: 63827, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + family: "gpt-4o", + version: "gpt-4o-2024-11-20", + name: "GPT-4o", + supportsToolCalling: true, + maxInputTokens: 63827, + }, + o1: { + contextWindow: 19827, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + family: "o1-ga", + version: "o1-2024-12-17", + name: "o1 (Preview)", + supportsToolCalling: true, + maxInputTokens: 19827, + }, + "o3-mini": { + contextWindow: 63827, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + family: "o3-mini", + version: "o3-mini-2025-01-31", + name: "o3-mini", + supportsToolCalling: true, + maxInputTokens: 63827, + }, + "claude-3.5-sonnet": { + contextWindow: 81638, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + family: "claude-3.5-sonnet", + version: "claude-3.5-sonnet", + name: "Claude 3.5 Sonnet", + supportsToolCalling: true, + maxInputTokens: 81638, + }, + "claude-3.7-sonnet": { + contextWindow: 89827, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + family: "claude-3.7-sonnet", + version: "claude-3.7-sonnet", + name: "Claude 3.7 Sonnet", + supportsToolCalling: true, + maxInputTokens: 89827, + }, + "claude-3.7-sonnet-thought": { + contextWindow: 89827, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + family: "claude-3.7-sonnet-thought", + version: "claude-3.7-sonnet-thought", + name: "Claude 3.7 Sonnet Thinking", + supportsToolCalling: false, + maxInputTokens: 89827, + thinking: true, + }, + "gemini-2.0-flash-001": { + contextWindow: 127827, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + family: "gemini-2.0-flash", + version: "gemini-2.0-flash-001", + name: "Gemini 2.0 Flash", + supportsToolCalling: false, + maxInputTokens: 127827, + }, + "gemini-2.5-pro": { + contextWindow: 63830, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + family: "gemini-2.5-pro", + version: "gemini-2.5-pro-preview-03-25", + name: "Gemini 2.5 Pro (Preview)", + supportsToolCalling: true, + maxInputTokens: 63830, + }, + "o4-mini": { + contextWindow: 111446, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + family: "o4-mini", + version: "o4-mini-2025-04-16", + name: "o4-mini (Preview)", + supportsToolCalling: true, + maxInputTokens: 111446, + }, + "gpt-4.1": { + contextWindow: 111446, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + family: "gpt-4.1", + version: "gpt-4.1-2025-04-14", + name: "GPT-4.1 (Preview)", + supportsToolCalling: true, + maxInputTokens: 111446, + }, +} as const satisfies Record< + string, + ModelInfo & { + family: string + version: string + name: string + supportsToolCalling: boolean + maxInputTokens: number + } +> diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 9381690b307..156b24c697e 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -38,6 +38,8 @@ import { xaiDefaultModelId, xaiModels, ApiProvider, + vscodeLlmModels, + vscodeLlmDefaultModelId, } from "@roo/shared/api" import { ExtensionMessage } from "@roo/shared/ExtensionMessage" @@ -1738,7 +1740,6 @@ const ApiOptions = ({ export function normalizeApiConfiguration(apiConfiguration?: ApiConfiguration) { const provider = apiConfiguration?.apiProvider || "anthropic" const modelId = apiConfiguration?.apiModelId - const getProviderData = (models: Record, defaultId: string) => { let selectedModelId: string let selectedModelInfo: ModelInfo @@ -1827,15 +1828,18 @@ export function normalizeApiConfiguration(apiConfiguration?: ApiConfiguration) { selectedModelInfo: openAiModelInfoSaneDefaults, } case "vscode-lm": + const modelFamily = apiConfiguration?.vsCodeLmModelSelector?.family ?? vscodeLlmDefaultModelId + const modelInfo = { + ...openAiModelInfoSaneDefaults, + ...vscodeLlmModels[modelFamily as keyof typeof vscodeLlmModels], + supportsImages: false, // VSCode LM API currently doesn't support images. + } return { selectedProvider: provider, selectedModelId: apiConfiguration?.vsCodeLmModelSelector ? `${apiConfiguration.vsCodeLmModelSelector.vendor}/${apiConfiguration.vsCodeLmModelSelector.family}` : "", - selectedModelInfo: { - ...openAiModelInfoSaneDefaults, - supportsImages: false, // VSCode LM API currently doesn't support images. - }, + selectedModelInfo: modelInfo, } default: return getProviderData(anthropicModels, anthropicDefaultModelId) From 4563b53e44a5499213f0e471b0e835c9cb1c19d3 Mon Sep 17 00:00:00 2001 From: hongzio <11085613+hongzio@users.noreply.github.com> Date: Wed, 23 Apr 2025 16:06:48 +0900 Subject: [PATCH 06/20] Fix: focusInput open roo code panel (#2626) (#2817) * Fix: focusInput open roo code panel (#2626) * Fix: `roo-cline.focusInput` open roo code panel * fixup! Fix: focusInput open roo code panel (#2626) --- src/activate/registerCommands.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/activate/registerCommands.ts b/src/activate/registerCommands.ts index 486566357bc..1883083b6ee 100644 --- a/src/activate/registerCommands.ts +++ b/src/activate/registerCommands.ts @@ -114,8 +114,20 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt const { promptForCustomStoragePath } = await import("../shared/storagePathManager") await promptForCustomStoragePath() }, - "roo-cline.focusInput": () => { - provider.postMessageToWebview({ type: "action", action: "focusInput" }) + "roo-cline.focusInput": async () => { + try { + const panel = getPanel() + if (!panel) { + await vscode.commands.executeCommand("workbench.view.extension.roo-cline-ActivityBar") + } else if (panel === tabPanel) { + panel.reveal(vscode.ViewColumn.Active, false) + } else if (panel === sidebarPanel) { + await vscode.commands.executeCommand(`${ClineProvider.sideBarId}.focus`) + provider.postMessageToWebview({ type: "action", action: "focusInput" }) + } + } catch (error) { + outputChannel.appendLine(`Error focusing input: ${error}`) + } }, "roo.acceptInput": () => { const visibleProvider = getVisibleProviderOrLog(outputChannel) From 5059636dfffffe87c3d18f7a4a7457474397155d Mon Sep 17 00:00:00 2001 From: Dicha Zelianivan Arkana <51877647+elianiva@users.noreply.github.com> Date: Wed, 23 Apr 2025 14:09:29 +0700 Subject: [PATCH 07/20] fix(mention): conditionally remove aftercursor content (#2732) --- webview-ui/src/utils/context-mentions.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/webview-ui/src/utils/context-mentions.ts b/webview-ui/src/utils/context-mentions.ts index 9302598bc00..293aa8f74bb 100644 --- a/webview-ui/src/utils/context-mentions.ts +++ b/webview-ui/src/utils/context-mentions.ts @@ -33,7 +33,12 @@ export function insertMention( if (lastAtIndex !== -1) { // If there's an '@' symbol, replace everything after it with the new mention const beforeMention = text.slice(0, lastAtIndex) - newValue = beforeMention + "@" + value + " " + afterCursor.replace(/^[^\s]*/, "") + // Only replace if afterCursor is all alphanumerical + // This is required to handle languages that don't use space as a word separator (chinese, japanese, korean, etc) + const afterCursorContent = /^[a-zA-Z0-9\s]*$/.test(afterCursor) + ? afterCursor.replace(/^[^\s]*/, "") + : afterCursor + newValue = beforeMention + "@" + value + " " + afterCursorContent mentionIndex = lastAtIndex } else { // If there's no '@' symbol, insert the mention at the cursor position From d3d96bc42da794c04da907437bf73659fab657d9 Mon Sep 17 00:00:00 2001 From: Trung Dang Date: Wed, 23 Apr 2025 14:13:04 +0700 Subject: [PATCH 08/20] feat: add `injectEnv` util, support env ref in mcp config (#2679) * feat: support environment variables reference in mcp `env` config * tests(src/utils/config): add test for `injectEnv` * fix(injectEnv): use `env == null` and `??` check instead of `!env`, `||` * refactor: remove unnecessary type declare * chore!: simplify regexp, remove replacement for env vars with dots --- src/services/mcp/McpHub.ts | 3 +- src/utils/__tests__/config.test.ts | 100 +++++++++++++++++++++++++++++ src/utils/config.ts | 25 ++++++++ 3 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 src/utils/__tests__/config.test.ts create mode 100644 src/utils/config.ts diff --git a/src/services/mcp/McpHub.ts b/src/services/mcp/McpHub.ts index 31d0dd8020e..59349ffdbcc 100644 --- a/src/services/mcp/McpHub.ts +++ b/src/services/mcp/McpHub.ts @@ -30,6 +30,7 @@ import { } from "../../shared/mcp" import { fileExistsAtPath } from "../../utils/fs" import { arePathsEqual } from "../../utils/path" +import { injectEnv } from "../../utils/config" export type McpConnection = { server: McpServer @@ -452,7 +453,7 @@ export class McpHub { args: config.args, cwd: config.cwd, env: { - ...config.env, + ...(config.env ? await injectEnv(config.env) : {}), ...(process.env.PATH ? { PATH: process.env.PATH } : {}), }, stderr: "pipe", diff --git a/src/utils/__tests__/config.test.ts b/src/utils/__tests__/config.test.ts new file mode 100644 index 00000000000..c5e9e2ba880 --- /dev/null +++ b/src/utils/__tests__/config.test.ts @@ -0,0 +1,100 @@ +import { injectEnv } from "../config" + +describe("injectEnv", () => { + const originalEnv = process.env + + beforeEach(() => { + // Assign a new / reset process.env before each test + jest.resetModules() + process.env = { ...originalEnv } + }) + + afterAll(() => { + // Restore original process.env after all tests + process.env = originalEnv + }) + + it("should replace env variables in a string", async () => { + process.env.TEST_VAR = "testValue" + const configString = "Hello ${env:TEST_VAR}" + const expectedString = "Hello testValue" + const result = await injectEnv(configString) + expect(result).toBe(expectedString) + }) + + it("should replace env variables in an object", async () => { + process.env.API_KEY = "12345" + process.env.ENDPOINT = "https://example.com" + const configObject = { + key: "${env:API_KEY}", + url: "${env:ENDPOINT}", + nested: { + value: "Keep this ${env:API_KEY}", + }, + } + const expectedObject = { + key: "12345", + url: "https://example.com", + nested: { + value: "Keep this 12345", + }, + } + const result = await injectEnv(configObject) + expect(result).toEqual(expectedObject) + }) + + it("should use notFoundValue for missing env variables", async () => { + const consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation() + process.env.EXISTING_VAR = "exists" + const configString = "Value: ${env:EXISTING_VAR}, Missing: ${env:MISSING_VAR}" + const expectedString = "Value: exists, Missing: NOT_FOUND" + const result = await injectEnv(configString, "NOT_FOUND") + expect(result).toBe(expectedString) + expect(consoleWarnSpy).toHaveBeenCalledWith( + "[injectEnv] env variable MISSING_VAR referenced but not found in process.env", + ) + consoleWarnSpy.mockRestore() + }) + + it("should use default empty string for missing env variables if notFoundValue is not provided", async () => { + const consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation() + const configString = "Missing: ${env:ANOTHER_MISSING}" + const expectedString = "Missing: " + const result = await injectEnv(configString) + expect(result).toBe(expectedString) + expect(consoleWarnSpy).toHaveBeenCalledWith( + "[injectEnv] env variable ANOTHER_MISSING referenced but not found in process.env", + ) + consoleWarnSpy.mockRestore() + }) + + it("should handle strings without env variables", async () => { + const configString = "Just a regular string" + const result = await injectEnv(configString) + expect(result).toBe(configString) + }) + + it("should handle objects without env variables", async () => { + const configObject = { key: "value", number: 123 } + const result = await injectEnv(configObject) + expect(result).toEqual(configObject) + }) + + it("should not mutate the original object", async () => { + process.env.MUTATE_TEST = "mutated" + const originalObject = { value: "${env:MUTATE_TEST}" } + const copyOfOriginal = { ...originalObject } // Shallow copy for comparison + await injectEnv(originalObject) + expect(originalObject).toEqual(copyOfOriginal) // Check if the original object remains unchanged + }) + + it("should handle empty string input", async () => { + const result = await injectEnv("") + expect(result).toBe("") + }) + + it("should handle empty object input", async () => { + const result = await injectEnv({}) + expect(result).toEqual({}) + }) +}) diff --git a/src/utils/config.ts b/src/utils/config.ts new file mode 100644 index 00000000000..24bf9cbdecc --- /dev/null +++ b/src/utils/config.ts @@ -0,0 +1,25 @@ +/** + * Deeply injects environment variables into a configuration object/string/json + * + * Uses VSCode env:name pattern: https://code.visualstudio.com/docs/reference/variables-reference#_environment-variables + * + * Does not mutate original object + */ +export async function injectEnv(config: string | Record, notFoundValue: any = "") { + // Use simple regex replace for now, will see if object traversal and recursion is needed here (e.g: for non-serializable objects) + + const isObject = typeof config === "object" + let _config = isObject ? JSON.stringify(config) : config + + _config = _config.replace(/\$\{env:([\w]+)\}/g, (_, name) => { + // Check if null or undefined + // intentionally using == to match null | undefined + // eslint-disable-next-line eqeqeq + if (process.env[name] == null) + console.warn(`[injectEnv] env variable ${name} referenced but not found in process.env`) + + return process.env[name] ?? notFoundValue + }) + + return isObject ? JSON.parse(_config) : _config +} From fd0fa0afd1b097f43d5bfec9f280e7d5ee6b7880 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 23 Apr 2025 03:14:08 -0400 Subject: [PATCH 09/20] Update contributors list (#2867) docs: update contributors list [skip ci] Co-authored-by: mrubens --- README.md | 48 ++++++++++++++++++++--------------------- locales/ca/README.md | 28 ++++++++++++------------ locales/de/README.md | 28 ++++++++++++------------ locales/es/README.md | 28 ++++++++++++------------ locales/fr/README.md | 28 ++++++++++++------------ locales/hi/README.md | 28 ++++++++++++------------ locales/it/README.md | 28 ++++++++++++------------ locales/ja/README.md | 28 ++++++++++++------------ locales/ko/README.md | 28 ++++++++++++------------ locales/pl/README.md | 28 ++++++++++++------------ locales/pt-BR/README.md | 28 ++++++++++++------------ locales/tr/README.md | 28 ++++++++++++------------ locales/vi/README.md | 28 ++++++++++++------------ locales/zh-CN/README.md | 28 ++++++++++++------------ locales/zh-TW/README.md | 28 ++++++++++++------------ 15 files changed, 220 insertions(+), 220 deletions(-) diff --git a/README.md b/README.md index 81fc0781de2..d14e93d9641 100644 --- a/README.md +++ b/README.md @@ -181,30 +181,30 @@ Thanks to all our contributors who have helped make Roo Code better! -| mrubens
mrubens
| saoudrizwan
saoudrizwan
| cte
cte
| samhvw8
samhvw8
| daniel-lxs
daniel-lxs
| a8trejo
a8trejo
| -| :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -| ColemanRoo
ColemanRoo
| stea9499
stea9499
| joemanley201
joemanley201
| System233
System233
| hannesrudolph
hannesrudolph
| nissa-seru
nissa-seru
| -| jquanton
jquanton
| KJ7LNW
KJ7LNW
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| punkpeye
punkpeye
| -| monotykamary
monotykamary
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| feifei325
feifei325
| wkordalski
wkordalski
| cannuri
cannuri
| lloydchang
lloydchang
| -| vigneshsubbiah16
vigneshsubbiah16
| Szpadel
Szpadel
| lupuletic
lupuletic
| qdaxb
qdaxb
| Premshay
Premshay
| psv2522
psv2522
| -| diarmidmackenzie
diarmidmackenzie
| olweraltuve
olweraltuve
| nbihan-mediware
nbihan-mediware
| PeterDaveHello
PeterDaveHello
| RaySinner
RaySinner
| sachasayan
sachasayan
| -| aheizi
aheizi
| afshawnlotfi
afshawnlotfi
| pugazhendhi-m
pugazhendhi-m
| pdecat
pdecat
| kyle-apex
kyle-apex
| emshvac
emshvac
| -| dtrugman
dtrugman
| Lunchb0ne
Lunchb0ne
| arthurauffray
arthurauffray
| zhangtony239
zhangtony239
| upamune
upamune
| StevenTCramer
StevenTCramer
| -| sammcj
sammcj
| p12tic
p12tic
| gtaylor
gtaylor
| elianiva
elianiva
| aitoroses
aitoroses
| yt3trees
yt3trees
| -| franekp
franekp
| yongjer
yongjer
| vincentsong
vincentsong
| vagadiya
vagadiya
| teddyOOXX
teddyOOXX
| eonghk
eonghk
| -| taisukeoe
taisukeoe
| heyseth
heyseth
| ross
ross
| axkirillov
axkirillov
| anton-otee
anton-otee
| benzntech
benzntech
| -| bramburn
bramburn
| GitlyHallows
GitlyHallows
| philfung
philfung
| napter
napter
| mdp
mdp
| jcbdev
jcbdev
| -| Chenjiayuan195
Chenjiayuan195
| SplittyDev
SplittyDev
| amittell
amittell
| ashktn
ashktn
| axmo
axmo
| bannzai
bannzai
| -| dairui1
dairui1
| dqroid
dqroid
| im47cn
im47cn
| shoopapa
shoopapa
| jwcraig
jwcraig
| kinandan
kinandan
| -| kohii
kohii
| lightrabbit
lightrabbit
| olup
olup
| mecab
mecab
| moqimoqidea
moqimoqidea
| mosleyit
mosleyit
| -| nevermorec
nevermorec
| nobu007
nobu007
| oprstchn
oprstchn
| philipnext
philipnext
| pokutuna
pokutuna
| refactorthis
refactorthis
| -| ronyblum
ronyblum
| samir-nimbly
samir-nimbly
| shaybc
shaybc
| shohei-ihaya
shohei-ihaya
| student20880
student20880
| cdlliuy
cdlliuy
| -| PretzelVector
PretzelVector
| AMHesch
AMHesch
| adamwlarson
adamwlarson
| alarno
alarno
| andreastempsch
andreastempsch
| atlasgong
atlasgong
| -| Atlogit
Atlogit
| bogdan0083
bogdan0083
| chadgauth
chadgauth
| dleen
dleen
| dbasclpy
dbasclpy
| snoyiatk
snoyiatk
| -| linegel
linegel
| celestial-vault
celestial-vault
| DeXtroTip
DeXtroTip
| hesara
hesara
| eltociear
eltociear
| Jdo300
Jdo300
| -| shtse8
shtse8
| libertyteeth
libertyteeth
| mamertofabian
mamertofabian
| marvijo-code
marvijo-code
| kvokka
kvokka
| Sarke
Sarke
| -| 01Rian
01Rian
| samsilveira
samsilveira
| maekawataiki
maekawataiki
| tgfjt
tgfjt
| tmsjngx0
tmsjngx0
| vladstudio
vladstudio
| -| Yoshino-Yukitaro
Yoshino-Yukitaro
| | | | | | +| mrubens
mrubens
| saoudrizwan
saoudrizwan
| cte
cte
| samhvw8
samhvw8
| daniel-lxs
daniel-lxs
| a8trejo
a8trejo
| +| :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| ColemanRoo
ColemanRoo
| stea9499
stea9499
| joemanley201
joemanley201
| System233
System233
| hannesrudolph
hannesrudolph
| nissa-seru
nissa-seru
| +| jquanton
jquanton
| KJ7LNW
KJ7LNW
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| punkpeye
punkpeye
| +| monotykamary
monotykamary
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| feifei325
feifei325
| wkordalski
wkordalski
| cannuri
cannuri
| lloydchang
lloydchang
| +| vigneshsubbiah16
vigneshsubbiah16
| Szpadel
Szpadel
| lupuletic
lupuletic
| qdaxb
qdaxb
| Premshay
Premshay
| psv2522
psv2522
| +| diarmidmackenzie
diarmidmackenzie
| olweraltuve
olweraltuve
| afshawnlotfi
afshawnlotfi
| aheizi
aheizi
| sachasayan
sachasayan
| RaySinner
RaySinner
| +| PeterDaveHello
PeterDaveHello
| nbihan-mediware
nbihan-mediware
| elianiva
elianiva
| dtrugman
dtrugman
| emshvac
emshvac
| kyle-apex
kyle-apex
| +| pdecat
pdecat
| pugazhendhi-m
pugazhendhi-m
| Lunchb0ne
Lunchb0ne
| arthurauffray
arthurauffray
| zhangtony239
zhangtony239
| upamune
upamune
| +| StevenTCramer
StevenTCramer
| sammcj
sammcj
| p12tic
p12tic
| gtaylor
gtaylor
| aitoroses
aitoroses
| yt3trees
yt3trees
| +| franekp
franekp
| yongjer
yongjer
| vincentsong
vincentsong
| vagadiya
vagadiya
| teddyOOXX
teddyOOXX
| eonghk
eonghk
| +| taisukeoe
taisukeoe
| heyseth
heyseth
| ross
ross
| axkirillov
axkirillov
| anton-otee
anton-otee
| benzntech
benzntech
| +| bramburn
bramburn
| GitlyHallows
GitlyHallows
| philfung
philfung
| napter
napter
| mdp
mdp
| jcbdev
jcbdev
| +| Chenjiayuan195
Chenjiayuan195
| SplittyDev
SplittyDev
| amittell
amittell
| ashktn
ashktn
| axmo
axmo
| bannzai
bannzai
| +| dairui1
dairui1
| dqroid
dqroid
| hongzio
hongzio
| im47cn
im47cn
| shoopapa
shoopapa
| jwcraig
jwcraig
| +| kinandan
kinandan
| kohii
kohii
| lightrabbit
lightrabbit
| olup
olup
| mecab
mecab
| moqimoqidea
moqimoqidea
| +| mosleyit
mosleyit
| nevermorec
nevermorec
| nobu007
nobu007
| oprstchn
oprstchn
| philipnext
philipnext
| pokutuna
pokutuna
| +| refactorthis
refactorthis
| ronyblum
ronyblum
| samir-nimbly
samir-nimbly
| seedlord
seedlord
| shaybc
shaybc
| shohei-ihaya
shohei-ihaya
| +| student20880
student20880
| cdlliuy
cdlliuy
| PretzelVector
PretzelVector
| AMHesch
AMHesch
| adamwlarson
adamwlarson
| alarno
alarno
| +| QuinsZouls
QuinsZouls
| andreastempsch
andreastempsch
| atlasgong
atlasgong
| Atlogit
Atlogit
| bogdan0083
bogdan0083
| chadgauth
chadgauth
| +| dleen
dleen
| dbasclpy
dbasclpy
| snoyiatk
snoyiatk
| linegel
linegel
| celestial-vault
celestial-vault
| DeXtroTip
DeXtroTip
| +| hesara
hesara
| eltociear
eltociear
| Jdo300
Jdo300
| shtse8
shtse8
| libertyteeth
libertyteeth
| mamertofabian
mamertofabian
| +| marvijo-code
marvijo-code
| kvokka
kvokka
| Sarke
Sarke
| 01Rian
01Rian
| samsilveira
samsilveira
| maekawataiki
maekawataiki
| +| tgfjt
tgfjt
| tmsjngx0
tmsjngx0
| NamesMT
NamesMT
| vladstudio
vladstudio
| Yoshino-Yukitaro
Yoshino-Yukitaro
| | diff --git a/locales/ca/README.md b/locales/ca/README.md index 89b7b23fe11..a04ea161c28 100644 --- a/locales/ca/README.md +++ b/locales/ca/README.md @@ -184,24 +184,24 @@ Gràcies a tots els nostres col·laboradors que han ajudat a millorar Roo Code! |jquanton
jquanton
|KJ7LNW
KJ7LNW
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| |monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|feifei325
feifei325
|wkordalski
wkordalski
|cannuri
cannuri
|lloydchang
lloydchang
| |vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|lupuletic
lupuletic
|qdaxb
qdaxb
|Premshay
Premshay
|psv2522
psv2522
| -|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|sachasayan
sachasayan
| -|aheizi
aheizi
|afshawnlotfi
afshawnlotfi
|pugazhendhi-m
pugazhendhi-m
|pdecat
pdecat
|kyle-apex
kyle-apex
|emshvac
emshvac
| -|dtrugman
dtrugman
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|elianiva
elianiva
|aitoroses
aitoroses
|yt3trees
yt3trees
| +|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
|aheizi
aheizi
|sachasayan
sachasayan
|RaySinner
RaySinner
| +|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|elianiva
elianiva
|dtrugman
dtrugman
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|pdecat
pdecat
|pugazhendhi-m
pugazhendhi-m
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
| +|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|yt3trees
yt3trees
| |franekp
franekp
|yongjer
yongjer
|vincentsong
vincentsong
|vagadiya
vagadiya
|teddyOOXX
teddyOOXX
|eonghk
eonghk
| |taisukeoe
taisukeoe
|heyseth
heyseth
|ross
ross
|axkirillov
axkirillov
|anton-otee
anton-otee
|benzntech
benzntech
| |bramburn
bramburn
|GitlyHallows
GitlyHallows
|philfung
philfung
|napter
napter
|mdp
mdp
|jcbdev
jcbdev
| |Chenjiayuan195
Chenjiayuan195
|SplittyDev
SplittyDev
|amittell
amittell
|ashktn
ashktn
|axmo
axmo
|bannzai
bannzai
| -|dairui1
dairui1
|dqroid
dqroid
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
| -|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
|mosleyit
mosleyit
| -|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
|refactorthis
refactorthis
| -|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
|student20880
student20880
|cdlliuy
cdlliuy
| -|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
|andreastempsch
andreastempsch
|atlasgong
atlasgong
| -|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
| -|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
| -|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
| -|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|vladstudio
vladstudio
| -|Yoshino-Yukitaro
Yoshino-Yukitaro
| | | | | | +|dairui1
dairui1
|dqroid
dqroid
|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
| +|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
| +|mosleyit
mosleyit
|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
| +|refactorthis
refactorthis
|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|seedlord
seedlord
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
| +|student20880
student20880
|cdlliuy
cdlliuy
|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
| +|QuinsZouls
QuinsZouls
|andreastempsch
andreastempsch
|atlasgong
atlasgong
|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
| +|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
| +|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
| +|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
| +|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|NamesMT
NamesMT
|vladstudio
vladstudio
|Yoshino-Yukitaro
Yoshino-Yukitaro
| | ## Llicència diff --git a/locales/de/README.md b/locales/de/README.md index 6f29b9a1d9f..d3a55b6b0cf 100644 --- a/locales/de/README.md +++ b/locales/de/README.md @@ -184,24 +184,24 @@ Danke an alle unsere Mitwirkenden, die geholfen haben, Roo Code zu verbessern! |jquanton
jquanton
|KJ7LNW
KJ7LNW
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| |monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|feifei325
feifei325
|wkordalski
wkordalski
|cannuri
cannuri
|lloydchang
lloydchang
| |vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|lupuletic
lupuletic
|qdaxb
qdaxb
|Premshay
Premshay
|psv2522
psv2522
| -|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|sachasayan
sachasayan
| -|aheizi
aheizi
|afshawnlotfi
afshawnlotfi
|pugazhendhi-m
pugazhendhi-m
|pdecat
pdecat
|kyle-apex
kyle-apex
|emshvac
emshvac
| -|dtrugman
dtrugman
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|elianiva
elianiva
|aitoroses
aitoroses
|yt3trees
yt3trees
| +|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
|aheizi
aheizi
|sachasayan
sachasayan
|RaySinner
RaySinner
| +|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|elianiva
elianiva
|dtrugman
dtrugman
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|pdecat
pdecat
|pugazhendhi-m
pugazhendhi-m
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
| +|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|yt3trees
yt3trees
| |franekp
franekp
|yongjer
yongjer
|vincentsong
vincentsong
|vagadiya
vagadiya
|teddyOOXX
teddyOOXX
|eonghk
eonghk
| |taisukeoe
taisukeoe
|heyseth
heyseth
|ross
ross
|axkirillov
axkirillov
|anton-otee
anton-otee
|benzntech
benzntech
| |bramburn
bramburn
|GitlyHallows
GitlyHallows
|philfung
philfung
|napter
napter
|mdp
mdp
|jcbdev
jcbdev
| |Chenjiayuan195
Chenjiayuan195
|SplittyDev
SplittyDev
|amittell
amittell
|ashktn
ashktn
|axmo
axmo
|bannzai
bannzai
| -|dairui1
dairui1
|dqroid
dqroid
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
| -|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
|mosleyit
mosleyit
| -|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
|refactorthis
refactorthis
| -|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
|student20880
student20880
|cdlliuy
cdlliuy
| -|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
|andreastempsch
andreastempsch
|atlasgong
atlasgong
| -|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
| -|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
| -|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
| -|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|vladstudio
vladstudio
| -|Yoshino-Yukitaro
Yoshino-Yukitaro
| | | | | | +|dairui1
dairui1
|dqroid
dqroid
|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
| +|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
| +|mosleyit
mosleyit
|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
| +|refactorthis
refactorthis
|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|seedlord
seedlord
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
| +|student20880
student20880
|cdlliuy
cdlliuy
|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
| +|QuinsZouls
QuinsZouls
|andreastempsch
andreastempsch
|atlasgong
atlasgong
|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
| +|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
| +|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
| +|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
| +|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|NamesMT
NamesMT
|vladstudio
vladstudio
|Yoshino-Yukitaro
Yoshino-Yukitaro
| | ## Lizenz diff --git a/locales/es/README.md b/locales/es/README.md index eb23013fac2..68470e32bfd 100644 --- a/locales/es/README.md +++ b/locales/es/README.md @@ -184,24 +184,24 @@ Usamos [changesets](https://github.com/changesets/changesets) para versionar y p |jquanton
jquanton
|KJ7LNW
KJ7LNW
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| |monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|feifei325
feifei325
|wkordalski
wkordalski
|cannuri
cannuri
|lloydchang
lloydchang
| |vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|lupuletic
lupuletic
|qdaxb
qdaxb
|Premshay
Premshay
|psv2522
psv2522
| -|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|sachasayan
sachasayan
| -|aheizi
aheizi
|afshawnlotfi
afshawnlotfi
|pugazhendhi-m
pugazhendhi-m
|pdecat
pdecat
|kyle-apex
kyle-apex
|emshvac
emshvac
| -|dtrugman
dtrugman
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|elianiva
elianiva
|aitoroses
aitoroses
|yt3trees
yt3trees
| +|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
|aheizi
aheizi
|sachasayan
sachasayan
|RaySinner
RaySinner
| +|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|elianiva
elianiva
|dtrugman
dtrugman
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|pdecat
pdecat
|pugazhendhi-m
pugazhendhi-m
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
| +|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|yt3trees
yt3trees
| |franekp
franekp
|yongjer
yongjer
|vincentsong
vincentsong
|vagadiya
vagadiya
|teddyOOXX
teddyOOXX
|eonghk
eonghk
| |taisukeoe
taisukeoe
|heyseth
heyseth
|ross
ross
|axkirillov
axkirillov
|anton-otee
anton-otee
|benzntech
benzntech
| |bramburn
bramburn
|GitlyHallows
GitlyHallows
|philfung
philfung
|napter
napter
|mdp
mdp
|jcbdev
jcbdev
| |Chenjiayuan195
Chenjiayuan195
|SplittyDev
SplittyDev
|amittell
amittell
|ashktn
ashktn
|axmo
axmo
|bannzai
bannzai
| -|dairui1
dairui1
|dqroid
dqroid
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
| -|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
|mosleyit
mosleyit
| -|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
|refactorthis
refactorthis
| -|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
|student20880
student20880
|cdlliuy
cdlliuy
| -|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
|andreastempsch
andreastempsch
|atlasgong
atlasgong
| -|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
| -|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
| -|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
| -|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|vladstudio
vladstudio
| -|Yoshino-Yukitaro
Yoshino-Yukitaro
| | | | | | +|dairui1
dairui1
|dqroid
dqroid
|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
| +|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
| +|mosleyit
mosleyit
|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
| +|refactorthis
refactorthis
|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|seedlord
seedlord
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
| +|student20880
student20880
|cdlliuy
cdlliuy
|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
| +|QuinsZouls
QuinsZouls
|andreastempsch
andreastempsch
|atlasgong
atlasgong
|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
| +|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
| +|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
| +|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
| +|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|NamesMT
NamesMT
|vladstudio
vladstudio
|Yoshino-Yukitaro
Yoshino-Yukitaro
| | ## Licencia diff --git a/locales/fr/README.md b/locales/fr/README.md index 563b45126af..617f84f535b 100644 --- a/locales/fr/README.md +++ b/locales/fr/README.md @@ -184,24 +184,24 @@ Merci à tous nos contributeurs qui ont aidé à améliorer Roo Code ! |jquanton
jquanton
|KJ7LNW
KJ7LNW
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| |monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|feifei325
feifei325
|wkordalski
wkordalski
|cannuri
cannuri
|lloydchang
lloydchang
| |vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|lupuletic
lupuletic
|qdaxb
qdaxb
|Premshay
Premshay
|psv2522
psv2522
| -|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|sachasayan
sachasayan
| -|aheizi
aheizi
|afshawnlotfi
afshawnlotfi
|pugazhendhi-m
pugazhendhi-m
|pdecat
pdecat
|kyle-apex
kyle-apex
|emshvac
emshvac
| -|dtrugman
dtrugman
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|elianiva
elianiva
|aitoroses
aitoroses
|yt3trees
yt3trees
| +|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
|aheizi
aheizi
|sachasayan
sachasayan
|RaySinner
RaySinner
| +|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|elianiva
elianiva
|dtrugman
dtrugman
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|pdecat
pdecat
|pugazhendhi-m
pugazhendhi-m
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
| +|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|yt3trees
yt3trees
| |franekp
franekp
|yongjer
yongjer
|vincentsong
vincentsong
|vagadiya
vagadiya
|teddyOOXX
teddyOOXX
|eonghk
eonghk
| |taisukeoe
taisukeoe
|heyseth
heyseth
|ross
ross
|axkirillov
axkirillov
|anton-otee
anton-otee
|benzntech
benzntech
| |bramburn
bramburn
|GitlyHallows
GitlyHallows
|philfung
philfung
|napter
napter
|mdp
mdp
|jcbdev
jcbdev
| |Chenjiayuan195
Chenjiayuan195
|SplittyDev
SplittyDev
|amittell
amittell
|ashktn
ashktn
|axmo
axmo
|bannzai
bannzai
| -|dairui1
dairui1
|dqroid
dqroid
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
| -|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
|mosleyit
mosleyit
| -|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
|refactorthis
refactorthis
| -|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
|student20880
student20880
|cdlliuy
cdlliuy
| -|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
|andreastempsch
andreastempsch
|atlasgong
atlasgong
| -|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
| -|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
| -|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
| -|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|vladstudio
vladstudio
| -|Yoshino-Yukitaro
Yoshino-Yukitaro
| | | | | | +|dairui1
dairui1
|dqroid
dqroid
|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
| +|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
| +|mosleyit
mosleyit
|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
| +|refactorthis
refactorthis
|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|seedlord
seedlord
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
| +|student20880
student20880
|cdlliuy
cdlliuy
|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
| +|QuinsZouls
QuinsZouls
|andreastempsch
andreastempsch
|atlasgong
atlasgong
|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
| +|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
| +|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
| +|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
| +|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|NamesMT
NamesMT
|vladstudio
vladstudio
|Yoshino-Yukitaro
Yoshino-Yukitaro
| | ## Licence diff --git a/locales/hi/README.md b/locales/hi/README.md index 25c9ddbb1c6..50ca4caa065 100644 --- a/locales/hi/README.md +++ b/locales/hi/README.md @@ -184,24 +184,24 @@ Roo Code को बेहतर बनाने में मदद करने |jquanton
jquanton
|KJ7LNW
KJ7LNW
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| |monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|feifei325
feifei325
|wkordalski
wkordalski
|cannuri
cannuri
|lloydchang
lloydchang
| |vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|lupuletic
lupuletic
|qdaxb
qdaxb
|Premshay
Premshay
|psv2522
psv2522
| -|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|sachasayan
sachasayan
| -|aheizi
aheizi
|afshawnlotfi
afshawnlotfi
|pugazhendhi-m
pugazhendhi-m
|pdecat
pdecat
|kyle-apex
kyle-apex
|emshvac
emshvac
| -|dtrugman
dtrugman
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|elianiva
elianiva
|aitoroses
aitoroses
|yt3trees
yt3trees
| +|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
|aheizi
aheizi
|sachasayan
sachasayan
|RaySinner
RaySinner
| +|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|elianiva
elianiva
|dtrugman
dtrugman
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|pdecat
pdecat
|pugazhendhi-m
pugazhendhi-m
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
| +|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|yt3trees
yt3trees
| |franekp
franekp
|yongjer
yongjer
|vincentsong
vincentsong
|vagadiya
vagadiya
|teddyOOXX
teddyOOXX
|eonghk
eonghk
| |taisukeoe
taisukeoe
|heyseth
heyseth
|ross
ross
|axkirillov
axkirillov
|anton-otee
anton-otee
|benzntech
benzntech
| |bramburn
bramburn
|GitlyHallows
GitlyHallows
|philfung
philfung
|napter
napter
|mdp
mdp
|jcbdev
jcbdev
| |Chenjiayuan195
Chenjiayuan195
|SplittyDev
SplittyDev
|amittell
amittell
|ashktn
ashktn
|axmo
axmo
|bannzai
bannzai
| -|dairui1
dairui1
|dqroid
dqroid
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
| -|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
|mosleyit
mosleyit
| -|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
|refactorthis
refactorthis
| -|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
|student20880
student20880
|cdlliuy
cdlliuy
| -|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
|andreastempsch
andreastempsch
|atlasgong
atlasgong
| -|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
| -|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
| -|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
| -|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|vladstudio
vladstudio
| -|Yoshino-Yukitaro
Yoshino-Yukitaro
| | | | | | +|dairui1
dairui1
|dqroid
dqroid
|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
| +|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
| +|mosleyit
mosleyit
|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
| +|refactorthis
refactorthis
|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|seedlord
seedlord
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
| +|student20880
student20880
|cdlliuy
cdlliuy
|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
| +|QuinsZouls
QuinsZouls
|andreastempsch
andreastempsch
|atlasgong
atlasgong
|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
| +|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
| +|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
| +|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
| +|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|NamesMT
NamesMT
|vladstudio
vladstudio
|Yoshino-Yukitaro
Yoshino-Yukitaro
| | ## लाइसेंस diff --git a/locales/it/README.md b/locales/it/README.md index ee75eabcc38..5b802fc8880 100644 --- a/locales/it/README.md +++ b/locales/it/README.md @@ -184,24 +184,24 @@ Grazie a tutti i nostri contributori che hanno aiutato a migliorare Roo Code! |jquanton
jquanton
|KJ7LNW
KJ7LNW
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| |monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|feifei325
feifei325
|wkordalski
wkordalski
|cannuri
cannuri
|lloydchang
lloydchang
| |vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|lupuletic
lupuletic
|qdaxb
qdaxb
|Premshay
Premshay
|psv2522
psv2522
| -|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|sachasayan
sachasayan
| -|aheizi
aheizi
|afshawnlotfi
afshawnlotfi
|pugazhendhi-m
pugazhendhi-m
|pdecat
pdecat
|kyle-apex
kyle-apex
|emshvac
emshvac
| -|dtrugman
dtrugman
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|elianiva
elianiva
|aitoroses
aitoroses
|yt3trees
yt3trees
| +|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
|aheizi
aheizi
|sachasayan
sachasayan
|RaySinner
RaySinner
| +|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|elianiva
elianiva
|dtrugman
dtrugman
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|pdecat
pdecat
|pugazhendhi-m
pugazhendhi-m
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
| +|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|yt3trees
yt3trees
| |franekp
franekp
|yongjer
yongjer
|vincentsong
vincentsong
|vagadiya
vagadiya
|teddyOOXX
teddyOOXX
|eonghk
eonghk
| |taisukeoe
taisukeoe
|heyseth
heyseth
|ross
ross
|axkirillov
axkirillov
|anton-otee
anton-otee
|benzntech
benzntech
| |bramburn
bramburn
|GitlyHallows
GitlyHallows
|philfung
philfung
|napter
napter
|mdp
mdp
|jcbdev
jcbdev
| |Chenjiayuan195
Chenjiayuan195
|SplittyDev
SplittyDev
|amittell
amittell
|ashktn
ashktn
|axmo
axmo
|bannzai
bannzai
| -|dairui1
dairui1
|dqroid
dqroid
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
| -|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
|mosleyit
mosleyit
| -|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
|refactorthis
refactorthis
| -|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
|student20880
student20880
|cdlliuy
cdlliuy
| -|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
|andreastempsch
andreastempsch
|atlasgong
atlasgong
| -|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
| -|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
| -|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
| -|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|vladstudio
vladstudio
| -|Yoshino-Yukitaro
Yoshino-Yukitaro
| | | | | | +|dairui1
dairui1
|dqroid
dqroid
|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
| +|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
| +|mosleyit
mosleyit
|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
| +|refactorthis
refactorthis
|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|seedlord
seedlord
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
| +|student20880
student20880
|cdlliuy
cdlliuy
|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
| +|QuinsZouls
QuinsZouls
|andreastempsch
andreastempsch
|atlasgong
atlasgong
|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
| +|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
| +|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
| +|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
| +|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|NamesMT
NamesMT
|vladstudio
vladstudio
|Yoshino-Yukitaro
Yoshino-Yukitaro
| | ## Licenza diff --git a/locales/ja/README.md b/locales/ja/README.md index 67eb93e6cf1..1b4ea0e8c09 100644 --- a/locales/ja/README.md +++ b/locales/ja/README.md @@ -184,24 +184,24 @@ Roo Codeの改善に貢献してくれたすべての貢献者に感謝します |jquanton
jquanton
|KJ7LNW
KJ7LNW
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| |monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|feifei325
feifei325
|wkordalski
wkordalski
|cannuri
cannuri
|lloydchang
lloydchang
| |vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|lupuletic
lupuletic
|qdaxb
qdaxb
|Premshay
Premshay
|psv2522
psv2522
| -|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|sachasayan
sachasayan
| -|aheizi
aheizi
|afshawnlotfi
afshawnlotfi
|pugazhendhi-m
pugazhendhi-m
|pdecat
pdecat
|kyle-apex
kyle-apex
|emshvac
emshvac
| -|dtrugman
dtrugman
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|elianiva
elianiva
|aitoroses
aitoroses
|yt3trees
yt3trees
| +|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
|aheizi
aheizi
|sachasayan
sachasayan
|RaySinner
RaySinner
| +|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|elianiva
elianiva
|dtrugman
dtrugman
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|pdecat
pdecat
|pugazhendhi-m
pugazhendhi-m
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
| +|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|yt3trees
yt3trees
| |franekp
franekp
|yongjer
yongjer
|vincentsong
vincentsong
|vagadiya
vagadiya
|teddyOOXX
teddyOOXX
|eonghk
eonghk
| |taisukeoe
taisukeoe
|heyseth
heyseth
|ross
ross
|axkirillov
axkirillov
|anton-otee
anton-otee
|benzntech
benzntech
| |bramburn
bramburn
|GitlyHallows
GitlyHallows
|philfung
philfung
|napter
napter
|mdp
mdp
|jcbdev
jcbdev
| |Chenjiayuan195
Chenjiayuan195
|SplittyDev
SplittyDev
|amittell
amittell
|ashktn
ashktn
|axmo
axmo
|bannzai
bannzai
| -|dairui1
dairui1
|dqroid
dqroid
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
| -|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
|mosleyit
mosleyit
| -|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
|refactorthis
refactorthis
| -|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
|student20880
student20880
|cdlliuy
cdlliuy
| -|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
|andreastempsch
andreastempsch
|atlasgong
atlasgong
| -|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
| -|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
| -|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
| -|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|vladstudio
vladstudio
| -|Yoshino-Yukitaro
Yoshino-Yukitaro
| | | | | | +|dairui1
dairui1
|dqroid
dqroid
|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
| +|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
| +|mosleyit
mosleyit
|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
| +|refactorthis
refactorthis
|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|seedlord
seedlord
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
| +|student20880
student20880
|cdlliuy
cdlliuy
|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
| +|QuinsZouls
QuinsZouls
|andreastempsch
andreastempsch
|atlasgong
atlasgong
|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
| +|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
| +|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
| +|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
| +|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|NamesMT
NamesMT
|vladstudio
vladstudio
|Yoshino-Yukitaro
Yoshino-Yukitaro
| | ## ライセンス diff --git a/locales/ko/README.md b/locales/ko/README.md index 4a62a139bfe..5bdf5efd377 100644 --- a/locales/ko/README.md +++ b/locales/ko/README.md @@ -184,24 +184,24 @@ Roo Code를 더 좋게 만드는 데 도움을 준 모든 기여자에게 감사 |jquanton
jquanton
|KJ7LNW
KJ7LNW
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| |monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|feifei325
feifei325
|wkordalski
wkordalski
|cannuri
cannuri
|lloydchang
lloydchang
| |vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|lupuletic
lupuletic
|qdaxb
qdaxb
|Premshay
Premshay
|psv2522
psv2522
| -|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|sachasayan
sachasayan
| -|aheizi
aheizi
|afshawnlotfi
afshawnlotfi
|pugazhendhi-m
pugazhendhi-m
|pdecat
pdecat
|kyle-apex
kyle-apex
|emshvac
emshvac
| -|dtrugman
dtrugman
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|elianiva
elianiva
|aitoroses
aitoroses
|yt3trees
yt3trees
| +|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
|aheizi
aheizi
|sachasayan
sachasayan
|RaySinner
RaySinner
| +|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|elianiva
elianiva
|dtrugman
dtrugman
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|pdecat
pdecat
|pugazhendhi-m
pugazhendhi-m
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
| +|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|yt3trees
yt3trees
| |franekp
franekp
|yongjer
yongjer
|vincentsong
vincentsong
|vagadiya
vagadiya
|teddyOOXX
teddyOOXX
|eonghk
eonghk
| |taisukeoe
taisukeoe
|heyseth
heyseth
|ross
ross
|axkirillov
axkirillov
|anton-otee
anton-otee
|benzntech
benzntech
| |bramburn
bramburn
|GitlyHallows
GitlyHallows
|philfung
philfung
|napter
napter
|mdp
mdp
|jcbdev
jcbdev
| |Chenjiayuan195
Chenjiayuan195
|SplittyDev
SplittyDev
|amittell
amittell
|ashktn
ashktn
|axmo
axmo
|bannzai
bannzai
| -|dairui1
dairui1
|dqroid
dqroid
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
| -|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
|mosleyit
mosleyit
| -|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
|refactorthis
refactorthis
| -|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
|student20880
student20880
|cdlliuy
cdlliuy
| -|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
|andreastempsch
andreastempsch
|atlasgong
atlasgong
| -|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
| -|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
| -|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
| -|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|vladstudio
vladstudio
| -|Yoshino-Yukitaro
Yoshino-Yukitaro
| | | | | | +|dairui1
dairui1
|dqroid
dqroid
|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
| +|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
| +|mosleyit
mosleyit
|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
| +|refactorthis
refactorthis
|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|seedlord
seedlord
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
| +|student20880
student20880
|cdlliuy
cdlliuy
|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
| +|QuinsZouls
QuinsZouls
|andreastempsch
andreastempsch
|atlasgong
atlasgong
|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
| +|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
| +|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
| +|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
| +|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|NamesMT
NamesMT
|vladstudio
vladstudio
|Yoshino-Yukitaro
Yoshino-Yukitaro
| | ## 라이선스 diff --git a/locales/pl/README.md b/locales/pl/README.md index a45d971bfc8..d3235ab1b0e 100644 --- a/locales/pl/README.md +++ b/locales/pl/README.md @@ -184,24 +184,24 @@ Dziękujemy wszystkim naszym współtwórcom, którzy pomogli ulepszyć Roo Code |jquanton
jquanton
|KJ7LNW
KJ7LNW
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| |monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|feifei325
feifei325
|wkordalski
wkordalski
|cannuri
cannuri
|lloydchang
lloydchang
| |vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|lupuletic
lupuletic
|qdaxb
qdaxb
|Premshay
Premshay
|psv2522
psv2522
| -|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|sachasayan
sachasayan
| -|aheizi
aheizi
|afshawnlotfi
afshawnlotfi
|pugazhendhi-m
pugazhendhi-m
|pdecat
pdecat
|kyle-apex
kyle-apex
|emshvac
emshvac
| -|dtrugman
dtrugman
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|elianiva
elianiva
|aitoroses
aitoroses
|yt3trees
yt3trees
| +|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
|aheizi
aheizi
|sachasayan
sachasayan
|RaySinner
RaySinner
| +|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|elianiva
elianiva
|dtrugman
dtrugman
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|pdecat
pdecat
|pugazhendhi-m
pugazhendhi-m
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
| +|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|yt3trees
yt3trees
| |franekp
franekp
|yongjer
yongjer
|vincentsong
vincentsong
|vagadiya
vagadiya
|teddyOOXX
teddyOOXX
|eonghk
eonghk
| |taisukeoe
taisukeoe
|heyseth
heyseth
|ross
ross
|axkirillov
axkirillov
|anton-otee
anton-otee
|benzntech
benzntech
| |bramburn
bramburn
|GitlyHallows
GitlyHallows
|philfung
philfung
|napter
napter
|mdp
mdp
|jcbdev
jcbdev
| |Chenjiayuan195
Chenjiayuan195
|SplittyDev
SplittyDev
|amittell
amittell
|ashktn
ashktn
|axmo
axmo
|bannzai
bannzai
| -|dairui1
dairui1
|dqroid
dqroid
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
| -|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
|mosleyit
mosleyit
| -|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
|refactorthis
refactorthis
| -|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
|student20880
student20880
|cdlliuy
cdlliuy
| -|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
|andreastempsch
andreastempsch
|atlasgong
atlasgong
| -|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
| -|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
| -|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
| -|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|vladstudio
vladstudio
| -|Yoshino-Yukitaro
Yoshino-Yukitaro
| | | | | | +|dairui1
dairui1
|dqroid
dqroid
|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
| +|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
| +|mosleyit
mosleyit
|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
| +|refactorthis
refactorthis
|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|seedlord
seedlord
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
| +|student20880
student20880
|cdlliuy
cdlliuy
|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
| +|QuinsZouls
QuinsZouls
|andreastempsch
andreastempsch
|atlasgong
atlasgong
|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
| +|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
| +|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
| +|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
| +|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|NamesMT
NamesMT
|vladstudio
vladstudio
|Yoshino-Yukitaro
Yoshino-Yukitaro
| | ## Licencja diff --git a/locales/pt-BR/README.md b/locales/pt-BR/README.md index 97ff1917788..7f2f14d8f2b 100644 --- a/locales/pt-BR/README.md +++ b/locales/pt-BR/README.md @@ -184,24 +184,24 @@ Obrigado a todos os nossos contribuidores que ajudaram a tornar o Roo Code melho |jquanton
jquanton
|KJ7LNW
KJ7LNW
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| |monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|feifei325
feifei325
|wkordalski
wkordalski
|cannuri
cannuri
|lloydchang
lloydchang
| |vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|lupuletic
lupuletic
|qdaxb
qdaxb
|Premshay
Premshay
|psv2522
psv2522
| -|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|sachasayan
sachasayan
| -|aheizi
aheizi
|afshawnlotfi
afshawnlotfi
|pugazhendhi-m
pugazhendhi-m
|pdecat
pdecat
|kyle-apex
kyle-apex
|emshvac
emshvac
| -|dtrugman
dtrugman
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|elianiva
elianiva
|aitoroses
aitoroses
|yt3trees
yt3trees
| +|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
|aheizi
aheizi
|sachasayan
sachasayan
|RaySinner
RaySinner
| +|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|elianiva
elianiva
|dtrugman
dtrugman
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|pdecat
pdecat
|pugazhendhi-m
pugazhendhi-m
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
| +|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|yt3trees
yt3trees
| |franekp
franekp
|yongjer
yongjer
|vincentsong
vincentsong
|vagadiya
vagadiya
|teddyOOXX
teddyOOXX
|eonghk
eonghk
| |taisukeoe
taisukeoe
|heyseth
heyseth
|ross
ross
|axkirillov
axkirillov
|anton-otee
anton-otee
|benzntech
benzntech
| |bramburn
bramburn
|GitlyHallows
GitlyHallows
|philfung
philfung
|napter
napter
|mdp
mdp
|jcbdev
jcbdev
| |Chenjiayuan195
Chenjiayuan195
|SplittyDev
SplittyDev
|amittell
amittell
|ashktn
ashktn
|axmo
axmo
|bannzai
bannzai
| -|dairui1
dairui1
|dqroid
dqroid
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
| -|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
|mosleyit
mosleyit
| -|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
|refactorthis
refactorthis
| -|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
|student20880
student20880
|cdlliuy
cdlliuy
| -|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
|andreastempsch
andreastempsch
|atlasgong
atlasgong
| -|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
| -|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
| -|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
| -|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|vladstudio
vladstudio
| -|Yoshino-Yukitaro
Yoshino-Yukitaro
| | | | | | +|dairui1
dairui1
|dqroid
dqroid
|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
| +|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
| +|mosleyit
mosleyit
|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
| +|refactorthis
refactorthis
|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|seedlord
seedlord
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
| +|student20880
student20880
|cdlliuy
cdlliuy
|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
| +|QuinsZouls
QuinsZouls
|andreastempsch
andreastempsch
|atlasgong
atlasgong
|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
| +|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
| +|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
| +|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
| +|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|NamesMT
NamesMT
|vladstudio
vladstudio
|Yoshino-Yukitaro
Yoshino-Yukitaro
| | ## Licença diff --git a/locales/tr/README.md b/locales/tr/README.md index ef52cb63055..58e0e5a5ebf 100644 --- a/locales/tr/README.md +++ b/locales/tr/README.md @@ -184,24 +184,24 @@ Roo Code'u daha iyi hale getirmeye yardımcı olan tüm katkıda bulunanlara te |jquanton
jquanton
|KJ7LNW
KJ7LNW
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| |monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|feifei325
feifei325
|wkordalski
wkordalski
|cannuri
cannuri
|lloydchang
lloydchang
| |vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|lupuletic
lupuletic
|qdaxb
qdaxb
|Premshay
Premshay
|psv2522
psv2522
| -|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|sachasayan
sachasayan
| -|aheizi
aheizi
|afshawnlotfi
afshawnlotfi
|pugazhendhi-m
pugazhendhi-m
|pdecat
pdecat
|kyle-apex
kyle-apex
|emshvac
emshvac
| -|dtrugman
dtrugman
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|elianiva
elianiva
|aitoroses
aitoroses
|yt3trees
yt3trees
| +|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
|aheizi
aheizi
|sachasayan
sachasayan
|RaySinner
RaySinner
| +|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|elianiva
elianiva
|dtrugman
dtrugman
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|pdecat
pdecat
|pugazhendhi-m
pugazhendhi-m
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
| +|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|yt3trees
yt3trees
| |franekp
franekp
|yongjer
yongjer
|vincentsong
vincentsong
|vagadiya
vagadiya
|teddyOOXX
teddyOOXX
|eonghk
eonghk
| |taisukeoe
taisukeoe
|heyseth
heyseth
|ross
ross
|axkirillov
axkirillov
|anton-otee
anton-otee
|benzntech
benzntech
| |bramburn
bramburn
|GitlyHallows
GitlyHallows
|philfung
philfung
|napter
napter
|mdp
mdp
|jcbdev
jcbdev
| |Chenjiayuan195
Chenjiayuan195
|SplittyDev
SplittyDev
|amittell
amittell
|ashktn
ashktn
|axmo
axmo
|bannzai
bannzai
| -|dairui1
dairui1
|dqroid
dqroid
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
| -|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
|mosleyit
mosleyit
| -|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
|refactorthis
refactorthis
| -|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
|student20880
student20880
|cdlliuy
cdlliuy
| -|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
|andreastempsch
andreastempsch
|atlasgong
atlasgong
| -|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
| -|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
| -|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
| -|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|vladstudio
vladstudio
| -|Yoshino-Yukitaro
Yoshino-Yukitaro
| | | | | | +|dairui1
dairui1
|dqroid
dqroid
|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
| +|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
| +|mosleyit
mosleyit
|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
| +|refactorthis
refactorthis
|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|seedlord
seedlord
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
| +|student20880
student20880
|cdlliuy
cdlliuy
|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
| +|QuinsZouls
QuinsZouls
|andreastempsch
andreastempsch
|atlasgong
atlasgong
|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
| +|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
| +|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
| +|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
| +|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|NamesMT
NamesMT
|vladstudio
vladstudio
|Yoshino-Yukitaro
Yoshino-Yukitaro
| | ## Lisans diff --git a/locales/vi/README.md b/locales/vi/README.md index b8d3305741e..52d377b2d27 100644 --- a/locales/vi/README.md +++ b/locales/vi/README.md @@ -184,24 +184,24 @@ Cảm ơn tất cả những người đóng góp đã giúp cải thiện Roo C |jquanton
jquanton
|KJ7LNW
KJ7LNW
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| |monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|feifei325
feifei325
|wkordalski
wkordalski
|cannuri
cannuri
|lloydchang
lloydchang
| |vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|lupuletic
lupuletic
|qdaxb
qdaxb
|Premshay
Premshay
|psv2522
psv2522
| -|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|sachasayan
sachasayan
| -|aheizi
aheizi
|afshawnlotfi
afshawnlotfi
|pugazhendhi-m
pugazhendhi-m
|pdecat
pdecat
|kyle-apex
kyle-apex
|emshvac
emshvac
| -|dtrugman
dtrugman
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|elianiva
elianiva
|aitoroses
aitoroses
|yt3trees
yt3trees
| +|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
|aheizi
aheizi
|sachasayan
sachasayan
|RaySinner
RaySinner
| +|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|elianiva
elianiva
|dtrugman
dtrugman
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|pdecat
pdecat
|pugazhendhi-m
pugazhendhi-m
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
| +|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|yt3trees
yt3trees
| |franekp
franekp
|yongjer
yongjer
|vincentsong
vincentsong
|vagadiya
vagadiya
|teddyOOXX
teddyOOXX
|eonghk
eonghk
| |taisukeoe
taisukeoe
|heyseth
heyseth
|ross
ross
|axkirillov
axkirillov
|anton-otee
anton-otee
|benzntech
benzntech
| |bramburn
bramburn
|GitlyHallows
GitlyHallows
|philfung
philfung
|napter
napter
|mdp
mdp
|jcbdev
jcbdev
| |Chenjiayuan195
Chenjiayuan195
|SplittyDev
SplittyDev
|amittell
amittell
|ashktn
ashktn
|axmo
axmo
|bannzai
bannzai
| -|dairui1
dairui1
|dqroid
dqroid
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
| -|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
|mosleyit
mosleyit
| -|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
|refactorthis
refactorthis
| -|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
|student20880
student20880
|cdlliuy
cdlliuy
| -|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
|andreastempsch
andreastempsch
|atlasgong
atlasgong
| -|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
| -|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
| -|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
| -|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|vladstudio
vladstudio
| -|Yoshino-Yukitaro
Yoshino-Yukitaro
| | | | | | +|dairui1
dairui1
|dqroid
dqroid
|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
| +|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
| +|mosleyit
mosleyit
|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
| +|refactorthis
refactorthis
|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|seedlord
seedlord
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
| +|student20880
student20880
|cdlliuy
cdlliuy
|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
| +|QuinsZouls
QuinsZouls
|andreastempsch
andreastempsch
|atlasgong
atlasgong
|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
| +|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
| +|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
| +|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
| +|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|NamesMT
NamesMT
|vladstudio
vladstudio
|Yoshino-Yukitaro
Yoshino-Yukitaro
| | ## Giấy Phép diff --git a/locales/zh-CN/README.md b/locales/zh-CN/README.md index 7007478c2fe..835da775cd5 100644 --- a/locales/zh-CN/README.md +++ b/locales/zh-CN/README.md @@ -184,24 +184,24 @@ code --install-extension bin/roo-cline-.vsix |jquanton
jquanton
|KJ7LNW
KJ7LNW
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| |monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|feifei325
feifei325
|wkordalski
wkordalski
|cannuri
cannuri
|lloydchang
lloydchang
| |vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|lupuletic
lupuletic
|qdaxb
qdaxb
|Premshay
Premshay
|psv2522
psv2522
| -|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|sachasayan
sachasayan
| -|aheizi
aheizi
|afshawnlotfi
afshawnlotfi
|pugazhendhi-m
pugazhendhi-m
|pdecat
pdecat
|kyle-apex
kyle-apex
|emshvac
emshvac
| -|dtrugman
dtrugman
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|elianiva
elianiva
|aitoroses
aitoroses
|yt3trees
yt3trees
| +|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
|aheizi
aheizi
|sachasayan
sachasayan
|RaySinner
RaySinner
| +|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|elianiva
elianiva
|dtrugman
dtrugman
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|pdecat
pdecat
|pugazhendhi-m
pugazhendhi-m
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
| +|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|yt3trees
yt3trees
| |franekp
franekp
|yongjer
yongjer
|vincentsong
vincentsong
|vagadiya
vagadiya
|teddyOOXX
teddyOOXX
|eonghk
eonghk
| |taisukeoe
taisukeoe
|heyseth
heyseth
|ross
ross
|axkirillov
axkirillov
|anton-otee
anton-otee
|benzntech
benzntech
| |bramburn
bramburn
|GitlyHallows
GitlyHallows
|philfung
philfung
|napter
napter
|mdp
mdp
|jcbdev
jcbdev
| |Chenjiayuan195
Chenjiayuan195
|SplittyDev
SplittyDev
|amittell
amittell
|ashktn
ashktn
|axmo
axmo
|bannzai
bannzai
| -|dairui1
dairui1
|dqroid
dqroid
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
| -|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
|mosleyit
mosleyit
| -|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
|refactorthis
refactorthis
| -|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
|student20880
student20880
|cdlliuy
cdlliuy
| -|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
|andreastempsch
andreastempsch
|atlasgong
atlasgong
| -|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
| -|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
| -|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
| -|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|vladstudio
vladstudio
| -|Yoshino-Yukitaro
Yoshino-Yukitaro
| | | | | | +|dairui1
dairui1
|dqroid
dqroid
|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
| +|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
| +|mosleyit
mosleyit
|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
| +|refactorthis
refactorthis
|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|seedlord
seedlord
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
| +|student20880
student20880
|cdlliuy
cdlliuy
|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
| +|QuinsZouls
QuinsZouls
|andreastempsch
andreastempsch
|atlasgong
atlasgong
|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
| +|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
| +|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
| +|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
| +|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|NamesMT
NamesMT
|vladstudio
vladstudio
|Yoshino-Yukitaro
Yoshino-Yukitaro
| | ## 许可证 diff --git a/locales/zh-TW/README.md b/locales/zh-TW/README.md index fbd7f7b143f..a27cf1bbc9b 100644 --- a/locales/zh-TW/README.md +++ b/locales/zh-TW/README.md @@ -185,24 +185,24 @@ code --install-extension bin/roo-cline-.vsix |jquanton
jquanton
|KJ7LNW
KJ7LNW
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
|punkpeye
punkpeye
| |monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|feifei325
feifei325
|wkordalski
wkordalski
|cannuri
cannuri
|lloydchang
lloydchang
| |vigneshsubbiah16
vigneshsubbiah16
|Szpadel
Szpadel
|lupuletic
lupuletic
|qdaxb
qdaxb
|Premshay
Premshay
|psv2522
psv2522
| -|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|sachasayan
sachasayan
| -|aheizi
aheizi
|afshawnlotfi
afshawnlotfi
|pugazhendhi-m
pugazhendhi-m
|pdecat
pdecat
|kyle-apex
kyle-apex
|emshvac
emshvac
| -|dtrugman
dtrugman
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|elianiva
elianiva
|aitoroses
aitoroses
|yt3trees
yt3trees
| +|diarmidmackenzie
diarmidmackenzie
|olweraltuve
olweraltuve
|afshawnlotfi
afshawnlotfi
|aheizi
aheizi
|sachasayan
sachasayan
|RaySinner
RaySinner
| +|PeterDaveHello
PeterDaveHello
|nbihan-mediware
nbihan-mediware
|elianiva
elianiva
|dtrugman
dtrugman
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|pdecat
pdecat
|pugazhendhi-m
pugazhendhi-m
|Lunchb0ne
Lunchb0ne
|arthurauffray
arthurauffray
|zhangtony239
zhangtony239
|upamune
upamune
| +|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|gtaylor
gtaylor
|aitoroses
aitoroses
|yt3trees
yt3trees
| |franekp
franekp
|yongjer
yongjer
|vincentsong
vincentsong
|vagadiya
vagadiya
|teddyOOXX
teddyOOXX
|eonghk
eonghk
| |taisukeoe
taisukeoe
|heyseth
heyseth
|ross
ross
|axkirillov
axkirillov
|anton-otee
anton-otee
|benzntech
benzntech
| |bramburn
bramburn
|GitlyHallows
GitlyHallows
|philfung
philfung
|napter
napter
|mdp
mdp
|jcbdev
jcbdev
| |Chenjiayuan195
Chenjiayuan195
|SplittyDev
SplittyDev
|amittell
amittell
|ashktn
ashktn
|axmo
axmo
|bannzai
bannzai
| -|dairui1
dairui1
|dqroid
dqroid
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
| -|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
|mosleyit
mosleyit
| -|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
|refactorthis
refactorthis
| -|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
|student20880
student20880
|cdlliuy
cdlliuy
| -|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
|andreastempsch
andreastempsch
|atlasgong
atlasgong
| -|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
| -|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
| -|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
| -|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|vladstudio
vladstudio
| -|Yoshino-Yukitaro
Yoshino-Yukitaro
| | | | | | +|dairui1
dairui1
|dqroid
dqroid
|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
| +|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
|olup
olup
|mecab
mecab
|moqimoqidea
moqimoqidea
| +|mosleyit
mosleyit
|nevermorec
nevermorec
|nobu007
nobu007
|oprstchn
oprstchn
|philipnext
philipnext
|pokutuna
pokutuna
| +|refactorthis
refactorthis
|ronyblum
ronyblum
|samir-nimbly
samir-nimbly
|seedlord
seedlord
|shaybc
shaybc
|shohei-ihaya
shohei-ihaya
| +|student20880
student20880
|cdlliuy
cdlliuy
|PretzelVector
PretzelVector
|AMHesch
AMHesch
|adamwlarson
adamwlarson
|alarno
alarno
| +|QuinsZouls
QuinsZouls
|andreastempsch
andreastempsch
|atlasgong
atlasgong
|Atlogit
Atlogit
|bogdan0083
bogdan0083
|chadgauth
chadgauth
| +|dleen
dleen
|dbasclpy
dbasclpy
|snoyiatk
snoyiatk
|linegel
linegel
|celestial-vault
celestial-vault
|DeXtroTip
DeXtroTip
| +|hesara
hesara
|eltociear
eltociear
|Jdo300
Jdo300
|shtse8
shtse8
|libertyteeth
libertyteeth
|mamertofabian
mamertofabian
| +|marvijo-code
marvijo-code
|kvokka
kvokka
|Sarke
Sarke
|01Rian
01Rian
|samsilveira
samsilveira
|maekawataiki
maekawataiki
| +|tgfjt
tgfjt
|tmsjngx0
tmsjngx0
|NamesMT
NamesMT
|vladstudio
vladstudio
|Yoshino-Yukitaro
Yoshino-Yukitaro
| | ## 授權 From 1ddc426c826b451496abbfc91257bfd3dd9cf925 Mon Sep 17 00:00:00 2001 From: Wojciech Kordalski Date: Wed, 23 Apr 2025 10:42:06 +0200 Subject: [PATCH 10/20] FakeAI "controller" object must not be copied (#2463) The FakeAI object passed by the user must be exactly the same object that is passed to FakeAIHandler via API configuration. Unfortunatelly, as the VSCode global state is used as configuration storage, we lose this property (VSCode global state creates copies of the object). Also the class of the stored object is lost, so methods of the object are unavailable. Therefore, we store the original objects in global variable and use ID field of FakeAI object to identify the original object. --- src/api/providers/fake-ai.ts | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/api/providers/fake-ai.ts b/src/api/providers/fake-ai.ts index f7509c8b066..68d028338e7 100644 --- a/src/api/providers/fake-ai.ts +++ b/src/api/providers/fake-ai.ts @@ -4,21 +4,52 @@ import { ApiHandlerOptions, ModelInfo } from "../../shared/api" import { ApiStream } from "../transform/stream" interface FakeAI { + /** + * The unique identifier for the FakeAI instance. + * It is used to lookup the original FakeAI object in the fakeAiMap + * when the fakeAI object is read from the VSCode global state. + */ + readonly id: string + + /** + * A function set by the FakeAIHandler on the FakeAI instance, that removes + * the FakeAI instance from the fakeAIMap when the FakeAI instance is + * no longer needed. + */ + removeFromCache?: () => void + createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream getModel(): { id: string; info: ModelInfo } countTokens(content: Array): Promise completePrompt(prompt: string): Promise } +/** + * API providers configuration is stored in the VSCode global state. + * Therefore, when a new task is created, the FakeAI object in the configuration + * is a new object not related to the original one, but with the same ID. + * + * We use the ID to lookup the original FakeAI object in the mapping. + */ +let fakeAiMap: Map = new Map() + export class FakeAIHandler implements ApiHandler, SingleCompletionHandler { private ai: FakeAI constructor(options: ApiHandlerOptions) { - if (!options.fakeAi) { + const optionsFakeAi = options.fakeAi as FakeAI | undefined + if (!optionsFakeAi) { throw new Error("Fake AI is not set") } - this.ai = options.fakeAi as FakeAI + const id = optionsFakeAi.id + let cachedFakeAi = fakeAiMap.get(id) + if (cachedFakeAi === undefined) { + cachedFakeAi = optionsFakeAi + cachedFakeAi.removeFromCache = () => fakeAiMap.delete(id) + fakeAiMap.set(id, cachedFakeAi) + } + this.ai = cachedFakeAi } async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { From 0570154eab8be502621edd02b761801b8e94dd7b Mon Sep 17 00:00:00 2001 From: Dominik Oswald <6849456+d-oit@users.noreply.github.com> Date: Wed, 23 Apr 2025 15:34:32 +0200 Subject: [PATCH 11/20] Remove unnecessary cost calculation from vscode-lm.ts (#2875) * feat: Removed unnecessary cost calculation * Update vscode-lm.ts --------- Co-authored-by: Matt Rubens --- src/api/providers/vscode-lm.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/api/providers/vscode-lm.ts b/src/api/providers/vscode-lm.ts index af998389c27..d37f92d7f53 100644 --- a/src/api/providers/vscode-lm.ts +++ b/src/api/providers/vscode-lm.ts @@ -2,7 +2,6 @@ import { Anthropic } from "@anthropic-ai/sdk" import * as vscode from "vscode" import { SingleCompletionHandler } from "../" -import { calculateApiCostAnthropic } from "../../utils/cost" import { ApiStream } from "../transform/stream" import { convertToVsCodeLmMessages } from "../transform/vscode-lm-format" import { SELECTOR_SEPARATOR, stringifyVsCodeLmModelSelector } from "../../shared/vsCodeSelectorUtils" @@ -443,8 +442,7 @@ export class VsCodeLmHandler extends BaseProvider implements SingleCompletionHan yield { type: "usage", inputTokens: totalInputTokens, - outputTokens: totalOutputTokens, - totalCost: calculateApiCostAnthropic(this.getModel().info, totalInputTokens, totalOutputTokens), + outputTokens: totalOutputTokens } } catch (error: unknown) { this.ensureCleanState() From 047722679d976c662f682045e20f4ab2015720a9 Mon Sep 17 00:00:00 2001 From: mlopezr Date: Wed, 23 Apr 2025 15:36:50 +0200 Subject: [PATCH 12/20] Allow Amazon Bedrock Marketplace ARNs (#2874) * Update validate.ts Allow ARNs from Bedrock Marketplace, which are different because models are deployed using SageMaker Inference behind the scenes. * Update bedrock.ts Allow ARNs from Bedrock Marketplace, which are different because models are deployed using SageMaker Inference behind the scenes. --- src/api/providers/bedrock.ts | 2 +- webview-ui/src/utils/validate.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/providers/bedrock.ts b/src/api/providers/bedrock.ts index c2da82d4acf..116e2822dd5 100644 --- a/src/api/providers/bedrock.ts +++ b/src/api/providers/bedrock.ts @@ -532,7 +532,7 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH * match[4] - The resource ID (e.g., "anthropic.claude-3-sonnet-20240229-v1:0") */ - const arnRegex = /^arn:aws:bedrock:([^:]+):([^:]*):(?:([^\/]+)\/([\w\.\-:]+)|([^\/]+))$/ + const arnRegex = /^arn:aws:(?:bedrock|sagemaker):([^:]+):([^:]*):(?:([^\/]+)\/([\w\.\-:]+)|([^\/]+))$/ let match = arn.match(arnRegex) if (match && match[1] && match[3] && match[4]) { diff --git a/webview-ui/src/utils/validate.ts b/webview-ui/src/utils/validate.ts index 82fda92626d..439839a347e 100644 --- a/webview-ui/src/utils/validate.ts +++ b/webview-ui/src/utils/validate.ts @@ -89,7 +89,7 @@ export function validateApiConfiguration(apiConfiguration?: ApiConfiguration): s */ export function validateBedrockArn(arn: string, region?: string) { // Validate ARN format - const arnRegex = /^arn:aws:bedrock:([^:]+):([^:]*):(?:([^/]+)\/([\w.\-:]+)|([^/]+))$/ + const arnRegex = /^arn:aws:(?:bedrock|sagemaker):([^:]+):([^:]*):(?:([^/]+)\/([\w.\-:]+)|([^/]+))$/ const match = arn.match(arnRegex) if (!match) { From 558925ab8c1baaea03bc9e0cbf3e780023cf2f70 Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Wed, 23 Apr 2025 06:45:57 -0700 Subject: [PATCH 13/20] OpenRouter Gemini caching (#2847) * OpenRouter Gemini caching * Fix tests * Remove unsupported models * Clean up the task header a bit * Update src/api/providers/openrouter.ts Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * Remove model that doesn't seem to work --------- Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> --- .../providers/__tests__/openrouter.test.ts | 486 +++++++++--------- src/api/providers/openrouter.ts | 132 +++-- src/api/transform/stream.ts | 4 +- webview-ui/src/components/chat/ChatRow.tsx | 6 +- .../components/chat/ContextWindowProgress.tsx | 90 ++++ webview-ui/src/components/chat/Mention.tsx | 33 ++ .../src/components/chat/TaskActions.tsx | 54 ++ webview-ui/src/components/chat/TaskHeader.tsx | 229 ++------- 8 files changed, 537 insertions(+), 497 deletions(-) create mode 100644 webview-ui/src/components/chat/ContextWindowProgress.tsx create mode 100644 webview-ui/src/components/chat/Mention.tsx create mode 100644 webview-ui/src/components/chat/TaskActions.tsx diff --git a/src/api/providers/__tests__/openrouter.test.ts b/src/api/providers/__tests__/openrouter.test.ts index 996644b07f7..d592e6c968b 100644 --- a/src/api/providers/__tests__/openrouter.test.ts +++ b/src/api/providers/__tests__/openrouter.test.ts @@ -15,7 +15,7 @@ jest.mock("delay", () => jest.fn(() => Promise.resolve())) const mockOpenRouterModelInfo: ModelInfo = { maxTokens: 1000, contextWindow: 2000, - supportsPromptCache: true, + supportsPromptCache: false, inputPrice: 0.01, outputPrice: 0.02, } @@ -31,9 +31,10 @@ describe("OpenRouterHandler", () => { jest.clearAllMocks() }) - test("constructor initializes with correct options", () => { + it("initializes with correct options", () => { const handler = new OpenRouterHandler(mockOptions) expect(handler).toBeInstanceOf(OpenRouterHandler) + expect(OpenAI).toHaveBeenCalledWith({ baseURL: "https://openrouter.ai/api/v1", apiKey: mockOptions.openRouterApiKey, @@ -44,284 +45,257 @@ describe("OpenRouterHandler", () => { }) }) - test("getModel returns correct model info when options are provided", () => { - const handler = new OpenRouterHandler(mockOptions) - const result = handler.getModel() - - expect(result).toEqual({ - id: mockOptions.openRouterModelId, - info: mockOptions.openRouterModelInfo, - maxTokens: 1000, - temperature: 0, - thinking: undefined, - topP: undefined, + describe("getModel", () => { + it("returns correct model info when options are provided", () => { + const handler = new OpenRouterHandler(mockOptions) + const result = handler.getModel() + + expect(result).toEqual({ + id: mockOptions.openRouterModelId, + info: mockOptions.openRouterModelInfo, + maxTokens: 1000, + reasoning: undefined, + supportsPromptCache: false, + temperature: 0, + thinking: undefined, + topP: undefined, + }) }) - }) - - test("getModel returns default model info when options are not provided", () => { - const handler = new OpenRouterHandler({}) - const result = handler.getModel() - expect(result.id).toBe("anthropic/claude-3.7-sonnet") - expect(result.info.supportsPromptCache).toBe(true) - }) + it("returns default model info when options are not provided", () => { + const handler = new OpenRouterHandler({}) + const result = handler.getModel() - test("getModel honors custom maxTokens for thinking models", () => { - const handler = new OpenRouterHandler({ - openRouterApiKey: "test-key", - openRouterModelId: "test-model", - openRouterModelInfo: { - ...mockOpenRouterModelInfo, - maxTokens: 128_000, - thinking: true, - }, - modelMaxTokens: 32_768, - modelMaxThinkingTokens: 16_384, + expect(result.id).toBe("anthropic/claude-3.7-sonnet") + expect(result.info.supportsPromptCache).toBe(true) }) - const result = handler.getModel() - expect(result.maxTokens).toBe(32_768) - expect(result.thinking).toEqual({ type: "enabled", budget_tokens: 16_384 }) - expect(result.temperature).toBe(1.0) - }) - - test("getModel does not honor custom maxTokens for non-thinking models", () => { - const handler = new OpenRouterHandler({ - ...mockOptions, - modelMaxTokens: 32_768, - modelMaxThinkingTokens: 16_384, + it("honors custom maxTokens for thinking models", () => { + const handler = new OpenRouterHandler({ + openRouterApiKey: "test-key", + openRouterModelId: "test-model", + openRouterModelInfo: { + ...mockOpenRouterModelInfo, + maxTokens: 128_000, + thinking: true, + }, + modelMaxTokens: 32_768, + modelMaxThinkingTokens: 16_384, + }) + + const result = handler.getModel() + expect(result.maxTokens).toBe(32_768) + expect(result.thinking).toEqual({ type: "enabled", budget_tokens: 16_384 }) + expect(result.temperature).toBe(1.0) }) - const result = handler.getModel() - expect(result.maxTokens).toBe(1000) - expect(result.thinking).toBeUndefined() - expect(result.temperature).toBe(0) - }) - - test("createMessage generates correct stream chunks", async () => { - const handler = new OpenRouterHandler(mockOptions) - const mockStream = { - async *[Symbol.asyncIterator]() { - yield { - id: "test-id", - choices: [ - { - delta: { - content: "test response", - }, - }, - ], - } - // Add usage information in the stream response - yield { - id: "test-id", - choices: [{ delta: {} }], - usage: { - prompt_tokens: 10, - completion_tokens: 20, - cost: 0.001, - }, - } - }, - } - - // Mock OpenAI chat.completions.create - const mockCreate = jest.fn().mockResolvedValue(mockStream) - ;(OpenAI as jest.MockedClass).prototype.chat = { - completions: { create: mockCreate }, - } as any - - const systemPrompt = "test system prompt" - const messages: Anthropic.Messages.MessageParam[] = [{ role: "user" as const, content: "test message" }] - - const generator = handler.createMessage(systemPrompt, messages) - const chunks = [] - - for await (const chunk of generator) { - chunks.push(chunk) - } - - // Verify stream chunks - expect(chunks).toHaveLength(2) // One text chunk and one usage chunk - expect(chunks[0]).toEqual({ - type: "text", - text: "test response", + it("does not honor custom maxTokens for non-thinking models", () => { + const handler = new OpenRouterHandler({ + ...mockOptions, + modelMaxTokens: 32_768, + modelMaxThinkingTokens: 16_384, + }) + + const result = handler.getModel() + expect(result.maxTokens).toBe(1000) + expect(result.thinking).toBeUndefined() + expect(result.temperature).toBe(0) }) - expect(chunks[1]).toEqual({ - type: "usage", - inputTokens: 10, - outputTokens: 20, - totalCost: 0.001, - }) - - // Verify OpenAI client was called with correct parameters - expect(mockCreate).toHaveBeenCalledWith( - expect.objectContaining({ - model: mockOptions.openRouterModelId, - temperature: 0, - messages: expect.arrayContaining([ - { role: "system", content: systemPrompt }, - { role: "user", content: "test message" }, - ]), - stream: true, - }), - ) }) - test("createMessage with middle-out transform enabled", async () => { - const handler = new OpenRouterHandler({ - ...mockOptions, - openRouterUseMiddleOutTransform: true, + describe("createMessage", () => { + it("generates correct stream chunks", async () => { + const handler = new OpenRouterHandler(mockOptions) + + const mockStream = { + async *[Symbol.asyncIterator]() { + yield { + id: "test-id", + choices: [{ delta: { content: "test response" } }], + } + yield { + id: "test-id", + choices: [{ delta: {} }], + usage: { prompt_tokens: 10, completion_tokens: 20, cost: 0.001 }, + } + }, + } + + // Mock OpenAI chat.completions.create + const mockCreate = jest.fn().mockResolvedValue(mockStream) + + ;(OpenAI as jest.MockedClass).prototype.chat = { + completions: { create: mockCreate }, + } as any + + const systemPrompt = "test system prompt" + const messages: Anthropic.Messages.MessageParam[] = [{ role: "user" as const, content: "test message" }] + + const generator = handler.createMessage(systemPrompt, messages) + const chunks = [] + + for await (const chunk of generator) { + chunks.push(chunk) + } + + // Verify stream chunks + expect(chunks).toHaveLength(2) // One text chunk and one usage chunk + expect(chunks[0]).toEqual({ type: "text", text: "test response" }) + expect(chunks[1]).toEqual({ type: "usage", inputTokens: 10, outputTokens: 20, totalCost: 0.001 }) + + // Verify OpenAI client was called with correct parameters + expect(mockCreate).toHaveBeenCalledWith( + expect.objectContaining({ + model: mockOptions.openRouterModelId, + temperature: 0, + messages: expect.arrayContaining([ + { role: "system", content: systemPrompt }, + { role: "user", content: "test message" }, + ]), + stream: true, + }), + ) }) - const mockStream = { - async *[Symbol.asyncIterator]() { - yield { - id: "test-id", - choices: [ - { - delta: { - content: "test response", - }, - }, - ], - } - }, - } - const mockCreate = jest.fn().mockResolvedValue(mockStream) - ;(OpenAI as jest.MockedClass).prototype.chat = { - completions: { create: mockCreate }, - } as any - ;(axios.get as jest.Mock).mockResolvedValue({ data: { data: {} } }) - - await handler.createMessage("test", []).next() - - expect(mockCreate).toHaveBeenCalledWith( - expect.objectContaining({ - transforms: ["middle-out"], - }), - ) - }) - - test("createMessage with Claude model adds cache control", async () => { - const handler = new OpenRouterHandler({ - ...mockOptions, - openRouterModelId: "anthropic/claude-3.5-sonnet", + it("supports the middle-out transform", async () => { + const handler = new OpenRouterHandler({ + ...mockOptions, + openRouterUseMiddleOutTransform: true, + }) + const mockStream = { + async *[Symbol.asyncIterator]() { + yield { + id: "test-id", + choices: [{ delta: { content: "test response" } }], + } + }, + } + + const mockCreate = jest.fn().mockResolvedValue(mockStream) + ;(OpenAI as jest.MockedClass).prototype.chat = { + completions: { create: mockCreate }, + } as any + ;(axios.get as jest.Mock).mockResolvedValue({ data: { data: {} } }) + + await handler.createMessage("test", []).next() + + expect(mockCreate).toHaveBeenCalledWith(expect.objectContaining({ transforms: ["middle-out"] })) }) - const mockStream = { - async *[Symbol.asyncIterator]() { - yield { - id: "test-id", - choices: [ - { - delta: { - content: "test response", - }, - }, - ], - } - }, - } - - const mockCreate = jest.fn().mockResolvedValue(mockStream) - ;(OpenAI as jest.MockedClass).prototype.chat = { - completions: { create: mockCreate }, - } as any - ;(axios.get as jest.Mock).mockResolvedValue({ data: { data: {} } }) - - const messages: Anthropic.Messages.MessageParam[] = [ - { role: "user", content: "message 1" }, - { role: "assistant", content: "response 1" }, - { role: "user", content: "message 2" }, - ] - - await handler.createMessage("test system", messages).next() - - expect(mockCreate).toHaveBeenCalledWith( - expect.objectContaining({ - messages: expect.arrayContaining([ - expect.objectContaining({ - role: "system", - content: expect.arrayContaining([ - expect.objectContaining({ - cache_control: { type: "ephemeral" }, - }), - ]), - }), - ]), - }), - ) - }) - test("createMessage handles API errors", async () => { - const handler = new OpenRouterHandler(mockOptions) - const mockStream = { - async *[Symbol.asyncIterator]() { - yield { - error: { - message: "API Error", - code: 500, - }, - } - }, - } - - const mockCreate = jest.fn().mockResolvedValue(mockStream) - ;(OpenAI as jest.MockedClass).prototype.chat = { - completions: { create: mockCreate }, - } as any + it("adds cache control for supported models", async () => { + const handler = new OpenRouterHandler({ + ...mockOptions, + openRouterModelInfo: { + ...mockOpenRouterModelInfo, + supportsPromptCache: true, + }, + openRouterModelId: "anthropic/claude-3.5-sonnet", + }) + + const mockStream = { + async *[Symbol.asyncIterator]() { + yield { + id: "test-id", + choices: [{ delta: { content: "test response" } }], + } + }, + } + + const mockCreate = jest.fn().mockResolvedValue(mockStream) + ;(OpenAI as jest.MockedClass).prototype.chat = { + completions: { create: mockCreate }, + } as any + ;(axios.get as jest.Mock).mockResolvedValue({ data: { data: {} } }) + + const messages: Anthropic.Messages.MessageParam[] = [ + { role: "user", content: "message 1" }, + { role: "assistant", content: "response 1" }, + { role: "user", content: "message 2" }, + ] + + await handler.createMessage("test system", messages).next() + + expect(mockCreate).toHaveBeenCalledWith( + expect.objectContaining({ + messages: expect.arrayContaining([ + expect.objectContaining({ + role: "system", + content: expect.arrayContaining([ + expect.objectContaining({ cache_control: { type: "ephemeral" } }), + ]), + }), + ]), + }), + ) + }) - const generator = handler.createMessage("test", []) - await expect(generator.next()).rejects.toThrow("OpenRouter API Error 500: API Error") + it("handles API errors", async () => { + const handler = new OpenRouterHandler(mockOptions) + const mockStream = { + async *[Symbol.asyncIterator]() { + yield { error: { message: "API Error", code: 500 } } + }, + } + + const mockCreate = jest.fn().mockResolvedValue(mockStream) + ;(OpenAI as jest.MockedClass).prototype.chat = { + completions: { create: mockCreate }, + } as any + + const generator = handler.createMessage("test", []) + await expect(generator.next()).rejects.toThrow("OpenRouter API Error 500: API Error") + }) }) - test("completePrompt returns correct response", async () => { - const handler = new OpenRouterHandler(mockOptions) - const mockResponse = { choices: [{ message: { content: "test completion" } }] } + describe("completePrompt", () => { + it("returns correct response", async () => { + const handler = new OpenRouterHandler(mockOptions) + const mockResponse = { choices: [{ message: { content: "test completion" } }] } - const mockCreate = jest.fn().mockResolvedValue(mockResponse) - ;(OpenAI as jest.MockedClass).prototype.chat = { - completions: { create: mockCreate }, - } as any + const mockCreate = jest.fn().mockResolvedValue(mockResponse) + ;(OpenAI as jest.MockedClass).prototype.chat = { + completions: { create: mockCreate }, + } as any - const result = await handler.completePrompt("test prompt") + const result = await handler.completePrompt("test prompt") - expect(result).toBe("test completion") + expect(result).toBe("test completion") - expect(mockCreate).toHaveBeenCalledWith({ - model: mockOptions.openRouterModelId, - max_tokens: 1000, - thinking: undefined, - temperature: 0, - messages: [{ role: "user", content: "test prompt" }], - stream: false, + expect(mockCreate).toHaveBeenCalledWith({ + model: mockOptions.openRouterModelId, + max_tokens: 1000, + thinking: undefined, + temperature: 0, + messages: [{ role: "user", content: "test prompt" }], + stream: false, + }) }) - }) - - test("completePrompt handles API errors", async () => { - const handler = new OpenRouterHandler(mockOptions) - const mockError = { - error: { - message: "API Error", - code: 500, - }, - } - const mockCreate = jest.fn().mockResolvedValue(mockError) - ;(OpenAI as jest.MockedClass).prototype.chat = { - completions: { create: mockCreate }, - } as any + it("handles API errors", async () => { + const handler = new OpenRouterHandler(mockOptions) + const mockError = { + error: { + message: "API Error", + code: 500, + }, + } + + const mockCreate = jest.fn().mockResolvedValue(mockError) + ;(OpenAI as jest.MockedClass).prototype.chat = { + completions: { create: mockCreate }, + } as any + + await expect(handler.completePrompt("test prompt")).rejects.toThrow("OpenRouter API Error 500: API Error") + }) - await expect(handler.completePrompt("test prompt")).rejects.toThrow("OpenRouter API Error 500: API Error") - }) + it("handles unexpected errors", async () => { + const handler = new OpenRouterHandler(mockOptions) + const mockCreate = jest.fn().mockRejectedValue(new Error("Unexpected error")) + ;(OpenAI as jest.MockedClass).prototype.chat = { + completions: { create: mockCreate }, + } as any - test("completePrompt handles unexpected errors", async () => { - const handler = new OpenRouterHandler(mockOptions) - const mockCreate = jest.fn().mockRejectedValue(new Error("Unexpected error")) - ;(OpenAI as jest.MockedClass).prototype.chat = { - completions: { create: mockCreate }, - } as any - - await expect(handler.completePrompt("test prompt")).rejects.toThrow("Unexpected error") + await expect(handler.completePrompt("test prompt")).rejects.toThrow("Unexpected error") + }) }) }) diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index 665d87542ba..ac5d8553e64 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -6,7 +6,7 @@ import OpenAI from "openai" import { ApiHandlerOptions, ModelInfo, openRouterDefaultModelId, openRouterDefaultModelInfo } from "../../shared/api" import { parseApiPrice } from "../../utils/cost" import { convertToOpenAiMessages } from "../transform/openai-format" -import { ApiStreamChunk, ApiStreamUsageChunk } from "../transform/stream" +import { ApiStreamChunk } from "../transform/stream" import { convertToR1Format } from "../transform/r1-format" import { DEFAULT_HEADERS, DEEP_SEEK_DEFAULT_TEMPERATURE } from "./constants" @@ -28,6 +28,22 @@ type OpenRouterChatCompletionParams = OpenAI.Chat.ChatCompletionCreateParams & { } } +// See `OpenAI.Chat.Completions.ChatCompletionChunk["usage"]` +// `CompletionsAPI.CompletionUsage` +// See also: https://openrouter.ai/docs/use-cases/usage-accounting +interface CompletionUsage { + completion_tokens?: number + completion_tokens_details?: { + reasoning_tokens?: number + } + prompt_tokens?: number + prompt_tokens_details?: { + cached_tokens?: number + } + total_tokens?: number + cost?: number +} + export class OpenRouterHandler extends BaseProvider implements SingleCompletionHandler { protected options: ApiHandlerOptions private client: OpenAI @@ -46,7 +62,15 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH systemPrompt: string, messages: Anthropic.Messages.MessageParam[], ): AsyncGenerator { - let { id: modelId, maxTokens, thinking, temperature, topP, reasoningEffort } = this.getModel() + let { + id: modelId, + maxTokens, + thinking, + temperature, + supportsPromptCache, + topP, + reasoningEffort, + } = this.getModel() // Convert Anthropic messages to OpenAI format. let openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [ @@ -59,46 +83,42 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH openAiMessages = convertToR1Format([{ role: "user", content: systemPrompt }, ...messages]) } - // prompt caching: https://openrouter.ai/docs/prompt-caching - // this is specifically for claude models (some models may 'support prompt caching' automatically without this) - switch (true) { - case modelId.startsWith("anthropic/"): - openAiMessages[0] = { - role: "system", - content: [ - { - type: "text", - text: systemPrompt, - // @ts-ignore-next-line - cache_control: { type: "ephemeral" }, - }, - ], - } + // Prompt caching: https://openrouter.ai/docs/prompt-caching + // Now with Gemini support: https://openrouter.ai/docs/features/prompt-caching + if (supportsPromptCache) { + openAiMessages[0] = { + role: "system", + content: [ + { + type: "text", + text: systemPrompt, + // @ts-ignore-next-line + cache_control: { type: "ephemeral" }, + }, + ], + } - // Add cache_control to the last two user messages - // (note: this works because we only ever add one user message at a time, but if we added multiple we'd need to mark the user message before the last assistant message) - const lastTwoUserMessages = openAiMessages.filter((msg) => msg.role === "user").slice(-2) + // Add cache_control to the last two user messages + // (note: this works because we only ever add one user message at a time, but if we added multiple we'd need to mark the user message before the last assistant message) + const lastTwoUserMessages = openAiMessages.filter((msg) => msg.role === "user").slice(-2) - lastTwoUserMessages.forEach((msg) => { - if (typeof msg.content === "string") { - msg.content = [{ type: "text", text: msg.content }] - } + lastTwoUserMessages.forEach((msg) => { + if (typeof msg.content === "string") { + msg.content = [{ type: "text", text: msg.content }] + } - if (Array.isArray(msg.content)) { - // NOTE: this is fine since env details will always be added at the end. but if it weren't there, and the user added a image_url type message, it would pop a text part before it and then move it after to the end. - let lastTextPart = msg.content.filter((part) => part.type === "text").pop() + if (Array.isArray(msg.content)) { + // NOTE: this is fine since env details will always be added at the end. but if it weren't there, and the user added a image_url type message, it would pop a text part before it and then move it after to the end. + let lastTextPart = msg.content.filter((part) => part.type === "text").pop() - if (!lastTextPart) { - lastTextPart = { type: "text", text: "..." } - msg.content.push(lastTextPart) - } - // @ts-ignore-next-line - lastTextPart["cache_control"] = { type: "ephemeral" } + if (!lastTextPart) { + lastTextPart = { type: "text", text: "..." } + msg.content.push(lastTextPart) } - }) - break - default: - break + // @ts-ignore-next-line + lastTextPart["cache_control"] = { type: "ephemeral" } + } + }) } // https://openrouter.ai/docs/transforms @@ -125,9 +145,9 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH const stream = await this.client.chat.completions.create(completionParams) - let lastUsage + let lastUsage: CompletionUsage | undefined = undefined - for await (const chunk of stream as unknown as AsyncIterable) { + for await (const chunk of stream) { // OpenRouter returns an error object instead of the OpenAI SDK throwing an error. if ("error" in chunk) { const error = chunk.error as { message?: string; code?: number } @@ -137,13 +157,13 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH const delta = chunk.choices[0]?.delta - if ("reasoning" in delta && delta.reasoning) { - yield { type: "reasoning", text: delta.reasoning } as ApiStreamChunk + if ("reasoning" in delta && delta.reasoning && typeof delta.reasoning === "string") { + yield { type: "reasoning", text: delta.reasoning } } if (delta?.content) { fullResponseText += delta.content - yield { type: "text", text: delta.content } as ApiStreamChunk + yield { type: "text", text: delta.content } } if (chunk.usage) { @@ -152,16 +172,16 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH } if (lastUsage) { - yield this.processUsageMetrics(lastUsage) - } - } - - processUsageMetrics(usage: any): ApiStreamUsageChunk { - return { - type: "usage", - inputTokens: usage?.prompt_tokens || 0, - outputTokens: usage?.completion_tokens || 0, - totalCost: usage?.cost || 0, + yield { + type: "usage", + inputTokens: lastUsage.prompt_tokens || 0, + outputTokens: lastUsage.completion_tokens || 0, + // Waiting on OpenRouter to figure out what this represents in the Gemini case + // and how to best support it. + // cacheReadTokens: lastUsage.prompt_tokens_details?.cached_tokens, + reasoningTokens: lastUsage.completion_tokens_details?.reasoning_tokens, + totalCost: lastUsage.cost || 0, + } } } @@ -171,7 +191,7 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH let id = modelId ?? openRouterDefaultModelId const info = modelInfo ?? openRouterDefaultModelInfo - + const supportsPromptCache = modelInfo?.supportsPromptCache const isDeepSeekR1 = id.startsWith("deepseek/deepseek-r1") || modelId === "perplexity/sonar-reasoning" const defaultTemperature = isDeepSeekR1 ? DEEP_SEEK_DEFAULT_TEMPERATURE : 0 const topP = isDeepSeekR1 ? 0.95 : undefined @@ -180,6 +200,7 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH id, info, ...getModelParams({ options: this.options, model: info, defaultTemperature }), + supportsPromptCache, topP, } } @@ -269,6 +290,11 @@ export async function getOpenRouterModels(options?: ApiHandlerOptions) { modelInfo.cacheReadsPrice = 0.03 modelInfo.maxTokens = 8192 break + case rawModel.id.startsWith("google/gemini-2.5-pro-preview-03-25"): + case rawModel.id.startsWith("google/gemini-2.0-flash-001"): + case rawModel.id.startsWith("google/gemini-flash-1.5"): + modelInfo.supportsPromptCache = true + break default: break } diff --git a/src/api/transform/stream.ts b/src/api/transform/stream.ts index 97751edd90d..caa69a09feb 100644 --- a/src/api/transform/stream.ts +++ b/src/api/transform/stream.ts @@ -1,4 +1,5 @@ export type ApiStream = AsyncGenerator + export type ApiStreamChunk = ApiStreamTextChunk | ApiStreamUsageChunk | ApiStreamReasoningChunk export interface ApiStreamTextChunk { @@ -17,5 +18,6 @@ export interface ApiStreamUsageChunk { outputTokens: number cacheWriteTokens?: number cacheReadTokens?: number - totalCost?: number // openrouter + reasoningTokens?: number + totalCost?: number } diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index ebcd59e1ee2..ec94326b632 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -21,7 +21,7 @@ import { ReasoningBlock } from "./ReasoningBlock" import Thumbnails from "../common/Thumbnails" import McpResourceRow from "../mcp/McpResourceRow" import McpToolRow from "../mcp/McpToolRow" -import { highlightMentions } from "./TaskHeader" +import { Mention } from "./Mention" import { CheckpointSaved } from "./checkpoints/CheckpointSaved" import { FollowUpSuggest } from "./FollowUpSuggest" @@ -867,7 +867,9 @@ export const ChatRowContent = ({ return (
-
{highlightMentions(message.text)}
+
+ +
+ {!!item?.size && item.size > 0 && ( + <> + + {deleteTaskId && ( + !open && setDeleteTaskId(null)} + open + /> + )} + + )} +
+ ) +} diff --git a/webview-ui/src/components/chat/TaskHeader.tsx b/webview-ui/src/components/chat/TaskHeader.tsx index 0dc2aa1a5f6..531ec908f04 100644 --- a/webview-ui/src/components/chat/TaskHeader.tsx +++ b/webview-ui/src/components/chat/TaskHeader.tsx @@ -1,23 +1,22 @@ -import React, { memo, useMemo, useRef, useState } from "react" +import { memo, useMemo, useRef, useState } from "react" import { useWindowSize } from "react-use" -import prettyBytes from "pretty-bytes" import { useTranslation } from "react-i18next" - -import { vscode } from "@/utils/vscode" -import { formatLargeNumber } from "@/utils/format" -import { calculateTokenDistribution, getMaxTokensForModel } from "@/utils/model-utils" -import { Button } from "@/components/ui" +import { VSCodeBadge } from "@vscode/webview-ui-toolkit/react" +import { CloudUpload, CloudDownload } from "lucide-react" import { ClineMessage } from "@roo/shared/ExtensionMessage" -import { mentionRegexGlobal } from "@roo/shared/context-mentions" -import { HistoryItem } from "@roo/shared/HistoryItem" +import { getMaxTokensForModel } from "@/utils/model-utils" +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui" import { useExtensionState } from "@src/context/ExtensionStateContext" + import Thumbnails from "../common/Thumbnails" import { normalizeApiConfiguration } from "../settings/ApiOptions" -import { DeleteTaskDialog } from "../history/DeleteTaskDialog" -import { cn } from "@/lib/utils" -import { VSCodeBadge } from "@vscode/webview-ui-toolkit/react" + +import { TaskActions } from "./TaskActions" +import { ContextWindowProgress } from "./ContextWindowProgress" +import { Mention } from "./Mention" interface TaskHeaderProps { task: ClineMessage @@ -31,7 +30,7 @@ interface TaskHeaderProps { onClose: () => void } -const TaskHeader: React.FC = ({ +const TaskHeader = ({ task, tokensIn, tokensOut, @@ -41,7 +40,7 @@ const TaskHeader: React.FC = ({ totalCost, contextTokens, onClose, -}) => { +}: TaskHeaderProps) => { const { t } = useTranslation() const { apiConfiguration, currentTaskItem } = useExtensionState() const { selectedModelInfo } = useMemo(() => normalizeApiConfiguration(apiConfiguration), [apiConfiguration]) @@ -53,8 +52,6 @@ const TaskHeader: React.FC = ({ const { width: windowWidth } = useWindowSize() - const shouldShowPromptCacheInfo = doesModelSupportPromptCache && apiConfiguration?.apiProvider !== "openrouter" - return (
= ({ {t("chat:task.title")} {!isTaskExpanded && ":"} - {!isTaskExpanded && {highlightMentions(task.text, false)}} + {!isTaskExpanded && ( + + + + )}
{task.images && task.images.length > 0 && } @@ -137,29 +138,37 @@ const TaskHeader: React.FC = ({
{t("chat:task.tokens")} - - - {formatLargeNumber(tokensIn || 0)} - - - - {formatLargeNumber(tokensOut || 0)} - + {typeof tokensIn === "number" && tokensIn > 0 && ( + + + {tokensIn} + + )} + {typeof tokensOut === "number" && tokensOut > 0 && ( + + + {tokensOut} + + )}
{!totalCost && }
- {shouldShowPromptCacheInfo && (cacheReads !== undefined || cacheWrites !== undefined) && ( + {doesModelSupportPromptCache && (cacheReads || cacheWrites) && (
{t("chat:task.cache")} - - + - {formatLargeNumber(cacheWrites || 0)} - - - - {formatLargeNumber(cacheReads || 0)} - + {typeof cacheWrites === "number" && cacheWrites > 0 && ( + + + {cacheWrites} + + )} + {typeof cacheReads === "number" && cacheReads > 0 && ( + + + {cacheReads} + + )}
)} @@ -180,154 +189,4 @@ const TaskHeader: React.FC = ({ ) } -export const highlightMentions = (text?: string, withShadow = true) => { - if (!text) return text - const parts = text.split(mentionRegexGlobal) - return parts.map((part, index) => { - if (index % 2 === 0) { - // This is regular text - return part - } else { - // This is a mention - return ( - vscode.postMessage({ type: "openMention", text: part })}> - @{part} - - ) - } - }) -} - -const TaskActions = ({ item }: { item: HistoryItem | undefined }) => { - const [deleteTaskId, setDeleteTaskId] = useState(null) - const { t } = useTranslation() - - return ( -
- - {!!item?.size && item.size > 0 && ( - <> - - {deleteTaskId && ( - !open && setDeleteTaskId(null)} - open - /> - )} - - )} -
- ) -} - -interface ContextWindowProgressProps { - contextWindow: number - contextTokens: number - maxTokens?: number -} - -const ContextWindowProgress = ({ contextWindow, contextTokens, maxTokens }: ContextWindowProgressProps) => { - const { t } = useTranslation() - // Use the shared utility function to calculate all token distribution values - const tokenDistribution = useMemo( - () => calculateTokenDistribution(contextWindow, contextTokens, maxTokens), - [contextWindow, contextTokens, maxTokens], - ) - - // Destructure the values we need - const { currentPercent, reservedPercent, availableSize, reservedForOutput, availablePercent } = tokenDistribution - - // For display purposes - const safeContextWindow = Math.max(0, contextWindow) - const safeContextTokens = Math.max(0, contextTokens) - - return ( - <> -
-
{formatLargeNumber(safeContextTokens)}
-
- {/* Invisible overlay for hover area */} -
- - {/* Main progress bar container */} -
- {/* Current tokens container */} -
- {/* Invisible overlay for current tokens section */} -
- {/* Current tokens used - darkest */} -
-
- - {/* Container for reserved tokens */} -
- {/* Invisible overlay for reserved section */} -
- {/* Reserved for output section - medium gray */} -
-
- - {/* Empty section (if any) */} - {availablePercent > 0 && ( -
- {/* Invisible overlay for available space */} -
-
- )} -
-
-
{formatLargeNumber(safeContextWindow)}
-
- - ) -} - export default memo(TaskHeader) From 95bed9a85d90eaf5d012ba409a4b71348cd23832 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Wed, 23 Apr 2025 09:47:19 -0400 Subject: [PATCH 14/20] v13.3.3 (#2876) --- .changeset/spotty-baboons-clap.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/spotty-baboons-clap.md diff --git a/.changeset/spotty-baboons-clap.md b/.changeset/spotty-baboons-clap.md new file mode 100644 index 00000000000..187385ed6b7 --- /dev/null +++ b/.changeset/spotty-baboons-clap.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +13.3.3 From 2689dbd244191b54d529b4ec816822685463b65c Mon Sep 17 00:00:00 2001 From: Dicha Zelianivan Arkana <51877647+elianiva@users.noreply.github.com> Date: Wed, 23 Apr 2025 22:57:50 +0700 Subject: [PATCH 15/20] fix(chat): better loading feedback (#2750) * fix(chat): better loading feedback * fix(chat): missing loading aria role Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * refactor(chat): use vscode loading for more consistency --------- Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> --- webview-ui/src/components/common/CodeAccordian.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/webview-ui/src/components/common/CodeAccordian.tsx b/webview-ui/src/components/common/CodeAccordian.tsx index 2f0cffd862b..ec39e1704a9 100644 --- a/webview-ui/src/components/common/CodeAccordian.tsx +++ b/webview-ui/src/components/common/CodeAccordian.tsx @@ -2,6 +2,7 @@ import { memo, useMemo } from "react" import { getLanguageFromPath } from "@src/utils/getLanguageFromPath" import CodeBlock, { CODE_BLOCK_BG_COLOR } from "./CodeBlock" import { ToolProgressStatus } from "@roo/shared/ExtensionMessage" +import { VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react" interface CodeAccordianProps { code?: string @@ -69,7 +70,9 @@ const CodeAccordian = ({ MozUserSelect: "none", msUserSelect: "none", }} + className={`${isLoading ? "animate-pulse" : ""}`} onClick={isLoading ? undefined : onToggleExpand}> + {isLoading && } {isFeedback || isConsoleLogs ? (
Date: Wed, 23 Apr 2025 12:21:32 -0500 Subject: [PATCH 16/20] feat: add other useful variables to the custom system prompt (#2879) --- src/core/prompts/sections/custom-system-prompt.ts | 4 ++++ src/core/prompts/system.ts | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/src/core/prompts/sections/custom-system-prompt.ts b/src/core/prompts/sections/custom-system-prompt.ts index b8353e2fd82..f401000bb55 100644 --- a/src/core/prompts/sections/custom-system-prompt.ts +++ b/src/core/prompts/sections/custom-system-prompt.ts @@ -5,6 +5,10 @@ import { fileExistsAtPath } from "../../../utils/fs" export type PromptVariables = { workspace?: string + mode?: string + language?: string + shell?: string + operatingSystem?: string } function interpolatePromptContent(content: string, variables: PromptVariables): string { diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index 077016fd55d..b7bbc06e096 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -14,6 +14,7 @@ import { DiffStrategy } from "../../shared/tools" import { McpHub } from "../../services/mcp/McpHub" import { getToolDescriptionsForMode } from "./tools" import * as vscode from "vscode" +import * as os from "os" import { getRulesSection, getSystemInfoSection, @@ -128,6 +129,10 @@ export const SYSTEM_PROMPT = async ( // Try to load custom system prompt from file const variablesForPrompt: PromptVariables = { workspace: cwd, + mode: mode, + language: language ?? formatLanguage(vscode.env.language), + shell: vscode.env.shell, + operatingSystem: os.type(), } const fileCustomSystemPrompt = await loadSystemPromptFile(cwd, mode, variablesForPrompt) From 466328ea88b51ae07ff69994bc021819ed595c0e Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Wed, 23 Apr 2025 11:16:20 -0700 Subject: [PATCH 17/20] Use formatLargeNumber on token counts in task header (#2883) * Format large numbers * Add changeset --- .changeset/shiny-wolves-attend.md | 5 +++++ webview-ui/src/components/chat/TaskHeader.tsx | 9 +++++---- 2 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 .changeset/shiny-wolves-attend.md diff --git a/.changeset/shiny-wolves-attend.md b/.changeset/shiny-wolves-attend.md new file mode 100644 index 00000000000..61b2bdc09b1 --- /dev/null +++ b/.changeset/shiny-wolves-attend.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Use formatLargeNumber on token counts in task header diff --git a/webview-ui/src/components/chat/TaskHeader.tsx b/webview-ui/src/components/chat/TaskHeader.tsx index 531ec908f04..885be542651 100644 --- a/webview-ui/src/components/chat/TaskHeader.tsx +++ b/webview-ui/src/components/chat/TaskHeader.tsx @@ -7,6 +7,7 @@ import { CloudUpload, CloudDownload } from "lucide-react" import { ClineMessage } from "@roo/shared/ExtensionMessage" import { getMaxTokensForModel } from "@/utils/model-utils" +import { formatLargeNumber } from "@/utils/format" import { cn } from "@/lib/utils" import { Button } from "@/components/ui" import { useExtensionState } from "@src/context/ExtensionStateContext" @@ -141,13 +142,13 @@ const TaskHeader = ({ {typeof tokensIn === "number" && tokensIn > 0 && ( - {tokensIn} + {formatLargeNumber(tokensIn)} )} {typeof tokensOut === "number" && tokensOut > 0 && ( - {tokensOut} + {formatLargeNumber(tokensOut)} )}
@@ -160,13 +161,13 @@ const TaskHeader = ({ {typeof cacheWrites === "number" && cacheWrites > 0 && ( - {cacheWrites} + {formatLargeNumber(cacheWrites)} )} {typeof cacheReads === "number" && cacheReads > 0 && ( - {cacheReads} + {formatLargeNumber(cacheReads)} )}
From 3131a2197fba3eca0edd7536a636341b2ca0254c Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Wed, 23 Apr 2025 11:21:35 -0700 Subject: [PATCH 18/20] Package material icons in vsix (#2882) * Package material icons in vsix * Add changeset --- .changeset/tasty-pants-applaud.md | 5 ++++ .vscodeignore | 41 +++++++++++++++++++------------ flake.lock | 27 -------------------- flake.nix | 28 --------------------- 4 files changed, 30 insertions(+), 71 deletions(-) create mode 100644 .changeset/tasty-pants-applaud.md delete mode 100644 flake.lock delete mode 100644 flake.nix diff --git a/.changeset/tasty-pants-applaud.md b/.changeset/tasty-pants-applaud.md new file mode 100644 index 00000000000..d52f8d6c881 --- /dev/null +++ b/.changeset/tasty-pants-applaud.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Package material icons in vsix diff --git a/.vscodeignore b/.vscodeignore index d5bf65b3d87..0a2a8ccec05 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -1,38 +1,44 @@ # Default +.changeset/** .github/** .husky/** .vscode/** -.vscode-test/** -out/** -out-integration/** -evals/** -e2e/** +coverage/** node_modules/** src/** +scripts/** .gitignore -.yarnrc esbuild.js -vsc-extension-quickstart.md +jest.* **/tsconfig.json **/.eslintrc.json +.prettierignore **/*.map **/*.ts -**/.vscode-test.* +**/.gitignore # Custom -.nvmrc +.env.sample +.git-blame-ignore-revs +.gitconfig .gitattributes -.prettierignore +.tool-versions +.vite-port +.nvmrc .clinerules* .roomodes +.rooignore .roo/** cline_docs/** -coverage/** +e2e/** +evals/** locales/** -benchmark/** -.direnv/** +out/** +ellipsis.yaml +knip.json -# Ignore all webview-ui files except the build directory (https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/frameworks/hello-world-react-cra/.vscodeignore) +# Ignore all webview-ui files except the build directory. +# https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/frameworks/hello-world-react-cra/.vscodeignore webview-ui/src/** webview-ui/public/** webview-ui/scripts/** @@ -41,17 +47,20 @@ webview-ui/README.md webview-ui/package.json webview-ui/package-lock.json webview-ui/node_modules/** -**/.gitignore -# Fix issue where codicons don't get packaged (https://github.com/microsoft/vscode-extension-samples/issues/692) +# Include codicons !node_modules/@vscode/codicons/dist/codicon.css !node_modules/@vscode/codicons/dist/codicon.ttf +# Include material icons +!node_modules/vscode-material-icons/generated/** + # Include default themes JSON files used in getTheme !src/integrations/theme/default-themes/** # Ignore doc assets assets/docs/** + # Include icons and images !assets/icons/** !assets/images/** diff --git a/flake.lock b/flake.lock deleted file mode 100644 index 5d5fa53a698..00000000000 --- a/flake.lock +++ /dev/null @@ -1,27 +0,0 @@ -{ - "nodes": { - "nixpkgs": { - "locked": { - "lastModified": 1737569578, - "narHash": "sha256-6qY0pk2QmUtBT9Mywdvif0i/CLVgpCjMUn6g9vB+f3M=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "47addd76727f42d351590c905d9d1905ca895b82", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-24.11", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "nixpkgs": "nixpkgs" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/flake.nix b/flake.nix deleted file mode 100644 index 690aa9e0183..00000000000 --- a/flake.nix +++ /dev/null @@ -1,28 +0,0 @@ -{ - description = "Roo Code development environment"; - - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11"; - }; - - outputs = { self, nixpkgs, ... }: let - systems = [ "aarch64-darwin" "x86_64-linux" ]; - - forAllSystems = nixpkgs.lib.genAttrs systems; - - mkDevShell = system: let - pkgs = import nixpkgs { inherit system; }; - in pkgs.mkShell { - name = "roo-code"; - - packages = with pkgs; [ - nodejs_20 - corepack_20 - ]; - }; - in { - devShells = forAllSystems (system: { - default = mkDevShell system; - }); - }; -} From 9991f42b6894e6c083ad4e55eb189c182c8f39aa Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Wed, 23 Apr 2025 11:35:50 -0700 Subject: [PATCH 19/20] Gemini prompt caching (#2827) --- src/api/index.ts | 3 +- src/api/providers/__tests__/gemini.test.ts | 100 ++++++++++-- src/api/providers/anthropic.ts | 19 ++- src/api/providers/gemini.ts | 153 ++++++++++++++---- src/api/providers/vscode-lm.ts | 2 +- src/core/Cline.ts | 20 ++- src/exports/roo-code.d.ts | 45 ++++++ src/exports/types.ts | 45 ++++++ src/schemas/index.ts | 11 ++ src/shared/api.ts | 50 +++++- .../src/components/settings/ApiOptions.tsx | 1 + .../src/components/settings/ModelInfoView.tsx | 20 ++- .../src/components/settings/ModelPicker.tsx | 1 + 13 files changed, 400 insertions(+), 70 deletions(-) diff --git a/src/api/index.ts b/src/api/index.ts index ef8f99b7e74..10a959b548c 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -28,7 +28,8 @@ export interface SingleCompletionHandler { } export interface ApiHandler { - createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream + createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[], cacheKey?: string): ApiStream + getModel(): { id: string; info: ModelInfo } /** diff --git a/src/api/providers/__tests__/gemini.test.ts b/src/api/providers/__tests__/gemini.test.ts index 897ece3ed31..34e057ffa45 100644 --- a/src/api/providers/__tests__/gemini.test.ts +++ b/src/api/providers/__tests__/gemini.test.ts @@ -3,7 +3,7 @@ import { Anthropic } from "@anthropic-ai/sdk" import { GeminiHandler } from "../gemini" -import { geminiDefaultModelId } from "../../../shared/api" +import { geminiDefaultModelId, type ModelInfo } from "../../../shared/api" const GEMINI_20_FLASH_THINKING_NAME = "gemini-2.0-flash-thinking-exp-1219" @@ -72,18 +72,15 @@ describe("GeminiHandler", () => { // Should have 3 chunks: 'Hello', ' world!', and usage info expect(chunks.length).toBe(3) - expect(chunks[0]).toEqual({ - type: "text", - text: "Hello", - }) - expect(chunks[1]).toEqual({ - type: "text", - text: " world!", - }) + expect(chunks[0]).toEqual({ type: "text", text: "Hello" }) + expect(chunks[1]).toEqual({ type: "text", text: " world!" }) expect(chunks[2]).toEqual({ type: "usage", inputTokens: 10, outputTokens: 5, + cacheReadTokens: undefined, + cacheWriteTokens: undefined, + thinkingTokens: undefined, }) // Verify the call to generateContentStream @@ -171,4 +168,89 @@ describe("GeminiHandler", () => { expect(modelInfo.id).toBe(geminiDefaultModelId) // Default model }) }) + + describe("calculateCost", () => { + // Mock ModelInfo based on gemini-1.5-flash-latest pricing (per 1M tokens) + // Removed 'id' and 'name' as they are not part of ModelInfo type directly + const mockInfo: ModelInfo = { + inputPrice: 0.125, // $/1M tokens + outputPrice: 0.375, // $/1M tokens + cacheWritesPrice: 0.125, // Assume same as input for test + cacheReadsPrice: 0.125 * 0.25, // Assume 0.25x input for test + contextWindow: 1_000_000, + maxTokens: 8192, + supportsPromptCache: true, // Enable cache calculations for tests + } + + it("should calculate cost correctly based on input and output tokens", () => { + const inputTokens = 10000 // Use larger numbers for per-million pricing + const outputTokens = 20000 + // Added non-null assertions (!) as mockInfo guarantees these values + const expectedCost = + (inputTokens / 1_000_000) * mockInfo.inputPrice! + (outputTokens / 1_000_000) * mockInfo.outputPrice! + + const cost = handler.calculateCost({ info: mockInfo, inputTokens, outputTokens }) + expect(cost).toBeCloseTo(expectedCost) + }) + + it("should return 0 if token counts are zero", () => { + // Note: The method expects numbers, not undefined. Passing undefined would be a type error. + // The calculateCost method itself returns undefined if prices are missing, but 0 if tokens are 0 and prices exist. + expect(handler.calculateCost({ info: mockInfo, inputTokens: 0, outputTokens: 0 })).toBe(0) + }) + + it("should handle only input tokens", () => { + const inputTokens = 5000 + // Added non-null assertion (!) + const expectedCost = (inputTokens / 1_000_000) * mockInfo.inputPrice! + expect(handler.calculateCost({ info: mockInfo, inputTokens, outputTokens: 0 })).toBeCloseTo(expectedCost) + }) + + it("should handle only output tokens", () => { + const outputTokens = 15000 + // Added non-null assertion (!) + const expectedCost = (outputTokens / 1_000_000) * mockInfo.outputPrice! + expect(handler.calculateCost({ info: mockInfo, inputTokens: 0, outputTokens })).toBeCloseTo(expectedCost) + }) + + it("should calculate cost with cache write tokens", () => { + const inputTokens = 10000 + const outputTokens = 20000 + const cacheWriteTokens = 5000 + const CACHE_TTL = 5 // Match the constant in gemini.ts + + // Added non-null assertions (!) + const expectedInputCost = (inputTokens / 1_000_000) * mockInfo.inputPrice! + const expectedOutputCost = (outputTokens / 1_000_000) * mockInfo.outputPrice! + const expectedCacheWriteCost = + mockInfo.cacheWritesPrice! * (cacheWriteTokens / 1_000_000) * (CACHE_TTL / 60) + const expectedCost = expectedInputCost + expectedOutputCost + expectedCacheWriteCost + + const cost = handler.calculateCost({ info: mockInfo, inputTokens, outputTokens, cacheWriteTokens }) + expect(cost).toBeCloseTo(expectedCost) + }) + + it("should calculate cost with cache read tokens", () => { + const inputTokens = 10000 // Total logical input + const outputTokens = 20000 + const cacheReadTokens = 8000 // Part of inputTokens read from cache + + const uncachedReadTokens = inputTokens - cacheReadTokens + // Added non-null assertions (!) + const expectedInputCost = (uncachedReadTokens / 1_000_000) * mockInfo.inputPrice! + const expectedOutputCost = (outputTokens / 1_000_000) * mockInfo.outputPrice! + const expectedCacheReadCost = mockInfo.cacheReadsPrice! * (cacheReadTokens / 1_000_000) + const expectedCost = expectedInputCost + expectedOutputCost + expectedCacheReadCost + + const cost = handler.calculateCost({ info: mockInfo, inputTokens, outputTokens, cacheReadTokens }) + expect(cost).toBeCloseTo(expectedCost) + }) + + it("should return undefined if pricing info is missing", () => { + // Create a copy and explicitly set a price to undefined + const incompleteInfo: ModelInfo = { ...mockInfo, outputPrice: undefined } + const cost = handler.calculateCost({ info: incompleteInfo, inputTokens: 1000, outputTokens: 1000 }) + expect(cost).toBeUndefined() + }) + }) }) diff --git a/src/api/providers/anthropic.ts b/src/api/providers/anthropic.ts index 9032754ac6a..5489b326093 100644 --- a/src/api/providers/anthropic.ts +++ b/src/api/providers/anthropic.ts @@ -42,8 +42,14 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa case "claude-3-opus-20240229": case "claude-3-haiku-20240307": { /** - * The latest message will be the new user message, one before will - * be the assistant message from a previous request, and the user message before that will be a previously cached user message. So we need to mark the latest user message as ephemeral to cache it for the next request, and mark the second to last user message as ephemeral to let the server know the last message to retrieve from the cache for the current request.. + * The latest message will be the new user message, one before + * will be the assistant message from a previous request, and + * the user message before that will be a previously cached user + * message. So we need to mark the latest user message as + * ephemeral to cache it for the next request, and mark the + * second to last user message as ephemeral to let the server + * know the last message to retrieve from the cache for the + * current request. */ const userMsgIndices = messages.reduce( (acc, msg, index) => (msg.role === "user" ? [...acc, index] : acc), @@ -77,9 +83,6 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa } return message }), - // tools, // cache breakpoints go from tools > system > messages, and since tools dont change, we can just set the breakpoint at the end of system (this avoids having to set a breakpoint at the end of tools which by itself does not meet min requirements for haiku caching) - // tool_choice: { type: "auto" }, - // tools: tools, stream: true, }, (() => { @@ -102,9 +105,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa case "claude-3-opus-20240229": case "claude-3-haiku-20240307": betas.push("prompt-caching-2024-07-31") - return { - headers: { "anthropic-beta": betas.join(",") }, - } + return { headers: { "anthropic-beta": betas.join(",") } } default: return undefined } @@ -119,8 +120,6 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa temperature, system: [{ text: systemPrompt, type: "text" }], messages, - // tools, - // tool_choice: { type: "auto" }, stream: true, })) as any break diff --git a/src/api/providers/gemini.ts b/src/api/providers/gemini.ts index 4bf3b293bb2..de77379abb4 100644 --- a/src/api/providers/gemini.ts +++ b/src/api/providers/gemini.ts @@ -1,9 +1,9 @@ import type { Anthropic } from "@anthropic-ai/sdk" import { GoogleGenAI, - ThinkingConfig, type GenerateContentResponseUsageMetadata, type GenerateContentParameters, + type Content, } from "@google/genai" import { SingleCompletionHandler } from "../" @@ -13,30 +13,64 @@ import { convertAnthropicContentToGemini, convertAnthropicMessageToGemini } from import type { ApiStream } from "../transform/stream" import { BaseProvider } from "./base-provider" +const CACHE_TTL = 5 + export class GeminiHandler extends BaseProvider implements SingleCompletionHandler { protected options: ApiHandlerOptions private client: GoogleGenAI + private contentCaches: Map constructor(options: ApiHandlerOptions) { super() this.options = options this.client = new GoogleGenAI({ apiKey: options.geminiApiKey ?? "not-provided" }) + this.contentCaches = new Map() } - async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { - const { id: model, thinkingConfig, maxOutputTokens } = this.getModel() + async *createMessage( + systemInstruction: string, + messages: Anthropic.Messages.MessageParam[], + cacheKey?: string, + ): ApiStream { + const { id: model, thinkingConfig, maxOutputTokens, info } = this.getModel() + + const contents = messages.map(convertAnthropicMessageToGemini) + let uncachedContent: Content[] | undefined = undefined + let cachedContent: string | undefined = undefined + let cacheWriteTokens: number | undefined = undefined + + // https://ai.google.dev/gemini-api/docs/caching?lang=node + if (info.supportsPromptCache && cacheKey) { + const cacheEntry = this.contentCaches.get(cacheKey) + + if (cacheEntry) { + uncachedContent = contents.slice(cacheEntry.count, contents.length) + cachedContent = cacheEntry.key + } + + const newCacheEntry = await this.client.caches.create({ + model, + config: { contents, systemInstruction, ttl: `${CACHE_TTL * 60}s` }, + }) + + if (newCacheEntry.name) { + this.contentCaches.set(cacheKey, { key: newCacheEntry.name, count: contents.length }) + cacheWriteTokens = newCacheEntry.usageMetadata?.totalTokenCount ?? 0 + } + } const params: GenerateContentParameters = { model, - contents: messages.map(convertAnthropicMessageToGemini), + contents: uncachedContent ?? contents, config: { + cachedContent, + systemInstruction: cachedContent ? undefined : systemInstruction, httpOptions: this.options.googleGeminiBaseUrl ? { baseUrl: this.options.googleGeminiBaseUrl } : undefined, thinkingConfig, maxOutputTokens, temperature: this.options.modelTemperature ?? 0, - systemInstruction: systemPrompt, }, } @@ -55,46 +89,58 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl } if (lastUsageMetadata) { + const inputTokens = lastUsageMetadata.promptTokenCount ?? 0 + const outputTokens = lastUsageMetadata.candidatesTokenCount ?? 0 + const cacheReadTokens = lastUsageMetadata.cachedContentTokenCount + const reasoningTokens = lastUsageMetadata.thoughtsTokenCount + + const totalCost = this.calculateCost({ + info, + inputTokens, + outputTokens, + cacheWriteTokens, + cacheReadTokens, + }) + yield { type: "usage", - inputTokens: lastUsageMetadata.promptTokenCount ?? 0, - outputTokens: lastUsageMetadata.candidatesTokenCount ?? 0, + inputTokens, + outputTokens, + cacheWriteTokens, + cacheReadTokens, + reasoningTokens, + totalCost, } } } - override getModel(): { - id: GeminiModelId - info: ModelInfo - thinkingConfig?: ThinkingConfig - maxOutputTokens?: number - } { + override getModel() { let id = this.options.apiModelId ? (this.options.apiModelId as GeminiModelId) : geminiDefaultModelId let info: ModelInfo = geminiModels[id] - let thinkingConfig: ThinkingConfig | undefined = undefined - let maxOutputTokens: number | undefined = undefined - const thinkingSuffix = ":thinking" + if (id?.endsWith(":thinking")) { + id = id.slice(0, -":thinking".length) as GeminiModelId - if (id?.endsWith(thinkingSuffix)) { - id = id.slice(0, -thinkingSuffix.length) as GeminiModelId - info = geminiModels[id] + if (geminiModels[id]) { + info = geminiModels[id] - thinkingConfig = this.options.modelMaxThinkingTokens - ? { thinkingBudget: this.options.modelMaxThinkingTokens } - : undefined - - maxOutputTokens = this.options.modelMaxTokens ?? info.maxTokens ?? undefined + return { + id, + info, + thinkingConfig: this.options.modelMaxThinkingTokens + ? { thinkingBudget: this.options.modelMaxThinkingTokens } + : undefined, + maxOutputTokens: this.options.modelMaxTokens ?? info.maxTokens ?? undefined, + } + } } if (!info) { id = geminiDefaultModelId info = geminiModels[geminiDefaultModelId] - thinkingConfig = undefined - maxOutputTokens = undefined } - return { id, info, thinkingConfig, maxOutputTokens } + return { id, info } } async completePrompt(prompt: string): Promise { @@ -142,4 +188,57 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl return super.countTokens(content) } } + + public calculateCost({ + info, + inputTokens, + outputTokens, + cacheWriteTokens, + cacheReadTokens, + }: { + info: ModelInfo + inputTokens: number + outputTokens: number + cacheWriteTokens?: number + cacheReadTokens?: number + }) { + if (!info.inputPrice || !info.outputPrice || !info.cacheWritesPrice || !info.cacheReadsPrice) { + return undefined + } + + let inputPrice = info.inputPrice + let outputPrice = info.outputPrice + let cacheWritesPrice = info.cacheWritesPrice + let cacheReadsPrice = info.cacheReadsPrice + + // If there's tiered pricing then adjust the input and output token prices + // based on the input tokens used. + if (info.tiers) { + const tier = info.tiers.find((tier) => inputTokens <= tier.contextWindow) + + if (tier) { + inputPrice = tier.inputPrice ?? inputPrice + outputPrice = tier.outputPrice ?? outputPrice + cacheWritesPrice = tier.cacheWritesPrice ?? cacheWritesPrice + cacheReadsPrice = tier.cacheReadsPrice ?? cacheReadsPrice + } + } + + let inputTokensCost = inputPrice * (inputTokens / 1_000_000) + let outputTokensCost = outputPrice * (outputTokens / 1_000_000) + let cacheWriteCost = 0 + let cacheReadCost = 0 + + if (cacheWriteTokens) { + cacheWriteCost = cacheWritesPrice * (cacheWriteTokens / 1_000_000) * (CACHE_TTL / 60) + } + + if (cacheReadTokens) { + const uncachedReadTokens = inputTokens - cacheReadTokens + cacheReadCost = cacheReadsPrice * (cacheReadTokens / 1_000_000) + inputTokensCost = inputPrice * (uncachedReadTokens / 1_000_000) + } + + return inputTokensCost + outputTokensCost + cacheWriteCost + cacheReadCost + } } diff --git a/src/api/providers/vscode-lm.ts b/src/api/providers/vscode-lm.ts index d37f92d7f53..f3d21884b3a 100644 --- a/src/api/providers/vscode-lm.ts +++ b/src/api/providers/vscode-lm.ts @@ -442,7 +442,7 @@ export class VsCodeLmHandler extends BaseProvider implements SingleCompletionHan yield { type: "usage", inputTokens: totalInputTokens, - outputTokens: totalOutputTokens + outputTokens: totalOutputTokens, } } catch (error: unknown) { this.ensureCleanState() diff --git a/src/core/Cline.ts b/src/core/Cline.ts index e16de27fb54..229cf2fa642 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -136,32 +136,39 @@ export class Cline extends EventEmitter { readonly rootTask: Cline | undefined = undefined readonly parentTask: Cline | undefined = undefined readonly taskNumber: number + isPaused: boolean = false pausedModeSlug: string = defaultModeSlug private pauseInterval: NodeJS.Timeout | undefined readonly apiConfiguration: ApiConfiguration api: ApiHandler + private promptCacheKey: string + + rooIgnoreController?: RooIgnoreController private fileContextTracker: FileContextTracker private urlContentFetcher: UrlContentFetcher browserSession: BrowserSession didEditFile: boolean = false customInstructions?: string + diffStrategy?: DiffStrategy diffEnabled: boolean = false fuzzyMatchThreshold: number apiConversationHistory: (Anthropic.MessageParam & { ts?: number })[] = [] clineMessages: ClineMessage[] = [] - rooIgnoreController?: RooIgnoreController + private askResponse?: ClineAskResponse private askResponseText?: string private askResponseImages?: string[] private lastMessageTs?: number + // Not private since it needs to be accessible by tools. consecutiveMistakeCount: number = 0 consecutiveMistakeLimit: number consecutiveMistakeCountForApplyDiff: Map = new Map() + // Not private since it needs to be accessible by tools. providerRef: WeakRef private abort: boolean = false @@ -203,7 +210,6 @@ export class Cline extends EventEmitter { task, images, historyItem, - experiments, startTask = true, rootTask, parentTask, @@ -222,11 +228,15 @@ export class Cline extends EventEmitter { this.rooIgnoreController = new RooIgnoreController(this.cwd) this.fileContextTracker = new FileContextTracker(provider, this.taskId) + this.rooIgnoreController.initialize().catch((error) => { console.error("Failed to initialize RooIgnoreController:", error) }) + this.apiConfiguration = apiConfiguration this.api = buildApiHandler(apiConfiguration) + this.promptCacheKey = crypto.randomUUID() + this.urlContentFetcher = new UrlContentFetcher(provider.context) this.browserSession = new BrowserSession(provider.context) this.customInstructions = customInstructions @@ -355,6 +365,8 @@ export class Cline extends EventEmitter { } public async overwriteClineMessages(newMessages: ClineMessage[]) { + // Reset the the prompt cache key since we've altered the conversation history. + this.promptCacheKey = crypto.randomUUID() this.clineMessages = newMessages await this.saveClineMessages() } @@ -663,6 +675,7 @@ export class Cline extends EventEmitter { modifiedClineMessages, (m) => !(m.ask === "resume_task" || m.ask === "resume_completed_task"), ) + if (lastRelevantMessageIndex !== -1) { modifiedClineMessages.splice(lastRelevantMessageIndex + 1) } @@ -672,6 +685,7 @@ export class Cline extends EventEmitter { modifiedClineMessages, (m) => m.type === "say" && m.say === "api_req_started", ) + if (lastApiReqStartedIndex !== -1) { const lastApiReqStarted = modifiedClineMessages[lastApiReqStartedIndex] const { cost, cancelReason }: ClineApiReqInfo = JSON.parse(lastApiReqStarted.text || "{}") @@ -1106,7 +1120,7 @@ export class Cline extends EventEmitter { return { role, content } }) - const stream = this.api.createMessage(systemPrompt, cleanConversationHistory) + const stream = this.api.createMessage(systemPrompt, cleanConversationHistory, this.promptCacheKey) const iterator = stream[Symbol.asyncIterator]() try { diff --git a/src/exports/roo-code.d.ts b/src/exports/roo-code.d.ts index 5883cb86417..f92671bfd59 100644 --- a/src/exports/roo-code.d.ts +++ b/src/exports/roo-code.d.ts @@ -46,6 +46,15 @@ type ProviderSettings = { 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 glamaApiKey?: string | undefined @@ -69,6 +78,15 @@ type ProviderSettings = { 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 openRouterBaseUrl?: string | undefined @@ -112,6 +130,15 @@ type ProviderSettings = { 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 @@ -158,6 +185,15 @@ type ProviderSettings = { 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 requestyApiKey?: string | undefined @@ -180,6 +216,15 @@ type ProviderSettings = { 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 xaiApiKey?: string | undefined diff --git a/src/exports/types.ts b/src/exports/types.ts index 134f4fd0668..855e51815c1 100644 --- a/src/exports/types.ts +++ b/src/exports/types.ts @@ -47,6 +47,15 @@ type ProviderSettings = { 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 glamaApiKey?: string | undefined @@ -70,6 +79,15 @@ type ProviderSettings = { 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 openRouterBaseUrl?: string | undefined @@ -113,6 +131,15 @@ type ProviderSettings = { 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 @@ -159,6 +186,15 @@ type ProviderSettings = { 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 requestyApiKey?: string | undefined @@ -181,6 +217,15 @@ type ProviderSettings = { 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 xaiApiKey?: string | undefined diff --git a/src/schemas/index.ts b/src/schemas/index.ts index 8ebcdfc63a5..ef2a1508c87 100644 --- a/src/schemas/index.ts +++ b/src/schemas/index.ts @@ -114,6 +114,17 @@ export const modelInfoSchema = z.object({ minTokensPerCachePoint: z.number().optional(), maxCachePoints: z.number().optional(), cachableFields: z.array(z.string()).optional(), + tiers: z + .array( + z.object({ + contextWindow: z.number(), + inputPrice: z.number().optional(), + outputPrice: z.number().optional(), + cacheWritesPrice: z.number().optional(), + cacheReadsPrice: z.number().optional(), + }), + ) + .optional(), }) export type ModelInfo = z.infer diff --git a/src/shared/api.ts b/src/shared/api.ts index 34ec801187f..ebe088599c9 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -682,17 +682,35 @@ export const geminiModels = { maxTokens: 65_535, contextWindow: 1_048_576, supportsImages: true, - supportsPromptCache: false, - inputPrice: 2.5, + supportsPromptCache: true, + inputPrice: 2.5, // This is the pricing for prompts above 200k tokens. outputPrice: 15, + cacheReadsPrice: 0.625, + cacheWritesPrice: 4.5, + tiers: [ + { + contextWindow: 200_000, + inputPrice: 1.25, + outputPrice: 10, + cacheReadsPrice: 0.31, + }, + { + contextWindow: Infinity, + inputPrice: 2.5, + outputPrice: 15, + cacheReadsPrice: 0.625, + }, + ], }, "gemini-2.0-flash-001": { maxTokens: 8192, contextWindow: 1_048_576, supportsImages: true, - supportsPromptCache: false, - inputPrice: 0, - outputPrice: 0, + supportsPromptCache: true, + inputPrice: 0.1, + outputPrice: 0.4, + cacheReadsPrice: 0.025, + cacheWritesPrice: 1.0, }, "gemini-2.0-flash-lite-preview-02-05": { maxTokens: 8192, @@ -738,9 +756,25 @@ export const geminiModels = { maxTokens: 8192, contextWindow: 1_048_576, supportsImages: true, - supportsPromptCache: false, - inputPrice: 0, - outputPrice: 0, + supportsPromptCache: true, + inputPrice: 0.15, // This is the pricing for prompts above 128k tokens. + outputPrice: 0.6, + cacheReadsPrice: 0.0375, + cacheWritesPrice: 1.0, + tiers: [ + { + contextWindow: 128_000, + inputPrice: 0.075, + outputPrice: 0.3, + cacheReadsPrice: 0.01875, + }, + { + contextWindow: Infinity, + inputPrice: 0.15, + outputPrice: 0.6, + cacheReadsPrice: 0.0375, + }, + ], }, "gemini-1.5-flash-exp-0827": { maxTokens: 8192, diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 156b24c697e..afefe70d865 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -1693,6 +1693,7 @@ const ApiOptions = ({ )} { const { t } = useAppTranslation() - const isGemini = useMemo(() => Object.keys(geminiModels).includes(selectedModelId), [selectedModelId]) const infoItems = [ , - !isGemini && ( - - ), + , typeof modelInfo.maxTokens === "number" && modelInfo.maxTokens > 0 && ( <> {t("settings:modelInfo.maxOutput")}:{" "} @@ -73,7 +71,7 @@ export const ModelInfoView = ({ {formatPrice(modelInfo.cacheWritesPrice || 0)} / 1M tokens ), - isGemini && ( + apiProvider === "gemini" && ( {selectedModelId === "gemini-2.5-pro-preview-03-25" ? t("settings:modelInfo.gemini.billingEstimate") diff --git a/webview-ui/src/components/settings/ModelPicker.tsx b/webview-ui/src/components/settings/ModelPicker.tsx index 5aa3f1b3140..7995409e098 100644 --- a/webview-ui/src/components/settings/ModelPicker.tsx +++ b/webview-ui/src/components/settings/ModelPicker.tsx @@ -186,6 +186,7 @@ export const ModelPicker = ({
{selectedModelId && selectedModelInfo && ( Date: Wed, 23 Apr 2025 11:38:07 -0700 Subject: [PATCH 20/20] PR feedback --- src/core/Cline.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 229cf2fa642..1d35577d5aa 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -317,11 +317,9 @@ export class Cline extends EventEmitter { } private async addToApiConversationHistory(message: Anthropic.MessageParam) { - const ts = Date.now() - const messageWithTs = { ...message, ts } + const messageWithTs = { ...message, ts: Date.now() } this.apiConversationHistory.push(messageWithTs) await this.saveApiConversationHistory() - console.log(`addToApiConversationHistory: ${Date.now() - ts}ms`) } async overwriteApiConversationHistory(newHistory: Anthropic.MessageParam[]) { @@ -381,8 +379,6 @@ export class Cline extends EventEmitter { private readonly taskDirSizeCheckInterval = 30_000 private async saveClineMessages() { - const ts = Date.now() - try { const taskDir = await this.ensureTaskDirectoryExists() const filePath = path.join(taskDir, GlobalFileNames.uiMessages) @@ -402,9 +398,10 @@ export class Cline extends EventEmitter { ] if (Date.now() - this.taskDirSizeCheckedAt > this.taskDirSizeCheckInterval) { + this.taskDirSizeCheckedAt = Date.now() + try { this.taskDirSize = await getFolderSize.loose(taskDir) - this.taskDirSizeCheckedAt = Date.now() } catch (err) { console.error( `[saveClineMessages] failed to get task directory size (${taskDir}): ${err instanceof Error ? err.message : String(err)}`, @@ -428,8 +425,6 @@ export class Cline extends EventEmitter { } catch (error) { console.error("Failed to save cline messages:", error) } - - console.log(`saveClineMessages: ${Date.now() - ts}ms`) } // Communicate with webview