From 6ebc95d2a833fbe548ea8c6718ecbdbdb2dfa720 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Sun, 25 May 2025 06:10:46 -0700 Subject: [PATCH 001/104] Update `xaiModels` and `xaiDefaultModelId` in `src/shared/api.ts` (#3957) * Add non-beta versions of `grok-3` models to the `xaiModels` object * Change the default `xaiDefaultModelId` from `grok-3-beta` to `grok-3` --- .changeset/thick-streets-give.md | 5 ++++ src/shared/api.ts | 40 +++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 .changeset/thick-streets-give.md diff --git a/.changeset/thick-streets-give.md b/.changeset/thick-streets-give.md new file mode 100644 index 0000000000..293ad8583c --- /dev/null +++ b/.changeset/thick-streets-give.md @@ -0,0 +1,5 @@ +--- +"roo-cline": minor +--- + +Update `xaiModels` and `xaiDefaultModelId` in `src/shared/api.ts` diff --git a/src/shared/api.ts b/src/shared/api.ts index 25196c6f10..d66aca6721 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -1264,7 +1264,7 @@ export const litellmDefaultModelInfo: ModelInfo = { // xAI // https://docs.x.ai/docs/api-reference export type XAIModelId = keyof typeof xaiModels -export const xaiDefaultModelId: XAIModelId = "grok-3-beta" +export const xaiDefaultModelId: XAIModelId = "grok-3" export const xaiModels = { "grok-3-beta": { maxTokens: 8192, @@ -1304,6 +1304,44 @@ export const xaiModels = { description: "xAI's Grok-3 mini fast beta model with 131K context window", supportsReasoningEffort: true, }, + "grok-3": { + maxTokens: 8192, + contextWindow: 131072, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 3.0, + outputPrice: 15.0, + description: "xAI's Grok-3 model with 131K context window", + }, + "grok-3-fast": { + maxTokens: 8192, + contextWindow: 131072, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 5.0, + outputPrice: 25.0, + description: "xAI's Grok-3 fast model with 131K context window", + }, + "grok-3-mini": { + maxTokens: 8192, + contextWindow: 131072, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0.3, + outputPrice: 0.5, + description: "xAI's Grok-3 mini model with 131K context window", + supportsReasoningEffort: true, + }, + "grok-3-mini-fast": { + maxTokens: 8192, + contextWindow: 131072, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0.6, + outputPrice: 4.0, + description: "xAI's Grok-3 mini fast model with 131K context window", + supportsReasoningEffort: true, + }, "grok-2-latest": { maxTokens: 8192, contextWindow: 131072, From ad2ff932fa6eae204c1b0cb54e091c623d852f14 Mon Sep 17 00:00:00 2001 From: avtc Date: Sun, 25 May 2025 16:14:54 +0300 Subject: [PATCH 002/104] Fix handling BOM when user Rejects apply_diff (#3960) Related issue is #1483, related pull request is #1500 - but it looks like it missed the revert of proposed diff case --- src/integrations/editor/DiffViewProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/integrations/editor/DiffViewProvider.ts b/src/integrations/editor/DiffViewProvider.ts index dc812eab6d..adc19ff014 100644 --- a/src/integrations/editor/DiffViewProvider.ts +++ b/src/integrations/editor/DiffViewProvider.ts @@ -278,7 +278,7 @@ export class DiffViewProvider { updatedDocument.positionAt(updatedDocument.getText().length), ) - edit.replace(updatedDocument.uri, fullRange, this.originalContent ?? "") + edit.replace(updatedDocument.uri, fullRange, this.stripAllBOMs(this.originalContent ?? "")) // Apply the edit and save, since contents shouldnt have changed // this won't show in local history unless of course the user made From 503c7585ded88cfa8fd6411eae73c5cdad07c8ff Mon Sep 17 00:00:00 2001 From: Ruakij Date: Sun, 25 May 2025 15:59:58 +0200 Subject: [PATCH 003/104] Fix: Wrongfully clearing input on autoApprove (#3956) --- webview-ui/src/components/chat/ChatView.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index a466f2cd44..7180ee8655 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -1195,8 +1195,6 @@ const ChatViewComponent: React.ForwardRefRenderFunction Date: Sun, 25 May 2025 21:14:40 +0100 Subject: [PATCH 004/104] Add metadata to create message (#3832) --- .changeset/hot-lies-flash.md | 5 ++++ src/api/index.ts | 11 ++++++++- src/api/providers/anthropic-vertex.ts | 8 +++++-- src/api/providers/anthropic.ts | 8 +++++-- .../base-openai-compatible-provider.ts | 8 +++++-- src/api/providers/base-provider.ts | 8 +++++-- src/api/providers/bedrock.ts | 10 +++++--- src/api/providers/fake-ai.ts | 16 +++++++++---- src/api/providers/gemini.ts | 8 +++++-- src/api/providers/glama.ts | 8 +++++-- src/api/providers/human-relay.ts | 9 ++++++-- src/api/providers/litellm.ts | 8 +++++-- src/api/providers/lmstudio.ts | 8 +++++-- src/api/providers/mistral.ts | 10 ++++++-- src/api/providers/ollama.ts | 8 +++++-- src/api/providers/openai-native.ts | 8 +++++-- src/api/providers/openai.ts | 8 +++++-- src/api/providers/requesty.ts | 23 ++++++++++++++++--- src/api/providers/unbound.ts | 8 +++++-- src/api/providers/vscode-lm.ts | 9 ++++++-- src/api/providers/xai.ts | 8 +++++-- src/core/task/Task.ts | 10 ++++++-- 22 files changed, 162 insertions(+), 45 deletions(-) create mode 100644 .changeset/hot-lies-flash.md diff --git a/.changeset/hot-lies-flash.md b/.changeset/hot-lies-flash.md new file mode 100644 index 0000000000..dcd91e33e1 --- /dev/null +++ b/.changeset/hot-lies-flash.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Add metadata to create message diff --git a/src/api/index.ts b/src/api/index.ts index 3c5fec6d83..f831e58e8d 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -29,8 +29,17 @@ export interface SingleCompletionHandler { completePrompt(prompt: string): Promise } +export interface ApiHandlerCreateMessageMetadata { + mode?: string + taskId: string +} + export interface ApiHandler { - createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream + createMessage( + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream getModel(): { id: string; info: ModelInfo } diff --git a/src/api/providers/anthropic-vertex.ts b/src/api/providers/anthropic-vertex.ts index 4a4989bf09..a4ace61c6e 100644 --- a/src/api/providers/anthropic-vertex.ts +++ b/src/api/providers/anthropic-vertex.ts @@ -11,7 +11,7 @@ import { getModelParams } from "../transform/model-params" import { ANTHROPIC_DEFAULT_MAX_TOKENS } from "./constants" import { BaseProvider } from "./base-provider" -import type { SingleCompletionHandler } from "../index" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" // https://docs.anthropic.com/en/api/claude-on-vertex-ai export class AnthropicVertexHandler extends BaseProvider implements SingleCompletionHandler { @@ -50,7 +50,11 @@ export class AnthropicVertexHandler extends BaseProvider implements SingleComple } } - override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + override async *createMessage( + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream { let { id, info: { supportsPromptCache }, diff --git a/src/api/providers/anthropic.ts b/src/api/providers/anthropic.ts index 4f839994b8..9c84f388ef 100644 --- a/src/api/providers/anthropic.ts +++ b/src/api/providers/anthropic.ts @@ -15,7 +15,7 @@ import { getModelParams } from "../transform/model-params" import { ANTHROPIC_DEFAULT_MAX_TOKENS } from "./constants" import { BaseProvider } from "./base-provider" -import type { SingleCompletionHandler } from "../index" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" export class AnthropicHandler extends BaseProvider implements SingleCompletionHandler { private options: ApiHandlerOptions @@ -34,7 +34,11 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa }) } - async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + async *createMessage( + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream { let stream: AnthropicStream const cacheControl: CacheControlEphemeral = { type: "ephemeral" } let { id: modelId, betas = [], maxTokens, temperature, reasoning: thinking } = this.getModel() diff --git a/src/api/providers/base-openai-compatible-provider.ts b/src/api/providers/base-openai-compatible-provider.ts index 82eeb83033..ba9d67b6e3 100644 --- a/src/api/providers/base-openai-compatible-provider.ts +++ b/src/api/providers/base-openai-compatible-provider.ts @@ -5,7 +5,7 @@ import { ApiHandlerOptions, ModelInfo } from "../../shared/api" import { ApiStream } from "../transform/stream" import { convertToOpenAiMessages } from "../transform/openai-format" -import { SingleCompletionHandler } from "../index" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" import { DEFAULT_HEADERS } from "./constants" import { BaseProvider } from "./base-provider" @@ -60,7 +60,11 @@ export abstract class BaseOpenAiCompatibleProvider }) } - override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + override async *createMessage( + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream { const { id: model, info: { maxTokens: max_tokens }, diff --git a/src/api/providers/base-provider.ts b/src/api/providers/base-provider.ts index c03994b334..edb15a3f85 100644 --- a/src/api/providers/base-provider.ts +++ b/src/api/providers/base-provider.ts @@ -2,7 +2,7 @@ import { Anthropic } from "@anthropic-ai/sdk" import { ModelInfo } from "../../shared/api" -import { ApiHandler } from "../index" +import type { ApiHandler, ApiHandlerCreateMessageMetadata } from "../index" import { ApiStream } from "../transform/stream" import { countTokens } from "../../utils/countTokens" @@ -10,7 +10,11 @@ import { countTokens } from "../../utils/countTokens" * Base class for API providers that implements common functionality. */ export abstract class BaseProvider implements ApiHandler { - abstract createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream + abstract createMessage( + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream abstract getModel(): { id: string; info: ModelInfo } /** diff --git a/src/api/providers/bedrock.ts b/src/api/providers/bedrock.ts index ae5b421a5a..c378441484 100644 --- a/src/api/providers/bedrock.ts +++ b/src/api/providers/bedrock.ts @@ -9,7 +9,6 @@ import { } from "@aws-sdk/client-bedrock-runtime" import { fromIni } from "@aws-sdk/credential-providers" import { Anthropic } from "@anthropic-ai/sdk" -import { SingleCompletionHandler } from "../" import { BedrockModelId, ModelInfo as SharedModelInfo, @@ -26,6 +25,7 @@ import { MultiPointStrategy } from "../transform/cache-strategy/multi-point-stra import { ModelInfo as CacheModelInfo } from "../transform/cache-strategy/types" import { AMAZON_BEDROCK_REGION_INFO } from "../../shared/aws_regions" import { convertToBedrockConverseMessages as sharedConverter } from "../transform/bedrock-converse-format" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" const BEDROCK_DEFAULT_TEMPERATURE = 0.3 const BEDROCK_MAX_TOKENS = 4096 @@ -189,7 +189,11 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH this.client = new BedrockRuntimeClient(clientConfig) } - override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + override async *createMessage( + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream { let modelConfig = this.getModel() // Handle cross-region inference const usePromptCache = Boolean(this.options.awsUsePromptCache && this.supportsAwsPromptCache(modelConfig)) @@ -769,7 +773,7 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH > = { ACCESS_DENIED: { patterns: ["access", "denied", "permission"], - messageTemplate: `You don't have access to the model specified. + messageTemplate: `You don't have access to the model specified. Please verify: 1. Try cross-region inference if you're using a foundation model diff --git a/src/api/providers/fake-ai.ts b/src/api/providers/fake-ai.ts index 68d028338e..9c4f1ca709 100644 --- a/src/api/providers/fake-ai.ts +++ b/src/api/providers/fake-ai.ts @@ -1,7 +1,7 @@ import { Anthropic } from "@anthropic-ai/sdk" -import { ApiHandler, SingleCompletionHandler } from ".." import { ApiHandlerOptions, ModelInfo } from "../../shared/api" import { ApiStream } from "../transform/stream" +import type { ApiHandler, SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" interface FakeAI { /** @@ -18,7 +18,11 @@ interface FakeAI { */ removeFromCache?: () => void - createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream + createMessage( + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream getModel(): { id: string; info: ModelInfo } countTokens(content: Array): Promise completePrompt(prompt: string): Promise @@ -52,8 +56,12 @@ export class FakeAIHandler implements ApiHandler, SingleCompletionHandler { this.ai = cachedFakeAi } - async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { - yield* this.ai.createMessage(systemPrompt, messages) + async *createMessage( + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream { + yield* this.ai.createMessage(systemPrompt, messages, metadata) } getModel(): { id: string; info: ModelInfo } { diff --git a/src/api/providers/gemini.ts b/src/api/providers/gemini.ts index d519d5e629..31c802d2de 100644 --- a/src/api/providers/gemini.ts +++ b/src/api/providers/gemini.ts @@ -10,7 +10,7 @@ import type { JWTInput } from "google-auth-library" import { ApiHandlerOptions, ModelInfo, GeminiModelId, geminiDefaultModelId, geminiModels } from "../../shared/api" import { safeJsonParse } from "../../shared/safeJsonParse" -import { SingleCompletionHandler } from "../index" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" import { convertAnthropicContentToGemini, convertAnthropicMessageToGemini } from "../transform/gemini-format" import type { ApiStream } from "../transform/stream" import { BaseProvider } from "./base-provider" @@ -54,7 +54,11 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl : new GoogleGenAI({ apiKey }) } - async *createMessage(systemInstruction: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + async *createMessage( + systemInstruction: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream { const { id: model, thinkingConfig, maxOutputTokens, info } = this.getModel() const contents = messages.map(convertAnthropicMessageToGemini) diff --git a/src/api/providers/glama.ts b/src/api/providers/glama.ts index 6010c85d41..e743f82399 100644 --- a/src/api/providers/glama.ts +++ b/src/api/providers/glama.ts @@ -9,7 +9,7 @@ import { ApiStream } from "../transform/stream" import { convertToOpenAiMessages } from "../transform/openai-format" import { addCacheBreakpoints } from "../transform/caching/anthropic" -import { SingleCompletionHandler } from "../index" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" import { RouterProvider } from "./router-provider" const GLAMA_DEFAULT_TEMPERATURE = 0 @@ -33,7 +33,11 @@ export class GlamaHandler extends RouterProvider implements SingleCompletionHand }) } - override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + override async *createMessage( + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream { const { id: modelId, info } = await this.fetchModel() const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [ diff --git a/src/api/providers/human-relay.ts b/src/api/providers/human-relay.ts index 7a7f3c10a1..4abdf7b0c0 100644 --- a/src/api/providers/human-relay.ts +++ b/src/api/providers/human-relay.ts @@ -4,7 +4,7 @@ import * as vscode from "vscode" import { ModelInfo } from "../../shared/api" import { getCommand } from "../../utils/commands" import { ApiStream } from "../transform/stream" -import { ApiHandler, SingleCompletionHandler } from "../index" +import type { ApiHandler, SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" /** * Human Relay API processor * This processor does not directly call the API, but interacts with the model through human operations copy and paste. @@ -18,8 +18,13 @@ export class HumanRelayHandler implements ApiHandler, SingleCompletionHandler { * Create a message processing flow, display a dialog box to request human assistance * @param systemPrompt System prompt words * @param messages Message list + * @param metadata Optional metadata */ - async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + async *createMessage( + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream { // Get the most recent user message const latestMessage = messages[messages.length - 1] diff --git a/src/api/providers/litellm.ts b/src/api/providers/litellm.ts index be88ede5f6..fc29f2c5f8 100644 --- a/src/api/providers/litellm.ts +++ b/src/api/providers/litellm.ts @@ -4,7 +4,7 @@ import { Anthropic } from "@anthropic-ai/sdk" // Keep for type usage only import { ApiHandlerOptions, litellmDefaultModelId, litellmDefaultModelInfo } from "../../shared/api" import { ApiStream, ApiStreamUsageChunk } from "../transform/stream" import { convertToOpenAiMessages } from "../transform/openai-format" -import { SingleCompletionHandler } from "../index" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" import { RouterProvider } from "./router-provider" /** @@ -26,7 +26,11 @@ export class LiteLLMHandler extends RouterProvider implements SingleCompletionHa }) } - override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + override async *createMessage( + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream { const { id: modelId, info } = await this.fetchModel() const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [ diff --git a/src/api/providers/lmstudio.ts b/src/api/providers/lmstudio.ts index c750c32a26..e1aee5e53e 100644 --- a/src/api/providers/lmstudio.ts +++ b/src/api/providers/lmstudio.ts @@ -2,12 +2,12 @@ import { Anthropic } from "@anthropic-ai/sdk" import OpenAI from "openai" import axios from "axios" -import { SingleCompletionHandler } from "../" import { ApiHandlerOptions, ModelInfo, openAiModelInfoSaneDefaults } from "../../shared/api" import { convertToOpenAiMessages } from "../transform/openai-format" import { ApiStream } from "../transform/stream" import { BaseProvider } from "./base-provider" import { XmlMatcher } from "../../utils/xml-matcher" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" const LMSTUDIO_DEFAULT_TEMPERATURE = 0 @@ -24,7 +24,11 @@ export class LmStudioHandler extends BaseProvider implements SingleCompletionHan }) } - override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + override async *createMessage( + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream { const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [ { role: "system", content: systemPrompt }, ...convertToOpenAiMessages(messages), diff --git a/src/api/providers/mistral.ts b/src/api/providers/mistral.ts index 4daaa2ab85..58cd7c7952 100644 --- a/src/api/providers/mistral.ts +++ b/src/api/providers/mistral.ts @@ -1,10 +1,10 @@ import { Anthropic } from "@anthropic-ai/sdk" import { Mistral } from "@mistralai/mistralai" -import { SingleCompletionHandler } from "../" import { ApiHandlerOptions, mistralDefaultModelId, MistralModelId, mistralModels, ModelInfo } from "../../shared/api" import { convertToMistralMessages } from "../transform/mistral-format" import { ApiStream } from "../transform/stream" import { BaseProvider } from "./base-provider" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" const MISTRAL_DEFAULT_TEMPERATURE = 0 @@ -41,7 +41,13 @@ export class MistralHandler extends BaseProvider implements SingleCompletionHand return "https://api.mistral.ai" } - override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + override async *createMessage( + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream { + const { id: model } = this.getModel() + const response = await this.client.chat.stream({ model: this.options.apiModelId || mistralDefaultModelId, messages: [{ role: "system", content: systemPrompt }, ...convertToMistralMessages(messages)], diff --git a/src/api/providers/ollama.ts b/src/api/providers/ollama.ts index 1b721a5909..ba2495c095 100644 --- a/src/api/providers/ollama.ts +++ b/src/api/providers/ollama.ts @@ -2,7 +2,7 @@ import { Anthropic } from "@anthropic-ai/sdk" import OpenAI from "openai" import axios from "axios" -import { SingleCompletionHandler } from "../" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" import { ApiHandlerOptions, ModelInfo, openAiModelInfoSaneDefaults } from "../../shared/api" import { convertToOpenAiMessages } from "../transform/openai-format" import { convertToR1Format } from "../transform/r1-format" @@ -27,7 +27,11 @@ export class OllamaHandler extends BaseProvider implements SingleCompletionHandl }) } - override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + override async *createMessage( + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream { const modelId = this.getModel().id const useR1Format = modelId.toLowerCase().includes("deepseek-r1") const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [ diff --git a/src/api/providers/openai-native.ts b/src/api/providers/openai-native.ts index 1999637228..8ce7eaa5ef 100644 --- a/src/api/providers/openai-native.ts +++ b/src/api/providers/openai-native.ts @@ -15,7 +15,7 @@ import { convertToOpenAiMessages } from "../transform/openai-format" import { ApiStream } from "../transform/stream" import { getModelParams } from "../transform/model-params" -import type { SingleCompletionHandler } from "../index" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" import { BaseProvider } from "./base-provider" const OPENAI_NATIVE_DEFAULT_TEMPERATURE = 0 @@ -33,7 +33,11 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio this.client = new OpenAI({ baseURL: this.options.openAiNativeBaseUrl, apiKey }) } - override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + override async *createMessage( + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream { const model = this.getModel() let id: "o3-mini" | "o3" | "o4-mini" | undefined diff --git a/src/api/providers/openai.ts b/src/api/providers/openai.ts index 73f5f0b882..69d0040d0d 100644 --- a/src/api/providers/openai.ts +++ b/src/api/providers/openai.ts @@ -18,7 +18,7 @@ import { ApiStream, ApiStreamUsageChunk } from "../transform/stream" import { getModelParams } from "../transform/model-params" import { DEFAULT_HEADERS, DEEP_SEEK_DEFAULT_TEMPERATURE } from "./constants" -import type { SingleCompletionHandler } from "../index" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" import { BaseProvider } from "./base-provider" export const AZURE_AI_INFERENCE_PATH = "/models/chat/completions" @@ -71,7 +71,11 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl } } - override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + override async *createMessage( + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream { const { info: modelInfo, reasoning } = this.getModel() const modelUrl = this.options.openAiBaseUrl ?? "" const modelId = this.options.openAiModelId ?? "" diff --git a/src/api/providers/requesty.ts b/src/api/providers/requesty.ts index c2e0a12bdd..1c21af5241 100644 --- a/src/api/providers/requesty.ts +++ b/src/api/providers/requesty.ts @@ -9,7 +9,7 @@ import { import { convertToOpenAiMessages } from "../transform/openai-format" import { calculateApiCostOpenAI } from "../../utils/cost" import { ApiStream, ApiStreamUsageChunk } from "../transform/stream" -import { SingleCompletionHandler } from "../" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../" import { BaseProvider } from "./base-provider" import { DEFAULT_HEADERS } from "./constants" import { getModels } from "./fetchers/modelCache" @@ -25,7 +25,14 @@ interface RequestyUsage extends OpenAI.CompletionUsage { total_cost?: number } -type RequestyChatCompletionParams = OpenAI.Chat.ChatCompletionCreateParams & {} +type RequestyChatCompletionParams = OpenAI.Chat.ChatCompletionCreateParams & { + requesty?: { + trace_id?: string + extra?: { + mode?: string + } + } +} export class RequestyHandler extends BaseProvider implements SingleCompletionHandler { protected options: ApiHandlerOptions @@ -75,7 +82,11 @@ export class RequestyHandler extends BaseProvider implements SingleCompletionHan } } - override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + override async *createMessage( + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream { const model = await this.fetchModel() let openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [ @@ -97,6 +108,12 @@ export class RequestyHandler extends BaseProvider implements SingleCompletionHan temperature: temperature, stream: true, stream_options: { include_usage: true }, + requesty: { + trace_id: metadata?.taskId, + extra: { + mode: metadata?.mode, + }, + }, } const stream = await this.client.chat.completions.create(completionParams) diff --git a/src/api/providers/unbound.ts b/src/api/providers/unbound.ts index 5e8dbf66b4..5ca38a8514 100644 --- a/src/api/providers/unbound.ts +++ b/src/api/providers/unbound.ts @@ -7,7 +7,7 @@ import { ApiStream, ApiStreamUsageChunk } from "../transform/stream" import { convertToOpenAiMessages } from "../transform/openai-format" import { addCacheBreakpoints } from "../transform/caching/anthropic" -import { SingleCompletionHandler } from "../index" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" import { RouterProvider } from "./router-provider" const DEFAULT_HEADERS = { @@ -32,7 +32,11 @@ export class UnboundHandler extends RouterProvider implements SingleCompletionHa }) } - override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + override async *createMessage( + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream { const { id: modelId, info } = await this.fetchModel() const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [ diff --git a/src/api/providers/vscode-lm.ts b/src/api/providers/vscode-lm.ts index c06510c26c..61aab91123 100644 --- a/src/api/providers/vscode-lm.ts +++ b/src/api/providers/vscode-lm.ts @@ -1,12 +1,12 @@ import { Anthropic } from "@anthropic-ai/sdk" import * as vscode from "vscode" -import { SingleCompletionHandler } from "../" import { ApiStream } from "../transform/stream" import { convertToVsCodeLmMessages } from "../transform/vscode-lm-format" import { SELECTOR_SEPARATOR, stringifyVsCodeLmModelSelector } from "../../shared/vsCodeSelectorUtils" import { ApiHandlerOptions, ModelInfo, openAiModelInfoSaneDefaults } from "../../shared/api" import { BaseProvider } from "./base-provider" +import type { SingleCompletionHandler, ApiHandler, ApiHandlerCreateMessageMetadata } from "../index" /** * Handles interaction with VS Code's Language Model API for chat-based operations. @@ -148,6 +148,7 @@ export class VsCodeLmHandler extends BaseProvider implements SingleCompletionHan * * @param systemPrompt - The system prompt to initialize the conversation context * @param messages - An array of message parameters following the Anthropic message format + * @param metadata - Optional metadata for the message * * @yields {ApiStream} An async generator that yields either text chunks or tool calls from the model response * @@ -329,7 +330,11 @@ export class VsCodeLmHandler extends BaseProvider implements SingleCompletionHan return content } - override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + override async *createMessage( + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream { // Ensure clean state before starting a new request this.ensureCleanState() const client: vscode.LanguageModelChat = await this.getClient() diff --git a/src/api/providers/xai.ts b/src/api/providers/xai.ts index 58654f6732..280b6800f0 100644 --- a/src/api/providers/xai.ts +++ b/src/api/providers/xai.ts @@ -9,7 +9,7 @@ import { getModelParams } from "../transform/model-params" import { DEFAULT_HEADERS } from "./constants" import { BaseProvider } from "./base-provider" -import { type SingleCompletionHandler } from "../index" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" const XAI_DEFAULT_TEMPERATURE = 0 @@ -38,7 +38,11 @@ export class XAIHandler extends BaseProvider implements SingleCompletionHandler return { id, info, ...params } } - override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { + override async *createMessage( + systemPrompt: string, + messages: Anthropic.Messages.MessageParam[], + metadata?: ApiHandlerCreateMessageMetadata, + ): ApiStream { const { id: modelId, info: modelInfo, reasoning } = this.getModel() // Use the OpenAI-compatible API. diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 4c307a3ed0..231a6049ad 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -12,7 +12,7 @@ import { serializeError } from "serialize-error" import { TokenUsage, ToolUsage, ToolName, ContextCondense } from "../../schemas" // api -import { ApiHandler, buildApiHandler } from "../../api" +import { ApiHandler, ApiHandlerCreateMessageMetadata, buildApiHandler } from "../../api" import { ApiStream } from "../../api/transform/stream" // shared @@ -1499,6 +1499,7 @@ export class Task extends EventEmitter { alwaysApproveResubmit, requestDelaySeconds, experiments, + mode, autoCondenseContextPercent = 100, } = state ?? {} @@ -1614,7 +1615,12 @@ export class Task extends EventEmitter { } } - const stream = this.api.createMessage(systemPrompt, cleanConversationHistory) + const metadata: ApiHandlerCreateMessageMetadata = { + mode: mode, + taskId: this.taskId, + } + + const stream = this.api.createMessage(systemPrompt, cleanConversationHistory, metadata) const iterator = stream[Symbol.asyncIterator]() try { From 977fa26e0133e99188a84d26b60908ff49bb1f81 Mon Sep 17 00:00:00 2001 From: Daniel <57051444+daniel-lxs@users.noreply.github.com> Date: Sun, 25 May 2025 16:11:01 -0500 Subject: [PATCH 005/104] Batch code segments when using ollama to report progress (#3968) fix: batch code segments when using ollama --- src/services/code-index/processors/scanner.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/services/code-index/processors/scanner.ts b/src/services/code-index/processors/scanner.ts index 1233b0e0d5..f0dafb60c3 100644 --- a/src/services/code-index/processors/scanner.ts +++ b/src/services/code-index/processors/scanner.ts @@ -145,11 +145,8 @@ export class DirectoryScanner implements IDirectoryScanner { }) } - // Check if batch threshold is met and not for Ollama - if ( - currentBatchBlocks.length >= BATCH_SEGMENT_THRESHOLD && - this.embedder.embedderInfo.name !== "ollama" - ) { + // Check if batch threshold is met + if (currentBatchBlocks.length >= BATCH_SEGMENT_THRESHOLD) { // Copy current batch data and clear accumulators const batchBlocks = [...currentBatchBlocks] const batchTexts = [...currentBatchTexts] From 6a8fb599c12517d60376e19d127f88820f719136 Mon Sep 17 00:00:00 2001 From: Daniel <57051444+daniel-lxs@users.noreply.github.com> Date: Sun, 25 May 2025 17:25:20 -0500 Subject: [PATCH 006/104] Fix settings saving logic to ensure the saved settings are used (#3976) * feat: Enhance configuration change handling and model dimension checks * fix: Update embedder creation to use modelId from config * test: Add unit tests for ServiceFactory embedder and vector store creation * test: Add comprehensive restart detection tests for configuration changes * feat: Implement model ID selection logic for provider changes in CodeIndexSettings * fix: Initialize configuration on constructor to prevent false restart triggers * fix: Enhance API key handling and restart logic in CodeIndexConfigManager * fix: Improve handling of external settings changes and automatic indexing in webviewMessageHandler * fix: Ensure handleExternalSettingsChange only restarts service when manager is initialized * refactor: remove console logs * fix: Load configuration during initialization to ensure correct state and restart requirements --- src/core/webview/webviewMessageHandler.ts | 13 +- .../__tests__/config-manager.test.ts | 394 +++++++++++++++++- .../code-index/__tests__/manager.test.ts | 117 ++++++ .../__tests__/service-factory.test.ts | 287 +++++++++++++ src/services/code-index/config-manager.ts | 180 +++++--- src/services/code-index/manager.ts | 17 +- src/services/code-index/service-factory.ts | 17 +- .../components/settings/CodeIndexSettings.tsx | 38 +- 8 files changed, 990 insertions(+), 73 deletions(-) create mode 100644 src/services/code-index/__tests__/manager.test.ts create mode 100644 src/services/code-index/__tests__/service-factory.test.ts diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 09c472cc0d..c8fd3608e4 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -1331,7 +1331,18 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We await updateGlobalState("codebaseIndexConfig", codebaseIndexConfig) try { - await provider.codeIndexManager?.initialize(provider.contextProxy) + if (provider.codeIndexManager) { + await provider.codeIndexManager.handleExternalSettingsChange() + + // If now configured and enabled, start indexing automatically + if (provider.codeIndexManager.isFeatureEnabled && provider.codeIndexManager.isFeatureConfigured) { + if (!provider.codeIndexManager.isInitialized) { + await provider.codeIndexManager.initialize(provider.contextProxy) + } + // Start indexing in background (no await) + provider.codeIndexManager.startIndexing() + } + } } catch (error) { provider.log( `[CodeIndexManager] Error during background CodeIndexManager configuration/indexing: ${error.message || error}`, diff --git a/src/services/code-index/__tests__/config-manager.test.ts b/src/services/code-index/__tests__/config-manager.test.ts index 7b27139943..b0aa024248 100644 --- a/src/services/code-index/__tests__/config-manager.test.ts +++ b/src/services/code-index/__tests__/config-manager.test.ts @@ -75,13 +75,17 @@ describe("CodeIndexConfigManager", () => { }) it("should detect restart requirement when provider changes", async () => { - // Initial state + // Initial state - properly configured mockContextProxy.getGlobalState.mockReturnValue({ codebaseIndexEnabled: true, codebaseIndexQdrantUrl: "http://qdrant.local", codebaseIndexEmbedderProvider: "openai", codebaseIndexEmbedderModelId: "text-embedding-3-large", }) + mockContextProxy.getSecret.mockImplementation((key: string) => { + if (key === "codeIndexOpenAiKey") return "test-openai-key" + return undefined + }) await configManager.loadConfiguration() @@ -91,12 +95,329 @@ describe("CodeIndexConfigManager", () => { codebaseIndexQdrantUrl: "http://qdrant.local", codebaseIndexEmbedderProvider: "ollama", codebaseIndexEmbedderBaseUrl: "http://ollama.local", - codebaseIndexEmbedderModelId: "llama2", + codebaseIndexEmbedderModelId: "nomic-embed-text", }) const result = await configManager.loadConfiguration() expect(result.requiresRestart).toBe(true) }) + + it("should detect restart requirement when vector dimensions change", async () => { + // Initial state with text-embedding-3-small (1536D) + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexQdrantUrl: "http://qdrant.local", + codebaseIndexEmbedderProvider: "openai", + codebaseIndexEmbedderModelId: "text-embedding-3-small", + }) + mockContextProxy.getSecret.mockReturnValue("test-key") + + await configManager.loadConfiguration() + + // Change to text-embedding-3-large (3072D) + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexQdrantUrl: "http://qdrant.local", + codebaseIndexEmbedderProvider: "openai", + codebaseIndexEmbedderModelId: "text-embedding-3-large", + }) + + const result = await configManager.loadConfiguration() + expect(result.requiresRestart).toBe(true) + }) + + it("should NOT require restart when models have same dimensions", async () => { + // Initial state with text-embedding-3-small (1536D) + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexQdrantUrl: "http://qdrant.local", + codebaseIndexEmbedderProvider: "openai", + codebaseIndexEmbedderModelId: "text-embedding-3-small", + }) + mockContextProxy.getSecret.mockImplementation((key: string) => { + if (key === "codeIndexOpenAiKey") return "test-key" + return undefined + }) + + await configManager.loadConfiguration() + + // Change to text-embedding-ada-002 (also 1536D) + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexQdrantUrl: "http://qdrant.local", + codebaseIndexEmbedderProvider: "openai", + codebaseIndexEmbedderModelId: "text-embedding-ada-002", + }) + + const result = await configManager.loadConfiguration() + expect(result.requiresRestart).toBe(false) + }) + + it("should detect restart requirement when transitioning to enabled+configured", async () => { + // Initial state - disabled + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: false, + }) + + await configManager.loadConfiguration() + + // Enable and configure + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexQdrantUrl: "http://qdrant.local", + codebaseIndexEmbedderProvider: "openai", + codebaseIndexEmbedderModelId: "text-embedding-3-small", + }) + mockContextProxy.getSecret.mockReturnValue("test-key") + + const result = await configManager.loadConfiguration() + expect(result.requiresRestart).toBe(true) + }) + + describe("simplified restart detection", () => { + it("should detect restart requirement for API key changes", async () => { + // Initial state + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexQdrantUrl: "http://qdrant.local", + codebaseIndexEmbedderProvider: "openai", + codebaseIndexEmbedderModelId: "text-embedding-3-small", + }) + mockContextProxy.getSecret.mockReturnValue("old-key") + + await configManager.loadConfiguration() + + // Change API key + mockContextProxy.getSecret.mockImplementation((key: string) => { + if (key === "codeIndexOpenAiKey") return "new-key" + return undefined + }) + + const result = await configManager.loadConfiguration() + expect(result.requiresRestart).toBe(true) + }) + + it("should detect restart requirement for Qdrant URL changes", async () => { + // Initial state + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexQdrantUrl: "http://old-qdrant.local", + codebaseIndexEmbedderProvider: "openai", + codebaseIndexEmbedderModelId: "text-embedding-3-small", + }) + mockContextProxy.getSecret.mockReturnValue("test-key") + + await configManager.loadConfiguration() + + // Change Qdrant URL + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexQdrantUrl: "http://new-qdrant.local", + codebaseIndexEmbedderProvider: "openai", + codebaseIndexEmbedderModelId: "text-embedding-3-small", + }) + + const result = await configManager.loadConfiguration() + expect(result.requiresRestart).toBe(true) + }) + + it("should handle unknown model dimensions safely", async () => { + // Initial state with known model + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexQdrantUrl: "http://qdrant.local", + codebaseIndexEmbedderProvider: "openai", + codebaseIndexEmbedderModelId: "text-embedding-3-small", + }) + mockContextProxy.getSecret.mockReturnValue("test-key") + + await configManager.loadConfiguration() + + // Change to unknown model + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexQdrantUrl: "http://qdrant.local", + codebaseIndexEmbedderProvider: "openai", + codebaseIndexEmbedderModelId: "unknown-model", + }) + + const result = await configManager.loadConfiguration() + expect(result.requiresRestart).toBe(true) + }) + + it("should handle Ollama configuration changes", async () => { + // Initial state + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexQdrantUrl: "http://qdrant.local", + codebaseIndexEmbedderProvider: "ollama", + codebaseIndexEmbedderBaseUrl: "http://old-ollama.local", + codebaseIndexEmbedderModelId: "nomic-embed-text", + }) + + await configManager.loadConfiguration() + + // Change Ollama base URL + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexQdrantUrl: "http://qdrant.local", + codebaseIndexEmbedderProvider: "ollama", + codebaseIndexEmbedderBaseUrl: "http://new-ollama.local", + codebaseIndexEmbedderModelId: "nomic-embed-text", + }) + + const result = await configManager.loadConfiguration() + expect(result.requiresRestart).toBe(true) + }) + + it("should not require restart when disabled remains disabled", async () => { + // Initial state - disabled but configured + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: false, + codebaseIndexQdrantUrl: "http://qdrant.local", + codebaseIndexEmbedderProvider: "openai", + }) + mockContextProxy.getSecret.mockImplementation((key: string) => { + if (key === "codeIndexOpenAiKey") return "test-key" + return undefined + }) + + await configManager.loadConfiguration() + + // Still disabled but change other settings + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: false, + codebaseIndexQdrantUrl: "http://different-qdrant.local", + codebaseIndexEmbedderProvider: "ollama", + codebaseIndexEmbedderBaseUrl: "http://ollama.local", + }) + + const result = await configManager.loadConfiguration() + expect(result.requiresRestart).toBe(false) + }) + + it("should not require restart when unconfigured remains unconfigured", async () => { + // Initial state - enabled but unconfigured (missing API key) + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexQdrantUrl: "http://qdrant.local", + codebaseIndexEmbedderProvider: "openai", + }) + mockContextProxy.getSecret.mockReturnValue(undefined) + + await configManager.loadConfiguration() + + // Still unconfigured but change model + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexQdrantUrl: "http://qdrant.local", + codebaseIndexEmbedderProvider: "openai", + codebaseIndexEmbedderModelId: "text-embedding-3-large", + }) + + const result = await configManager.loadConfiguration() + expect(result.requiresRestart).toBe(false) + }) + }) + + describe("empty/missing API key handling", () => { + it("should not require restart when API keys are consistently empty", async () => { + // Initial state with no API keys (undefined from secrets) + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexQdrantUrl: "http://qdrant.local", + codebaseIndexEmbedderProvider: "openai", + codebaseIndexEmbedderModelId: "text-embedding-3-small", + }) + mockContextProxy.getSecret.mockReturnValue(undefined) + + await configManager.loadConfiguration() + + // Change an unrelated setting while keeping API keys empty + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexQdrantUrl: "http://qdrant.local", + codebaseIndexEmbedderProvider: "openai", + codebaseIndexEmbedderModelId: "text-embedding-3-small", + codebaseIndexSearchMinScore: 0.5, // Changed unrelated setting + }) + + const result = await configManager.loadConfiguration() + // Should NOT require restart since API keys are consistently empty + expect(result.requiresRestart).toBe(false) + }) + + it("should not require restart when API keys transition from undefined to empty string", async () => { + // Initial state with undefined API keys + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: false, // Start disabled to avoid restart due to enable+configure + codebaseIndexQdrantUrl: "http://qdrant.local", + codebaseIndexEmbedderProvider: "openai", + }) + mockContextProxy.getSecret.mockReturnValue(undefined) + + await configManager.loadConfiguration() + + // Change to empty string API keys (simulating what happens when secrets return "") + mockContextProxy.getSecret.mockReturnValue("") + + const result = await configManager.loadConfiguration() + // Should NOT require restart since undefined and "" are both "empty" + expect(result.requiresRestart).toBe(false) + }) + + it("should require restart when API key actually changes from empty to non-empty", async () => { + // Initial state with empty API key + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexQdrantUrl: "http://qdrant.local", + codebaseIndexEmbedderProvider: "openai", + }) + mockContextProxy.getSecret.mockReturnValue("") + + await configManager.loadConfiguration() + + // Add actual API key + mockContextProxy.getSecret.mockImplementation((key: string) => { + if (key === "codeIndexOpenAiKey") return "actual-api-key" + return "" + }) + + const result = await configManager.loadConfiguration() + // Should require restart since we went from empty to actual key + expect(result.requiresRestart).toBe(true) + }) + }) + + describe("getRestartInfo public method", () => { + it("should provide restart info without loading configuration", async () => { + // Setup initial state + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexQdrantUrl: "http://qdrant.local", + codebaseIndexEmbedderProvider: "openai", + codebaseIndexEmbedderModelId: "text-embedding-3-small", + }) + mockContextProxy.getSecret.mockReturnValue("test-key") + + await configManager.loadConfiguration() + + // Create a mock previous config + const mockPrevConfig = { + enabled: true, + configured: true, + embedderProvider: "openai" as const, + modelId: "text-embedding-3-large", // Different model with different dimensions + openAiKey: "test-key", + ollamaBaseUrl: undefined, + qdrantUrl: "http://qdrant.local", + qdrantApiKey: undefined, + } + + const requiresRestart = configManager.doesConfigChangeRequireRestart(mockPrevConfig) + expect(requiresRestart).toBe(true) + }) + }) }) describe("isConfigured", () => { @@ -189,4 +510,73 @@ describe("CodeIndexConfigManager", () => { expect(configManager.currentModelId).toBe("text-embedding-3-large") }) }) + + describe("initialization and restart prevention", () => { + it("should not require restart when configuration hasn't changed between calls", async () => { + // Setup initial configuration - start with enabled and configured to avoid initial transition restart + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexQdrantUrl: "http://qdrant.local", + codebaseIndexEmbedderProvider: "openai", + codebaseIndexEmbedderModelId: "text-embedding-3-small", + }) + mockContextProxy.getSecret.mockImplementation((key: string) => { + if (key === "codeIndexOpenAiKey") return "test-key" + return undefined + }) + + // First load - this will initialize the config manager with current state + await configManager.loadConfiguration() + + // Second load with same configuration - should not require restart + const secondResult = await configManager.loadConfiguration() + expect(secondResult.requiresRestart).toBe(false) + }) + + it("should properly initialize with current config to prevent false restarts", async () => { + // Setup configuration + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: false, // Start disabled to avoid transition restart + codebaseIndexQdrantUrl: "http://qdrant.local", + codebaseIndexEmbedderProvider: "openai", + codebaseIndexEmbedderModelId: "text-embedding-3-small", + }) + mockContextProxy.getSecret.mockImplementation((key: string) => { + if (key === "codeIndexOpenAiKey") return "test-key" + return undefined + }) + + // Create a new config manager (simulating what happens in CodeIndexManager.initialize) + const newConfigManager = new CodeIndexConfigManager(mockContextProxy) + + // Load configuration - should not require restart since the manager should be initialized with current config + const result = await newConfigManager.loadConfiguration() + expect(result.requiresRestart).toBe(false) + }) + + it("should not require restart when settings are saved but code indexing config unchanged", async () => { + // This test simulates the original issue: handleExternalSettingsChange() being called + // when other settings are saved, but code indexing settings haven't changed + + // Setup initial state - enabled and configured + mockContextProxy.getGlobalState.mockReturnValue({ + codebaseIndexEnabled: true, + codebaseIndexQdrantUrl: "http://qdrant.local", + codebaseIndexEmbedderProvider: "openai", + codebaseIndexEmbedderModelId: "text-embedding-3-small", + }) + mockContextProxy.getSecret.mockImplementation((key: string) => { + if (key === "codeIndexOpenAiKey") return "test-key" + return undefined + }) + + // First load to establish baseline + await configManager.loadConfiguration() + + // Simulate external settings change where code indexing config hasn't changed + // (this is what happens when other settings are saved) + const result = await configManager.loadConfiguration() + expect(result.requiresRestart).toBe(false) + }) + }) }) diff --git a/src/services/code-index/__tests__/manager.test.ts b/src/services/code-index/__tests__/manager.test.ts new file mode 100644 index 0000000000..012a9450ee --- /dev/null +++ b/src/services/code-index/__tests__/manager.test.ts @@ -0,0 +1,117 @@ +import * as vscode from "vscode" +import { CodeIndexManager } from "../manager" +import { ContextProxy } from "../../../core/config/ContextProxy" + +// Mock only the essential dependencies +jest.mock("../../../utils/path", () => ({ + getWorkspacePath: jest.fn(() => "/test/workspace"), +})) + +jest.mock("../state-manager", () => ({ + CodeIndexStateManager: jest.fn().mockImplementation(() => ({ + onProgressUpdate: jest.fn(), + getCurrentStatus: jest.fn(), + dispose: jest.fn(), + })), +})) + +describe("CodeIndexManager - handleExternalSettingsChange regression", () => { + let mockContext: jest.Mocked + let manager: CodeIndexManager + + beforeEach(() => { + // Clear all instances before each test + CodeIndexManager.disposeAll() + + mockContext = { + subscriptions: [], + workspaceState: {} as any, + globalState: {} as any, + extensionUri: {} as any, + extensionPath: "/test/extension", + asAbsolutePath: jest.fn(), + storageUri: {} as any, + storagePath: "/test/storage", + globalStorageUri: {} as any, + globalStoragePath: "/test/global-storage", + logUri: {} as any, + logPath: "/test/log", + extensionMode: vscode.ExtensionMode.Test, + secrets: {} as any, + environmentVariableCollection: {} as any, + extension: {} as any, + languageModelAccessInformation: {} as any, + } + + manager = CodeIndexManager.getInstance(mockContext)! + }) + + afterEach(() => { + CodeIndexManager.disposeAll() + }) + + describe("handleExternalSettingsChange", () => { + it("should not throw when called on uninitialized manager (regression test)", async () => { + // This is the core regression test: handleExternalSettingsChange() should not throw + // when called before the manager is initialized (during first-time configuration) + + // Ensure manager is not initialized + expect(manager.isInitialized).toBe(false) + + // Mock a minimal config manager that simulates first-time configuration + const mockConfigManager = { + loadConfiguration: jest.fn().mockResolvedValue({ requiresRestart: true }), + } + ;(manager as any)._configManager = mockConfigManager + + // Mock the feature state to simulate valid configuration that would normally trigger restart + jest.spyOn(manager, "isFeatureEnabled", "get").mockReturnValue(true) + jest.spyOn(manager, "isFeatureConfigured", "get").mockReturnValue(true) + + // The key test: this should NOT throw "CodeIndexManager not initialized" error + await expect(manager.handleExternalSettingsChange()).resolves.not.toThrow() + + // Verify that loadConfiguration was called (the method should still work) + expect(mockConfigManager.loadConfiguration).toHaveBeenCalled() + }) + + it("should work normally when manager is initialized", async () => { + // Mock a minimal config manager + const mockConfigManager = { + loadConfiguration: jest.fn().mockResolvedValue({ requiresRestart: true }), + } + ;(manager as any)._configManager = mockConfigManager + + // Simulate an initialized manager by setting the required properties + ;(manager as any)._orchestrator = { stopWatcher: jest.fn() } + ;(manager as any)._searchService = {} + ;(manager as any)._cacheManager = {} + + // Verify manager is considered initialized + expect(manager.isInitialized).toBe(true) + + // Mock the methods that would be called during restart + const stopWatcherSpy = jest.spyOn(manager, "stopWatcher").mockImplementation() + const startIndexingSpy = jest.spyOn(manager, "startIndexing").mockResolvedValue() + + // Mock the feature state + jest.spyOn(manager, "isFeatureEnabled", "get").mockReturnValue(true) + jest.spyOn(manager, "isFeatureConfigured", "get").mockReturnValue(true) + + await manager.handleExternalSettingsChange() + + // Verify that the restart sequence was called + expect(mockConfigManager.loadConfiguration).toHaveBeenCalled() + expect(stopWatcherSpy).toHaveBeenCalled() + expect(startIndexingSpy).toHaveBeenCalled() + }) + + it("should handle case when config manager is not set", async () => { + // Ensure config manager is not set (edge case) + ;(manager as any)._configManager = undefined + + // This should not throw an error + await expect(manager.handleExternalSettingsChange()).resolves.not.toThrow() + }) + }) +}) diff --git a/src/services/code-index/__tests__/service-factory.test.ts b/src/services/code-index/__tests__/service-factory.test.ts new file mode 100644 index 0000000000..90ce46b97f --- /dev/null +++ b/src/services/code-index/__tests__/service-factory.test.ts @@ -0,0 +1,287 @@ +import { CodeIndexServiceFactory } from "../service-factory" +import { CodeIndexConfigManager } from "../config-manager" +import { CacheManager } from "../cache-manager" +import { OpenAiEmbedder } from "../embedders/openai" +import { CodeIndexOllamaEmbedder } from "../embedders/ollama" +import { QdrantVectorStore } from "../vector-store/qdrant-client" + +// Mock the embedders and vector store +jest.mock("../embedders/openai") +jest.mock("../embedders/ollama") +jest.mock("../vector-store/qdrant-client") + +// Mock the embedding models module +jest.mock("../../../shared/embeddingModels", () => ({ + getDefaultModelId: jest.fn(), + getModelDimension: jest.fn(), +})) + +const MockedOpenAiEmbedder = OpenAiEmbedder as jest.MockedClass +const MockedCodeIndexOllamaEmbedder = CodeIndexOllamaEmbedder as jest.MockedClass +const MockedQdrantVectorStore = QdrantVectorStore as jest.MockedClass + +// Import the mocked functions +import { getDefaultModelId, getModelDimension } from "../../../shared/embeddingModels" +const mockGetDefaultModelId = getDefaultModelId as jest.MockedFunction +const mockGetModelDimension = getModelDimension as jest.MockedFunction + +describe("CodeIndexServiceFactory", () => { + let factory: CodeIndexServiceFactory + let mockConfigManager: jest.Mocked + let mockCacheManager: jest.Mocked + + beforeEach(() => { + jest.clearAllMocks() + + mockConfigManager = { + getConfig: jest.fn(), + } as any + + mockCacheManager = {} as any + + factory = new CodeIndexServiceFactory(mockConfigManager, "/test/workspace", mockCacheManager) + }) + + describe("createEmbedder", () => { + it("should pass model ID to OpenAI embedder when using OpenAI provider", () => { + // Arrange + const testModelId = "text-embedding-3-large" + const testConfig = { + embedderProvider: "openai", + modelId: testModelId, + openAiOptions: { + openAiNativeApiKey: "test-api-key", + }, + } + mockConfigManager.getConfig.mockReturnValue(testConfig as any) + + // Act + factory.createEmbedder() + + // Assert + expect(MockedOpenAiEmbedder).toHaveBeenCalledWith({ + openAiNativeApiKey: "test-api-key", + openAiEmbeddingModelId: testModelId, + }) + }) + + it("should pass model ID to Ollama embedder when using Ollama provider", () => { + // Arrange + const testModelId = "nomic-embed-text:latest" + const testConfig = { + embedderProvider: "ollama", + modelId: testModelId, + ollamaOptions: { + ollamaBaseUrl: "http://localhost:11434", + }, + } + mockConfigManager.getConfig.mockReturnValue(testConfig as any) + + // Act + factory.createEmbedder() + + // Assert + expect(MockedCodeIndexOllamaEmbedder).toHaveBeenCalledWith({ + ollamaBaseUrl: "http://localhost:11434", + ollamaModelId: testModelId, + }) + }) + + it("should handle undefined model ID for OpenAI embedder", () => { + // Arrange + const testConfig = { + embedderProvider: "openai", + modelId: undefined, + openAiOptions: { + openAiNativeApiKey: "test-api-key", + }, + } + mockConfigManager.getConfig.mockReturnValue(testConfig as any) + + // Act + factory.createEmbedder() + + // Assert + expect(MockedOpenAiEmbedder).toHaveBeenCalledWith({ + openAiNativeApiKey: "test-api-key", + openAiEmbeddingModelId: undefined, + }) + }) + + it("should handle undefined model ID for Ollama embedder", () => { + // Arrange + const testConfig = { + embedderProvider: "ollama", + modelId: undefined, + ollamaOptions: { + ollamaBaseUrl: "http://localhost:11434", + }, + } + mockConfigManager.getConfig.mockReturnValue(testConfig as any) + + // Act + factory.createEmbedder() + + // Assert + expect(MockedCodeIndexOllamaEmbedder).toHaveBeenCalledWith({ + ollamaBaseUrl: "http://localhost:11434", + ollamaModelId: undefined, + }) + }) + + it("should throw error when OpenAI API key is missing", () => { + // Arrange + const testConfig = { + embedderProvider: "openai", + modelId: "text-embedding-3-large", + openAiOptions: { + openAiNativeApiKey: undefined, + }, + } + mockConfigManager.getConfig.mockReturnValue(testConfig as any) + + // Act & Assert + expect(() => factory.createEmbedder()).toThrow("OpenAI configuration missing for embedder creation") + }) + + it("should throw error when Ollama base URL is missing", () => { + // Arrange + const testConfig = { + embedderProvider: "ollama", + modelId: "nomic-embed-text:latest", + ollamaOptions: { + ollamaBaseUrl: undefined, + }, + } + mockConfigManager.getConfig.mockReturnValue(testConfig as any) + + // Act & Assert + expect(() => factory.createEmbedder()).toThrow("Ollama configuration missing for embedder creation") + }) + + it("should throw error for invalid embedder provider", () => { + // Arrange + const testConfig = { + embedderProvider: "invalid-provider", + modelId: "some-model", + } + mockConfigManager.getConfig.mockReturnValue(testConfig as any) + + // Act & Assert + expect(() => factory.createEmbedder()).toThrow("Invalid embedder type configured: invalid-provider") + }) + }) + + describe("createVectorStore", () => { + beforeEach(() => { + jest.clearAllMocks() + mockGetDefaultModelId.mockReturnValue("default-model") + }) + + it("should use config.modelId for OpenAI provider", () => { + // Arrange + const testModelId = "text-embedding-3-large" + const testConfig = { + embedderProvider: "openai", + modelId: testModelId, + qdrantUrl: "http://localhost:6333", + qdrantApiKey: "test-key", + } + mockConfigManager.getConfig.mockReturnValue(testConfig as any) + mockGetModelDimension.mockReturnValue(3072) + + // Act + factory.createVectorStore() + + // Assert + expect(mockGetModelDimension).toHaveBeenCalledWith("openai", testModelId) + expect(MockedQdrantVectorStore).toHaveBeenCalledWith( + "/test/workspace", + "http://localhost:6333", + 3072, + "test-key", + ) + }) + + it("should use config.modelId for Ollama provider", () => { + // Arrange + const testModelId = "nomic-embed-text:latest" + const testConfig = { + embedderProvider: "ollama", + modelId: testModelId, + qdrantUrl: "http://localhost:6333", + qdrantApiKey: "test-key", + } + mockConfigManager.getConfig.mockReturnValue(testConfig as any) + mockGetModelDimension.mockReturnValue(768) + + // Act + factory.createVectorStore() + + // Assert + expect(mockGetModelDimension).toHaveBeenCalledWith("ollama", testModelId) + expect(MockedQdrantVectorStore).toHaveBeenCalledWith( + "/test/workspace", + "http://localhost:6333", + 768, + "test-key", + ) + }) + + it("should use default model when config.modelId is undefined", () => { + // Arrange + const testConfig = { + embedderProvider: "openai", + modelId: undefined, + qdrantUrl: "http://localhost:6333", + qdrantApiKey: "test-key", + } + mockConfigManager.getConfig.mockReturnValue(testConfig as any) + mockGetModelDimension.mockReturnValue(1536) + + // Act + factory.createVectorStore() + + // Assert + expect(mockGetModelDimension).toHaveBeenCalledWith("openai", "default-model") + expect(MockedQdrantVectorStore).toHaveBeenCalledWith( + "/test/workspace", + "http://localhost:6333", + 1536, + "test-key", + ) + }) + + it("should throw error when vector dimension cannot be determined", () => { + // Arrange + const testConfig = { + embedderProvider: "openai", + modelId: "unknown-model", + qdrantUrl: "http://localhost:6333", + qdrantApiKey: "test-key", + } + mockConfigManager.getConfig.mockReturnValue(testConfig as any) + mockGetModelDimension.mockReturnValue(undefined) + + // Act & Assert + expect(() => factory.createVectorStore()).toThrow( + "Could not determine vector dimension for model 'unknown-model'. Check model profiles or config.", + ) + }) + + it("should throw error when Qdrant URL is missing", () => { + // Arrange + const testConfig = { + embedderProvider: "openai", + modelId: "text-embedding-3-small", + qdrantUrl: undefined, + qdrantApiKey: "test-key", + } + mockConfigManager.getConfig.mockReturnValue(testConfig as any) + mockGetModelDimension.mockReturnValue(1536) + + // Act & Assert + expect(() => factory.createVectorStore()).toThrow("Qdrant URL missing for vector store creation") + }) + }) +}) diff --git a/src/services/code-index/config-manager.ts b/src/services/code-index/config-manager.ts index 0b0bf2e7e8..730f43e3c5 100644 --- a/src/services/code-index/config-manager.ts +++ b/src/services/code-index/config-manager.ts @@ -3,6 +3,7 @@ import { ContextProxy } from "../../core/config/ContextProxy" import { EmbedderProvider } from "./interfaces/manager" import { CodeIndexConfig, PreviousConfigSnapshot } from "./interfaces/config" import { SEARCH_MIN_SCORE } from "./constants" +import { getDefaultModelId, getModelDimension } from "../../shared/embeddingModels" /** * Manages configuration state and validation for the code indexing feature. @@ -18,38 +19,18 @@ export class CodeIndexConfigManager { private qdrantApiKey?: string private searchMinScore?: number - constructor(private readonly contextProxy: ContextProxy) {} + constructor(private readonly contextProxy: ContextProxy) { + // Initialize with current configuration to avoid false restart triggers + this._loadAndSetConfiguration() + } /** - * Loads persisted configuration from globalState. + * Private method that handles loading configuration from storage and updating instance variables. + * This eliminates code duplication between initializeWithCurrentConfig() and loadConfiguration(). */ - public async loadConfiguration(): Promise<{ - configSnapshot: PreviousConfigSnapshot - currentConfig: { - isEnabled: boolean - isConfigured: boolean - embedderProvider: EmbedderProvider - modelId?: string - openAiOptions?: ApiHandlerOptions - ollamaOptions?: ApiHandlerOptions - qdrantUrl?: string - qdrantApiKey?: string - searchMinScore?: number - } - requiresRestart: boolean - }> { - const previousConfigSnapshot: PreviousConfigSnapshot = { - enabled: this.isEnabled, - configured: this.isConfigured(), - embedderProvider: this.embedderProvider, - modelId: this.modelId, - openAiKey: this.openAiOptions?.openAiNativeApiKey, - ollamaBaseUrl: this.ollamaOptions?.ollamaBaseUrl, - qdrantUrl: this.qdrantUrl, - qdrantApiKey: this.qdrantApiKey, - } - - let codebaseIndexConfig = this.contextProxy?.getGlobalState("codebaseIndexConfig") ?? { + private _loadAndSetConfiguration(): void { + // Load configuration from storage + const codebaseIndexConfig = this.contextProxy?.getGlobalState("codebaseIndexConfig") ?? { codebaseIndexEnabled: false, codebaseIndexQdrantUrl: "http://localhost:6333", codebaseIndexSearchMinScore: 0.4, @@ -69,6 +50,7 @@ export class CodeIndexConfigManager { const openAiKey = this.contextProxy?.getSecret("codeIndexOpenAiKey") ?? "" const qdrantApiKey = this.contextProxy?.getSecret("codeIndexQdrantApiKey") ?? "" + // Update instance variables with configuration this.isEnabled = codebaseIndexEnabled || false this.qdrantUrl = codebaseIndexQdrantUrl this.qdrantApiKey = qdrantApiKey ?? "" @@ -81,6 +63,42 @@ export class CodeIndexConfigManager { this.ollamaOptions = { ollamaBaseUrl: codebaseIndexEmbedderBaseUrl, } + } + + /** + * Loads persisted configuration from globalState. + */ + public async loadConfiguration(): Promise<{ + configSnapshot: PreviousConfigSnapshot + currentConfig: { + isEnabled: boolean + isConfigured: boolean + embedderProvider: EmbedderProvider + modelId?: string + openAiOptions?: ApiHandlerOptions + ollamaOptions?: ApiHandlerOptions + qdrantUrl?: string + qdrantApiKey?: string + searchMinScore?: number + } + requiresRestart: boolean + }> { + // Capture the ACTUAL previous state before loading new configuration + const previousConfigSnapshot: PreviousConfigSnapshot = { + enabled: this.isEnabled, + configured: this.isConfigured(), + embedderProvider: this.embedderProvider, + modelId: this.modelId, + openAiKey: this.openAiOptions?.openAiNativeApiKey ?? "", + ollamaBaseUrl: this.ollamaOptions?.ollamaBaseUrl ?? "", + qdrantUrl: this.qdrantUrl ?? "", + qdrantApiKey: this.qdrantApiKey ?? "", + } + + // Load new configuration from storage and update instance variables + this._loadAndSetConfiguration() + + const requiresRestart = this.doesConfigChangeRequireRestart(previousConfigSnapshot) return { configSnapshot: previousConfigSnapshot, @@ -95,7 +113,7 @@ export class CodeIndexConfigManager { qdrantApiKey: this.qdrantApiKey, searchMinScore: this.searchMinScore, }, - requiresRestart: this._didConfigChangeRequireRestart(previousConfigSnapshot), + requiresRestart, } } @@ -103,7 +121,6 @@ export class CodeIndexConfigManager { * Checks if the service is properly configured based on the embedder type. */ public isConfigured(): boolean { - if (this.embedderProvider === "openai") { const openAiKey = this.openAiOptions?.openAiNativeApiKey const qdrantUrl = this.qdrantUrl @@ -121,43 +138,66 @@ export class CodeIndexConfigManager { /** * Determines if a configuration change requires restarting the indexing process. - * @param prev The previous configuration snapshot - * @returns boolean indicating whether a restart is needed */ - private _didConfigChangeRequireRestart(prev: PreviousConfigSnapshot): boolean { - const nowConfigured = this.isConfigured() // Recalculate based on current state + doesConfigChangeRequireRestart(prev: PreviousConfigSnapshot): boolean { + const nowConfigured = this.isConfigured() - // Check for transition from disabled/unconfigured to enabled+configured - const transitionedToReady = (!prev.enabled || !prev.configured) && this.isEnabled && nowConfigured - if (transitionedToReady) return true + // Handle null/undefined values safely - use empty strings for consistency with loaded config + const prevEnabled = prev?.enabled ?? false + const prevConfigured = prev?.configured ?? false + const prevProvider = prev?.embedderProvider ?? "openai" + const prevModelId = prev?.modelId ?? undefined + const prevOpenAiKey = prev?.openAiKey ?? "" + const prevOllamaBaseUrl = prev?.ollamaBaseUrl ?? "" + const prevQdrantUrl = prev?.qdrantUrl ?? "" + const prevQdrantApiKey = prev?.qdrantApiKey ?? "" - // If wasn't ready before and isn't ready now, no restart needed for config change itself - if (!prev.configured && !nowConfigured) return false - // If was disabled and still is, no restart needed - if (!prev.enabled && !this.isEnabled) return false + // 1. Transition from disabled/unconfigured to enabled+configured + if ((!prevEnabled || !prevConfigured) && this.isEnabled && nowConfigured) { + return true + } - // Check for changes in relevant settings if the feature is enabled (or was enabled) - if (this.isEnabled || prev.enabled) { - // Check for embedder type change - if (prev.embedderProvider !== this.embedderProvider) return true - if (prev.modelId !== this.modelId) return true // Any model change requires restart + // 2. If was disabled and still is, no restart needed + if (!prevEnabled && !this.isEnabled) { + return false + } - // Check OpenAI settings change if using OpenAI - if (this.embedderProvider === "openai") { - if (prev.openAiKey !== this.openAiOptions?.openAiNativeApiKey) return true - // Model ID check moved above + // 3. If wasn't ready before and isn't ready now, no restart needed + if (!prevConfigured && !nowConfigured) { + return false + } + + // 4. Check for changes in relevant settings if the feature is enabled (or was enabled) + if (this.isEnabled || prevEnabled) { + // Provider change + if (prevProvider !== this.embedderProvider) { + return true } - // Check Ollama settings change if using Ollama - if (this.embedderProvider === "ollama") { - if (prev.ollamaBaseUrl !== this.ollamaOptions?.ollamaBaseUrl) { + if (this._hasVectorDimensionChanged(prevProvider, prevModelId)) { + return true + } + + // Authentication changes + if (this.embedderProvider === "openai") { + const currentOpenAiKey = this.openAiOptions?.openAiNativeApiKey ?? "" + if (prevOpenAiKey !== currentOpenAiKey) { return true } - // Model ID check moved above } - // Check Qdrant settings changes - if (prev.qdrantUrl !== this.qdrantUrl || prev.qdrantApiKey !== this.qdrantApiKey) { + if (this.embedderProvider === "ollama") { + const currentOllamaBaseUrl = this.ollamaOptions?.ollamaBaseUrl ?? "" + if (prevOllamaBaseUrl !== currentOllamaBaseUrl) { + return true + } + } + + // Qdrant configuration changes + const currentQdrantUrl = this.qdrantUrl ?? "" + const currentQdrantApiKey = this.qdrantApiKey ?? "" + + if (prevQdrantUrl !== currentQdrantUrl || prevQdrantApiKey !== currentQdrantApiKey) { return true } } @@ -165,6 +205,32 @@ export class CodeIndexConfigManager { return false } + /** + * Checks if model changes result in vector dimension changes that require restart. + */ + private _hasVectorDimensionChanged(prevProvider: EmbedderProvider, prevModelId?: string): boolean { + const currentProvider = this.embedderProvider + const currentModelId = this.modelId ?? getDefaultModelId(currentProvider) + const resolvedPrevModelId = prevModelId ?? getDefaultModelId(prevProvider) + + // If model IDs are the same and provider is the same, no dimension change + if (prevProvider === currentProvider && resolvedPrevModelId === currentModelId) { + return false + } + + // Get vector dimensions for both models + const prevDimension = getModelDimension(prevProvider, resolvedPrevModelId) + const currentDimension = getModelDimension(currentProvider, currentModelId) + + // If we can't determine dimensions, be safe and restart + if (prevDimension === undefined || currentDimension === undefined) { + return true + } + + // Only restart if dimensions actually changed + return prevDimension !== currentDimension + } + /** * Gets the current configuration state. */ diff --git a/src/services/code-index/manager.ts b/src/services/code-index/manager.ts index 6922ae90da..465fa95d0c 100644 --- a/src/services/code-index/manager.ts +++ b/src/services/code-index/manager.ts @@ -99,7 +99,10 @@ export class CodeIndexManager { */ public async initialize(contextProxy: ContextProxy): Promise<{ requiresRestart: boolean }> { // 1. ConfigManager Initialization and Configuration Loading - this._configManager = new CodeIndexConfigManager(contextProxy) + if (!this._configManager) { + this._configManager = new CodeIndexConfigManager(contextProxy) + } + // Load configuration once to get current state and restart requirements const { requiresRestart } = await this._configManager.loadConfiguration() // 2. Check if feature is enabled @@ -249,10 +252,20 @@ export class CodeIndexManager { * Handles external settings changes by reloading configuration. * This method should be called when API provider settings are updated * to ensure the CodeIndexConfigManager picks up the new configuration. + * If the configuration changes require a restart, the service will be restarted. */ public async handleExternalSettingsChange(): Promise { if (this._configManager) { - await this._configManager.loadConfiguration() + const { requiresRestart } = await this._configManager.loadConfiguration() + + const isFeatureEnabled = this.isFeatureEnabled + const isFeatureConfigured = this.isFeatureConfigured + + // If configuration changes require a restart and the manager is initialized, restart the service + if (requiresRestart && isFeatureEnabled && isFeatureConfigured && this.isInitialized) { + this.stopWatcher() + await this.startIndexing() + } } } } diff --git a/src/services/code-index/service-factory.ts b/src/services/code-index/service-factory.ts index 84304a348c..7a95fe9556 100644 --- a/src/services/code-index/service-factory.ts +++ b/src/services/code-index/service-factory.ts @@ -31,12 +31,18 @@ export class CodeIndexServiceFactory { if (!config.openAiOptions?.openAiNativeApiKey) { throw new Error("OpenAI configuration missing for embedder creation") } - return new OpenAiEmbedder(config.openAiOptions) // Reverted temporarily + return new OpenAiEmbedder({ + ...config.openAiOptions, + openAiEmbeddingModelId: config.modelId, + }) } else if (provider === "ollama") { if (!config.ollamaOptions?.ollamaBaseUrl) { throw new Error("Ollama configuration missing for embedder creation") } - return new CodeIndexOllamaEmbedder(config.ollamaOptions) // Reverted temporarily + return new CodeIndexOllamaEmbedder({ + ...config.ollamaOptions, + ollamaModelId: config.modelId, + }) } throw new Error(`Invalid embedder type configured: ${config.embedderProvider}`) @@ -50,11 +56,8 @@ export class CodeIndexServiceFactory { const provider = config.embedderProvider as EmbedderProvider const defaultModel = getDefaultModelId(provider) - // Determine the modelId based on the provider and config, using apiModelId - const modelId = - provider === "openai" - ? (config.openAiOptions?.apiModelId ?? defaultModel) - : (config.ollamaOptions?.apiModelId ?? defaultModel) + // Use the embedding model ID from config, not the chat model IDs + const modelId = config.modelId ?? defaultModel const vectorSize = getModelDimension(provider, modelId) diff --git a/webview-ui/src/components/settings/CodeIndexSettings.tsx b/webview-ui/src/components/settings/CodeIndexSettings.tsx index 8aaaa888bb..06bac1927d 100644 --- a/webview-ui/src/components/settings/CodeIndexSettings.tsx +++ b/webview-ui/src/components/settings/CodeIndexSettings.tsx @@ -97,6 +97,30 @@ export const CodeIndexSettings: React.FC = ({ } }, [codebaseIndexConfig, codebaseIndexModels]) + /** + * Determines the appropriate model ID when changing providers + */ + function getModelIdForProvider( + newProvider: EmbedderProvider, + currentProvider: EmbedderProvider | undefined, + currentModelId: string | undefined, + availableModels: CodebaseIndexModels | undefined, + ): string { + if (newProvider === currentProvider && currentModelId) { + return currentModelId + } + + const models = availableModels?.[newProvider] + const modelIds = models ? Object.keys(models) : [] + + if (currentModelId && modelIds.includes(currentModelId)) { + return currentModelId + } + + const selectedModel = modelIds.length > 0 ? modelIds[0] : "" + return selectedModel + } + function validateIndexingConfig(config: CodebaseIndexConfig | undefined, apiConfig: ProviderSettings): boolean { if (!config) return false @@ -210,15 +234,21 @@ export const CodeIndexSettings: React.FC = ({ value={codebaseIndexConfig?.codebaseIndexEmbedderProvider || "openai"} onValueChange={(value) => { const newProvider = value as EmbedderProvider - const models = codebaseIndexModels?.[newProvider] - const modelIds = models ? Object.keys(models) : [] - const defaultModelId = modelIds.length > 0 ? modelIds[0] : "" // Use empty string if no models + const currentProvider = codebaseIndexConfig?.codebaseIndexEmbedderProvider + const currentModelId = codebaseIndexConfig?.codebaseIndexEmbedderModelId + + const modelIdToUse = getModelIdForProvider( + newProvider, + currentProvider, + currentModelId, + codebaseIndexModels, + ) if (codebaseIndexConfig) { setCachedStateField("codebaseIndexConfig", { ...codebaseIndexConfig, codebaseIndexEmbedderProvider: newProvider, - codebaseIndexEmbedderModelId: defaultModelId, + codebaseIndexEmbedderModelId: modelIdToUse, }) } }}> From 7509be8d41c2d16ffe448f8299059cd078f946e9 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Sun, 25 May 2025 22:49:31 -0400 Subject: [PATCH 007/104] Remove the hardcoded line breaks in the about (#3982) --- webview-ui/src/components/chat/ChatView.tsx | 2 +- webview-ui/src/i18n/locales/ca/chat.json | 2 +- webview-ui/src/i18n/locales/de/chat.json | 2 +- webview-ui/src/i18n/locales/en/chat.json | 2 +- webview-ui/src/i18n/locales/es/chat.json | 2 +- webview-ui/src/i18n/locales/fr/chat.json | 2 +- webview-ui/src/i18n/locales/it/chat.json | 2 +- webview-ui/src/i18n/locales/nl/chat.json | 2 +- webview-ui/src/i18n/locales/pl/chat.json | 2 +- webview-ui/src/i18n/locales/pt-BR/chat.json | 2 +- webview-ui/src/i18n/locales/ru/chat.json | 2 +- webview-ui/src/i18n/locales/tr/chat.json | 2 +- webview-ui/src/i18n/locales/vi/chat.json | 2 +- webview-ui/src/i18n/locales/zh-CN/chat.json | 2 +- webview-ui/src/i18n/locales/zh-TW/chat.json | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index 7180ee8655..0b39baf39a 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -1325,7 +1325,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction} {/* Show the task history preview if expanded and tasks exist */} {taskHistory.length > 0 && isExpanded && } -

+

Consulta la nostra documentació per obtenir més informació.", + "about": "Genera, refactoritza i depura codi amb l'ajuda de la IA. Consulta la nostra documentació per obtenir més informació.", "onboarding": " La vostra llista de tasques en aquest espai de treball està buida. Comença escrivint una tasca a continuació. \nNo esteu segur per on començar? \nMés informació sobre què pot fer Roo als documents.", "rooTips": { "boomerangTasks": { diff --git a/webview-ui/src/i18n/locales/de/chat.json b/webview-ui/src/i18n/locales/de/chat.json index 497d51824a..ca2bb62b20 100644 --- a/webview-ui/src/i18n/locales/de/chat.json +++ b/webview-ui/src/i18n/locales/de/chat.json @@ -70,7 +70,7 @@ "tooltip": "Aktuelle Operation abbrechen" }, "scrollToBottom": "Zum Chat-Ende scrollen", - "about": "Generiere, überarbeite und debugge Code mit KI-Unterstützung.
Weitere Informationen findest du in unserer Dokumentation.", + "about": "Generiere, überarbeite und debugge Code mit KI-Unterstützung. Weitere Informationen findest du in unserer Dokumentation.", "onboarding": "Deine Aufgabenliste in diesem Arbeitsbereich ist leer. Beginne mit der Eingabe einer Aufgabe unten. Du bist dir nicht sicher, wie du anfangen sollst? Lies mehr darüber, was Roo für dich tun kann, in den Dokumenten.", "rooTips": { "boomerangTasks": { diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index 3c133c40e8..adc47fc7d4 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -70,7 +70,7 @@ "tooltip": "Cancel the current operation" }, "scrollToBottom": "Scroll to bottom of chat", - "about": "Generate, refactor, and debug code with AI assistance.
Check out our documentation to learn more.", + "about": "Generate, refactor, and debug code with AI assistance. Check out our documentation to learn more.", "onboarding": "Your task list in this workspace is empty.", "rooTips": { "boomerangTasks": { diff --git a/webview-ui/src/i18n/locales/es/chat.json b/webview-ui/src/i18n/locales/es/chat.json index d0b3115466..c05863f3dc 100644 --- a/webview-ui/src/i18n/locales/es/chat.json +++ b/webview-ui/src/i18n/locales/es/chat.json @@ -70,7 +70,7 @@ "tooltip": "Cancelar la operación actual" }, "scrollToBottom": "Desplazarse al final del chat", - "about": "Genera, refactoriza y depura código con asistencia de IA.
Consulta nuestra documentación para obtener más información.", + "about": "Genera, refactoriza y depura código con asistencia de IA. Consulta nuestra documentación para obtener más información.", "onboarding": "Tu lista de tareas en este espacio de trabajo está vacía. Comienza escribiendo una tarea abajo. ¿No estás seguro cómo empezar? Lee más sobre lo que Roo puede hacer por ti en la documentación.", "rooTips": { "boomerangTasks": { diff --git a/webview-ui/src/i18n/locales/fr/chat.json b/webview-ui/src/i18n/locales/fr/chat.json index ce03487272..073da26ee2 100644 --- a/webview-ui/src/i18n/locales/fr/chat.json +++ b/webview-ui/src/i18n/locales/fr/chat.json @@ -70,7 +70,7 @@ "tooltip": "Annuler l'opération actuelle" }, "scrollToBottom": "Défiler jusqu'au bas du chat", - "about": "Générer, refactoriser et déboguer du code avec l'assistance de l'IA.
Consultez notre documentation pour en savoir plus.", + "about": "Générer, refactoriser et déboguer du code avec l'assistance de l'IA. Consultez notre documentation pour en savoir plus.", "onboarding": "Grâce aux dernières avancées en matière de capacités de codage agent, je peux gérer des tâches complexes de développement logiciel étape par étape. Avec des outils qui me permettent de créer et d'éditer des fichiers, d'explorer des projets complexes, d'utiliser le navigateur et d'exécuter des commandes de terminal (après votre autorisation), je peux vous aider de manières qui vont au-delà de la complétion de code ou du support technique. Je peux même utiliser MCP pour créer de nouveaux outils et étendre mes propres capacités.", "rooTips": { "boomerangTasks": { diff --git a/webview-ui/src/i18n/locales/it/chat.json b/webview-ui/src/i18n/locales/it/chat.json index c334bd3359..4208040b59 100644 --- a/webview-ui/src/i18n/locales/it/chat.json +++ b/webview-ui/src/i18n/locales/it/chat.json @@ -70,7 +70,7 @@ "tooltip": "Annulla l'operazione corrente" }, "scrollToBottom": "Scorri fino alla fine della chat", - "about": "Genera, refactor e debug del codice con l'assistenza dell'IA.
Consulta la nostra documentazione per saperne di più.", + "about": "Genera, refactor e debug del codice con l'assistenza dell'IA. Consulta la nostra documentazione per saperne di più.", "onboarding": "Grazie alle più recenti innovazioni nelle capacità di codifica agentica, posso gestire complesse attività di sviluppo software passo dopo passo. Con strumenti che mi permettono di creare e modificare file, esplorare progetti complessi, utilizzare il browser ed eseguire comandi da terminale (dopo la tua autorizzazione), posso aiutarti in modi che vanno oltre il completamento del codice o il supporto tecnico. Posso persino usare MCP per creare nuovi strumenti ed estendere le mie capacità.", "rooTips": { "boomerangTasks": { diff --git a/webview-ui/src/i18n/locales/nl/chat.json b/webview-ui/src/i18n/locales/nl/chat.json index ac49525769..b5217049aa 100644 --- a/webview-ui/src/i18n/locales/nl/chat.json +++ b/webview-ui/src/i18n/locales/nl/chat.json @@ -70,7 +70,7 @@ "tooltip": "Annuleer de huidige bewerking" }, "scrollToBottom": "Scroll naar onderaan de chat", - "about": "Genereer, refactor en debug code met AI-assistentie.
Bekijk onze documentatie voor meer informatie.", + "about": "Genereer, refactor en debug code met AI-assistentie. Bekijk onze documentatie voor meer informatie.", "onboarding": "Je takenlijst in deze werkruimte is leeg.", "rooTips": { "boomerangTasks": { diff --git a/webview-ui/src/i18n/locales/pl/chat.json b/webview-ui/src/i18n/locales/pl/chat.json index f6e666ad48..a33c3126d8 100644 --- a/webview-ui/src/i18n/locales/pl/chat.json +++ b/webview-ui/src/i18n/locales/pl/chat.json @@ -70,7 +70,7 @@ "tooltip": "Anuluj bieżącą operację" }, "scrollToBottom": "Przewiń do dołu czatu", - "about": "Generuj, refaktoryzuj i debuguj kod z pomocą sztucznej inteligencji.
Sprawdź naszą dokumentację, aby dowiedzieć się więcej.", + "about": "Generuj, refaktoryzuj i debuguj kod z pomocą sztucznej inteligencji. Sprawdź naszą dokumentację, aby dowiedzieć się więcej.", "onboarding": "Twoja lista zadań w tym obszarze roboczym jest pusta. Zacznij od wpisania zadania poniżej. Nie wiesz, jak zacząć? Przeczytaj więcej o tym, co Roo może dla Ciebie zrobić w dokumentacji.", "rooTips": { "boomerangTasks": { diff --git a/webview-ui/src/i18n/locales/pt-BR/chat.json b/webview-ui/src/i18n/locales/pt-BR/chat.json index 6e9dd94326..f75634c45b 100644 --- a/webview-ui/src/i18n/locales/pt-BR/chat.json +++ b/webview-ui/src/i18n/locales/pt-BR/chat.json @@ -70,7 +70,7 @@ "tooltip": "Cancelar a operação atual" }, "scrollToBottom": "Rolar para o final do chat", - "about": "Gere, refatore e depure código com assistência de IA.
Confira nossa documentação para saber mais.", + "about": "Gere, refatore e depure código com assistência de IA. Confira nossa documentação para saber mais.", "onboarding": "Sua lista de tarefas neste espaço de trabalho está vazia. Comece digitando uma tarefa abaixo. Não sabe como começar? Leia mais sobre o que o Roo pode fazer por você nos documentos.", "rooTips": { "boomerangTasks": { diff --git a/webview-ui/src/i18n/locales/ru/chat.json b/webview-ui/src/i18n/locales/ru/chat.json index 8046374211..4c7ef08021 100644 --- a/webview-ui/src/i18n/locales/ru/chat.json +++ b/webview-ui/src/i18n/locales/ru/chat.json @@ -70,7 +70,7 @@ "tooltip": "Отменить текущую операцию" }, "scrollToBottom": "Прокрутить чат вниз", - "about": "Создавайте, рефакторите и отлаживайте код с помощью ИИ.
Подробнее см. в нашей документации.", + "about": "Создавайте, рефакторите и отлаживайте код с помощью ИИ. Подробнее см. в нашей документации.", "rooTips": { "boomerangTasks": { "title": "Задачи-бумеранги", diff --git a/webview-ui/src/i18n/locales/tr/chat.json b/webview-ui/src/i18n/locales/tr/chat.json index c7542d1973..98fde1efe7 100644 --- a/webview-ui/src/i18n/locales/tr/chat.json +++ b/webview-ui/src/i18n/locales/tr/chat.json @@ -70,7 +70,7 @@ "tooltip": "Mevcut işlemi iptal et" }, "scrollToBottom": "Sohbetin altına kaydır", - "about": "AI yardımıyla kod oluşturun, yeniden düzenleyin ve hatalarını ayıklayın.
Daha fazla bilgi edinmek için belgelerimize göz atın.", + "about": "AI yardımıyla kod oluşturun, yeniden düzenleyin ve hatalarını ayıklayın. Daha fazla bilgi edinmek için belgelerimize göz atın.", "onboarding": "Bu çalışma alanındaki görev listeniz boş. Aşağıya bir görev yazarak başlayın. Nasıl başlayacağınızdan emin değil misiniz? Roo'nun sizin için neler yapabileceği hakkında daha fazla bilgiyi belgelerde okuyun.", "rooTips": { "boomerangTasks": { diff --git a/webview-ui/src/i18n/locales/vi/chat.json b/webview-ui/src/i18n/locales/vi/chat.json index bb3eecb98b..a6b661a1ae 100644 --- a/webview-ui/src/i18n/locales/vi/chat.json +++ b/webview-ui/src/i18n/locales/vi/chat.json @@ -70,7 +70,7 @@ "tooltip": "Hủy thao tác hiện tại" }, "scrollToBottom": "Cuộn xuống cuối cuộc trò chuyện", - "about": "Tạo, tái cấu trúc và gỡ lỗi mã bằng sự hỗ trợ của AI.
Kiểm tra tài liệu của chúng tôi để tìm hiểu thêm.", + "about": "Tạo, tái cấu trúc và gỡ lỗi mã bằng sự hỗ trợ của AI. Kiểm tra tài liệu của chúng tôi để tìm hiểu thêm.", "onboarding": "Danh sách nhiệm vụ của bạn trong không gian làm việc này trống. Bắt đầu bằng cách nhập nhiệm vụ bên dưới. Bạn không chắc chắn nên bắt đầu như thế nào? Đọc thêm về những gì Roo có thể làm cho bạn trong tài liệu.", "rooTips": { "boomerangTasks": { diff --git a/webview-ui/src/i18n/locales/zh-CN/chat.json b/webview-ui/src/i18n/locales/zh-CN/chat.json index a0adbadd87..80cae1e519 100644 --- a/webview-ui/src/i18n/locales/zh-CN/chat.json +++ b/webview-ui/src/i18n/locales/zh-CN/chat.json @@ -70,7 +70,7 @@ "tooltip": "取消当前操作" }, "scrollToBottom": "滚动到聊天底部", - "about": "通过 AI 辅助生成、重构和调试代码。
查看我们的 文档 了解更多信息。", + "about": "通过 AI 辅助生成、重构和调试代码。查看我们的 文档 了解更多信息。", "onboarding": "此工作区中的任务列表为空。 请在下方输入任务开始。 不确定如何开始? 在 文档 中阅读更多关于 Roo 可以为您做什么的信息。", "rooTips": { "boomerangTasks": { diff --git a/webview-ui/src/i18n/locales/zh-TW/chat.json b/webview-ui/src/i18n/locales/zh-TW/chat.json index be00f7674f..75b42c44e2 100644 --- a/webview-ui/src/i18n/locales/zh-TW/chat.json +++ b/webview-ui/src/i18n/locales/zh-TW/chat.json @@ -70,7 +70,7 @@ "tooltip": "取消目前操作" }, "scrollToBottom": "捲動至對話框底部", - "about": "透過 AI 輔助產生、重構和偵錯程式碼。
查看我們的 說明文件 以瞭解更多資訊。", + "about": "透過 AI 輔助產生、重構和偵錯程式碼。查看我們的 說明文件 以瞭解更多資訊。", "onboarding": "您在此工作區中的工作清單是空的。 請在下方輸入工作以開始。 不確定如何開始? 在 說明文件 中閱讀更多關於 Roo 能為您做什麼的資訊。", "rooTips": { "boomerangTasks": { From fd84d4840059a0bff54f208e0de9fc4208616f4e Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Sun, 25 May 2025 22:56:44 -0400 Subject: [PATCH 008/104] v3.18.4 (#3984) --- .changeset/v3.18.4.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .changeset/v3.18.4.md diff --git a/.changeset/v3.18.4.md b/.changeset/v3.18.4.md new file mode 100644 index 0000000000..b6ed94af08 --- /dev/null +++ b/.changeset/v3.18.4.md @@ -0,0 +1,12 @@ +--- +"roo-cline": patch +--- + +- Fix settings saving logic to ensure the saved settings are used (thanks @daniel-lxs!) +- Fix handling BOM when user rejects apply_diff (thanks @avtc!) +- Fix wrongfully clearing input on autoApprove (thanks @Ruakij!) +- Fix correct spawnSync parameters for pnpm check in bootstrap.mjs (thanks @ChuKhaLi!) +- Batch code segments when using ollama to report progress (thanks @daniel-lxs!) +- Remove hardcoded line breaks in the about section (thanks @mrubens!) +- Update xAI models and default model ID (thanks @PeterDaveHello!) +- Add metadata to create message (thanks @dtrugman!) From 5bf46542cd295a04c8072d80005051213a15f617 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 25 May 2025 22:57:37 -0400 Subject: [PATCH 009/104] Update contributors list (#3961) Co-authored-by: mrubens --- README.md | 60 ++++++++++++++++++++--------------------- locales/ca/README.md | 46 +++++++++++++++---------------- locales/de/README.md | 46 +++++++++++++++---------------- locales/es/README.md | 46 +++++++++++++++---------------- locales/fr/README.md | 46 +++++++++++++++---------------- locales/hi/README.md | 46 +++++++++++++++---------------- locales/it/README.md | 46 +++++++++++++++---------------- locales/ja/README.md | 46 +++++++++++++++---------------- locales/ko/README.md | 46 +++++++++++++++---------------- locales/nl/README.md | 46 +++++++++++++++---------------- locales/pl/README.md | 46 +++++++++++++++---------------- locales/pt-BR/README.md | 46 +++++++++++++++---------------- locales/ru/README.md | 46 +++++++++++++++---------------- locales/tr/README.md | 46 +++++++++++++++---------------- locales/vi/README.md | 46 +++++++++++++++---------------- locales/zh-CN/README.md | 46 +++++++++++++++---------------- locales/zh-TW/README.md | 46 +++++++++++++++---------------- 17 files changed, 398 insertions(+), 398 deletions(-) diff --git a/README.md b/README.md index 86d4eb53b3..f03668c692 100644 --- a/README.md +++ b/README.md @@ -176,36 +176,36 @@ 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
| hannesrudolph
hannesrudolph
| KJ7LNW
KJ7LNW
| stea9499
stea9499
| canrobins13
canrobins13
| joemanley201
joemanley201
| -| System233
System233
| nissa-seru
nissa-seru
| jquanton
jquanton
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| -| punkpeye
punkpeye
| wkordalski
wkordalski
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| monotykamary
monotykamary
| elianiva
elianiva
| cannuri
cannuri
| -| feifei325
feifei325
| zhangtony239
zhangtony239
| qdaxb
qdaxb
| vigneshsubbiah16
vigneshsubbiah16
| shariqriazz
shariqriazz
| lloydchang
lloydchang
| -| sachasayan
sachasayan
| Szpadel
Szpadel
| diarmidmackenzie
diarmidmackenzie
| psv2522
psv2522
| Premshay
Premshay
| lupuletic
lupuletic
| -| xyOz-dev
xyOz-dev
| pugazhendhi-m
pugazhendhi-m
| aheizi
aheizi
| olweraltuve
olweraltuve
| jr
jr
| dtrugman
dtrugman
| -| nbihan-mediware
nbihan-mediware
| PeterDaveHello
PeterDaveHello
| RaySinner
RaySinner
| afshawnlotfi
afshawnlotfi
| emshvac
emshvac
| kyle-apex
kyle-apex
| -| pdecat
pdecat
| Lunchb0ne
Lunchb0ne
| vagadiya
vagadiya
| arthurauffray
arthurauffray
| upamune
upamune
| StevenTCramer
StevenTCramer
| -| sammcj
sammcj
| p12tic
p12tic
| noritaka1166
noritaka1166
| gtaylor
gtaylor
| aitoroses
aitoroses
| philfung
philfung
| -| ross
ross
| heyseth
heyseth
| taisukeoe
taisukeoe
| dlab-anton
dlab-anton
| eonghk
eonghk
| ronyblum
ronyblum
| -| teddyOOXX
teddyOOXX
| vincentsong
vincentsong
| yongjer
yongjer
| SmartManoj
SmartManoj
| ashktn
ashktn
| franekp
franekp
| -| yt3trees
yt3trees
| benzntech
benzntech
| axkirillov
axkirillov
| anton-otee
anton-otee
| bramburn
bramburn
| hassoncs
hassoncs
| -| ChuKhaLi
ChuKhaLi
| snoyiatk
snoyiatk
| GitlyHallows
GitlyHallows
| jcbdev
jcbdev
| Chenjiayuan195
Chenjiayuan195
| julionav
julionav
| -| SplittyDev
SplittyDev
| mdp
mdp
| napter
napter
| GOODBOY008
GOODBOY008
| hatsu38
hatsu38
| hongzio
hongzio
| -| im47cn
im47cn
| shoopapa
shoopapa
| jwcraig
jwcraig
| kinandan
kinandan
| kohii
kohii
| lightrabbit
lightrabbit
| -| nevermorec
nevermorec
| dqroid
dqroid
| dairui1
dairui1
| bannzai
bannzai
| axmo
axmo
| avtc
avtc
| -| asychin
asychin
| amittell
amittell
| Yoshino-Yukitaro
Yoshino-Yukitaro
| Yikai-Liao
Yikai-Liao
| zxdvd
zxdvd
| vladstudio
vladstudio
| -| PretzelVector
PretzelVector
| zetaloop
zetaloop
| zeozeozeo
zeozeozeo
| cdlliuy
cdlliuy
| student20880
student20880
| slytechnical
slytechnical
| -| shohei-ihaya
shohei-ihaya
| shaybc
shaybc
| seedlord
seedlord
| samir-nimbly
samir-nimbly
| robertheadley
robertheadley
| refactorthis
refactorthis
| -| pokutuna
pokutuna
| philipnext
philipnext
| oprstchn
oprstchn
| nobu007
nobu007
| mosleyit
mosleyit
| moqimoqidea
moqimoqidea
| -| mlopezr
mlopezr
| mecab
mecab
| olup
olup
| DeXtroTip
DeXtroTip
| pfitz
pfitz
| celestial-vault
celestial-vault
| -| linegel
linegel
| dbasclpy
dbasclpy
| Deon588
Deon588
| dleen
dleen
| devxpain
devxpain
| chadgauth
chadgauth
| -| olearycrew
olearycrew
| bogdan0083
bogdan0083
| Atlogit
Atlogit
| atlasgong
atlasgong
| andreastempsch
andreastempsch
| alasano
alasano
| -| QuinsZouls
QuinsZouls
| HadesArchitect
HadesArchitect
| alarno
alarno
| adilhafeez
adilhafeez
| adamwlarson
adamwlarson
| AMHesch
AMHesch
| -| NamesMT
NamesMT
| tmsjngx0
tmsjngx0
| tgfjt
tgfjt
| maekawataiki
maekawataiki
| SannidhyaSah
SannidhyaSah
| samsilveira
samsilveira
| -| mr-ryan-james
mr-ryan-james
| 01Rian
01Rian
| RSO
RSO
| R-omk
R-omk
| Sarke
Sarke
| kvokka
kvokka
| -| ecmasx
ecmasx
| marvijo-code
marvijo-code
| mamertofabian
mamertofabian
| monkeyDluffy6017
monkeyDluffy6017
| libertyteeth
libertyteeth
| shtse8
shtse8
| -| ksze
ksze
| Jdo300
Jdo300
| hesara
hesara
| | | | +| mrubens
mrubens
| saoudrizwan
saoudrizwan
| cte
cte
| samhvw8
samhvw8
| daniel-lxs
daniel-lxs
| a8trejo
a8trejo
| +| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| ColemanRoo
ColemanRoo
| hannesrudolph
hannesrudolph
| KJ7LNW
KJ7LNW
| stea9499
stea9499
| canrobins13
canrobins13
| joemanley201
joemanley201
| +| System233
System233
| nissa-seru
nissa-seru
| jquanton
jquanton
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| +| punkpeye
punkpeye
| wkordalski
wkordalski
| monotykamary
monotykamary
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| elianiva
elianiva
| cannuri
cannuri
| +| feifei325
feifei325
| zhangtony239
zhangtony239
| qdaxb
qdaxb
| vigneshsubbiah16
vigneshsubbiah16
| shariqriazz
shariqriazz
| lloydchang
lloydchang
| +| sachasayan
sachasayan
| Szpadel
Szpadel
| dtrugman
dtrugman
| diarmidmackenzie
diarmidmackenzie
| psv2522
psv2522
| Premshay
Premshay
| +| lupuletic
lupuletic
| xyOz-dev
xyOz-dev
| pugazhendhi-m
pugazhendhi-m
| aheizi
aheizi
| PeterDaveHello
PeterDaveHello
| olweraltuve
olweraltuve
| +| jr
jr
| nbihan-mediware
nbihan-mediware
| RaySinner
RaySinner
| afshawnlotfi
afshawnlotfi
| emshvac
emshvac
| kyle-apex
kyle-apex
| +| pdecat
pdecat
| Lunchb0ne
Lunchb0ne
| vagadiya
vagadiya
| arthurauffray
arthurauffray
| upamune
upamune
| StevenTCramer
StevenTCramer
| +| sammcj
sammcj
| p12tic
p12tic
| noritaka1166
noritaka1166
| gtaylor
gtaylor
| ChuKhaLi
ChuKhaLi
| aitoroses
aitoroses
| +| ross
ross
| heyseth
heyseth
| taisukeoe
taisukeoe
| avtc
avtc
| dlab-anton
dlab-anton
| eonghk
eonghk
| +| ronyblum
ronyblum
| teddyOOXX
teddyOOXX
| vincentsong
vincentsong
| yongjer
yongjer
| SmartManoj
SmartManoj
| ashktn
ashktn
| +| franekp
franekp
| yt3trees
yt3trees
| benzntech
benzntech
| axkirillov
axkirillov
| anton-otee
anton-otee
| bramburn
bramburn
| +| hassoncs
hassoncs
| snoyiatk
snoyiatk
| GitlyHallows
GitlyHallows
| jcbdev
jcbdev
| Chenjiayuan195
Chenjiayuan195
| julionav
julionav
| +| SplittyDev
SplittyDev
| mdp
mdp
| napter
napter
| philfung
philfung
| GOODBOY008
GOODBOY008
| hatsu38
hatsu38
| +| hongzio
hongzio
| im47cn
im47cn
| shoopapa
shoopapa
| jwcraig
jwcraig
| kinandan
kinandan
| kohii
kohii
| +| lightrabbit
lightrabbit
| nevermorec
nevermorec
| dqroid
dqroid
| dairui1
dairui1
| bannzai
bannzai
| axmo
axmo
| +| asychin
asychin
| amittell
amittell
| Yoshino-Yukitaro
Yoshino-Yukitaro
| Yikai-Liao
Yikai-Liao
| zxdvd
zxdvd
| vladstudio
vladstudio
| +| NamesMT
NamesMT
| PretzelVector
PretzelVector
| zetaloop
zetaloop
| zeozeozeo
zeozeozeo
| cdlliuy
cdlliuy
| student20880
student20880
| +| slytechnical
slytechnical
| shohei-ihaya
shohei-ihaya
| shaybc
shaybc
| seedlord
seedlord
| samir-nimbly
samir-nimbly
| robertheadley
robertheadley
| +| refactorthis
refactorthis
| pokutuna
pokutuna
| philipnext
philipnext
| oprstchn
oprstchn
| nobu007
nobu007
| mosleyit
mosleyit
| +| moqimoqidea
moqimoqidea
| mlopezr
mlopezr
| mecab
mecab
| olup
olup
| DeXtroTip
DeXtroTip
| pfitz
pfitz
| +| celestial-vault
celestial-vault
| linegel
linegel
| dbasclpy
dbasclpy
| Deon588
Deon588
| dleen
dleen
| devxpain
devxpain
| +| chadgauth
chadgauth
| olearycrew
olearycrew
| bogdan0083
bogdan0083
| Atlogit
Atlogit
| atlasgong
atlasgong
| andreastempsch
andreastempsch
| +| alasano
alasano
| QuinsZouls
QuinsZouls
| HadesArchitect
HadesArchitect
| alarno
alarno
| adilhafeez
adilhafeez
| adamwlarson
adamwlarson
| +| AMHesch
AMHesch
| tmsjngx0
tmsjngx0
| tgfjt
tgfjt
| maekawataiki
maekawataiki
| SannidhyaSah
SannidhyaSah
| samsilveira
samsilveira
| +| mr-ryan-james
mr-ryan-james
| Ruakij
Ruakij
| 01Rian
01Rian
| RSO
RSO
| R-omk
R-omk
| Sarke
Sarke
| +| kvokka
kvokka
| ecmasx
ecmasx
| marvijo-code
marvijo-code
| mamertofabian
mamertofabian
| monkeyDluffy6017
monkeyDluffy6017
| libertyteeth
libertyteeth
| +| shtse8
shtse8
| ksze
ksze
| Jdo300
Jdo300
| hesara
hesara
| | | diff --git a/locales/ca/README.md b/locales/ca/README.md index 2234e69f8d..9603cb2fec 100644 --- a/locales/ca/README.md +++ b/locales/ca/README.md @@ -185,32 +185,32 @@ Gràcies a tots els nostres col·laboradors que han ajudat a millorar Roo Code! |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|elianiva
elianiva
|cannuri
cannuri
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| |feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
|lupuletic
lupuletic
| -|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|olweraltuve
olweraltuve
|jr
jr
|dtrugman
dtrugman
| -|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| |pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|aitoroses
aitoroses
|philfung
philfung
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
| -|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
| -|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
|hassoncs
hassoncs
| -|ChuKhaLi
ChuKhaLi
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
|hongzio
hongzio
| -|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
| -|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|avtc
avtc
| +|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| +|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| +|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| +|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| +|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| +|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| +|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| +|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| |asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
|slytechnical
slytechnical
| -|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
|refactorthis
refactorthis
| -|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
| -|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
|celestial-vault
celestial-vault
| -|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|alasano
alasano
| -|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
| -|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
|kvokka
kvokka
| -|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
|shtse8
shtse8
| -|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | | +|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| +|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| +|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| +|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| +|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| +|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| +|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| +|kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | ## Llicència diff --git a/locales/de/README.md b/locales/de/README.md index e64d574c50..93d1d040b2 100644 --- a/locales/de/README.md +++ b/locales/de/README.md @@ -185,32 +185,32 @@ Danke an alle unsere Mitwirkenden, die geholfen haben, Roo Code zu verbessern! |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|elianiva
elianiva
|cannuri
cannuri
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| |feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
|lupuletic
lupuletic
| -|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|olweraltuve
olweraltuve
|jr
jr
|dtrugman
dtrugman
| -|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| |pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|aitoroses
aitoroses
|philfung
philfung
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
| -|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
| -|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
|hassoncs
hassoncs
| -|ChuKhaLi
ChuKhaLi
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
|hongzio
hongzio
| -|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
| -|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|avtc
avtc
| +|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| +|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| +|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| +|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| +|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| +|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| +|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| +|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| |asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
|slytechnical
slytechnical
| -|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
|refactorthis
refactorthis
| -|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
| -|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
|celestial-vault
celestial-vault
| -|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|alasano
alasano
| -|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
| -|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
|kvokka
kvokka
| -|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
|shtse8
shtse8
| -|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | | +|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| +|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| +|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| +|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| +|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| +|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| +|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| +|kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | ## Lizenz diff --git a/locales/es/README.md b/locales/es/README.md index b81f4a7294..e3f0794935 100644 --- a/locales/es/README.md +++ b/locales/es/README.md @@ -185,32 +185,32 @@ Usamos [changesets](https://github.com/changesets/changesets) para versionar y p |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|elianiva
elianiva
|cannuri
cannuri
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| |feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
|lupuletic
lupuletic
| -|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|olweraltuve
olweraltuve
|jr
jr
|dtrugman
dtrugman
| -|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| |pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|aitoroses
aitoroses
|philfung
philfung
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
| -|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
| -|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
|hassoncs
hassoncs
| -|ChuKhaLi
ChuKhaLi
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
|hongzio
hongzio
| -|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
| -|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|avtc
avtc
| +|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| +|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| +|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| +|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| +|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| +|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| +|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| +|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| |asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
|slytechnical
slytechnical
| -|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
|refactorthis
refactorthis
| -|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
| -|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
|celestial-vault
celestial-vault
| -|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|alasano
alasano
| -|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
| -|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
|kvokka
kvokka
| -|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
|shtse8
shtse8
| -|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | | +|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| +|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| +|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| +|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| +|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| +|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| +|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| +|kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | ## Licencia diff --git a/locales/fr/README.md b/locales/fr/README.md index 07415e04e0..d62225c4fd 100644 --- a/locales/fr/README.md +++ b/locales/fr/README.md @@ -185,32 +185,32 @@ Merci à tous nos contributeurs qui ont aidé à améliorer Roo Code ! |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|elianiva
elianiva
|cannuri
cannuri
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| |feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
|lupuletic
lupuletic
| -|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|olweraltuve
olweraltuve
|jr
jr
|dtrugman
dtrugman
| -|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| |pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|aitoroses
aitoroses
|philfung
philfung
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
| -|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
| -|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
|hassoncs
hassoncs
| -|ChuKhaLi
ChuKhaLi
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
|hongzio
hongzio
| -|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
| -|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|avtc
avtc
| +|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| +|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| +|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| +|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| +|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| +|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| +|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| +|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| |asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
|slytechnical
slytechnical
| -|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
|refactorthis
refactorthis
| -|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
| -|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
|celestial-vault
celestial-vault
| -|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|alasano
alasano
| -|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
| -|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
|kvokka
kvokka
| -|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
|shtse8
shtse8
| -|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | | +|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| +|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| +|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| +|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| +|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| +|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| +|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| +|kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | ## Licence diff --git a/locales/hi/README.md b/locales/hi/README.md index 24bf2e0a6e..78cf479229 100644 --- a/locales/hi/README.md +++ b/locales/hi/README.md @@ -185,32 +185,32 @@ Roo Code को बेहतर बनाने में मदद करने |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|elianiva
elianiva
|cannuri
cannuri
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| |feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
|lupuletic
lupuletic
| -|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|olweraltuve
olweraltuve
|jr
jr
|dtrugman
dtrugman
| -|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| |pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|aitoroses
aitoroses
|philfung
philfung
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
| -|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
| -|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
|hassoncs
hassoncs
| -|ChuKhaLi
ChuKhaLi
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
|hongzio
hongzio
| -|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
| -|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|avtc
avtc
| +|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| +|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| +|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| +|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| +|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| +|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| +|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| +|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| |asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
|slytechnical
slytechnical
| -|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
|refactorthis
refactorthis
| -|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
| -|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
|celestial-vault
celestial-vault
| -|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|alasano
alasano
| -|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
| -|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
|kvokka
kvokka
| -|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
|shtse8
shtse8
| -|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | | +|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| +|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| +|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| +|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| +|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| +|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| +|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| +|kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | ## लाइसेंस diff --git a/locales/it/README.md b/locales/it/README.md index afed5abdf8..49d0380c75 100644 --- a/locales/it/README.md +++ b/locales/it/README.md @@ -185,32 +185,32 @@ Grazie a tutti i nostri contributori che hanno aiutato a migliorare Roo Code! |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|elianiva
elianiva
|cannuri
cannuri
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| |feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
|lupuletic
lupuletic
| -|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|olweraltuve
olweraltuve
|jr
jr
|dtrugman
dtrugman
| -|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| |pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|aitoroses
aitoroses
|philfung
philfung
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
| -|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
| -|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
|hassoncs
hassoncs
| -|ChuKhaLi
ChuKhaLi
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
|hongzio
hongzio
| -|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
| -|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|avtc
avtc
| +|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| +|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| +|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| +|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| +|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| +|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| +|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| +|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| |asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
|slytechnical
slytechnical
| -|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
|refactorthis
refactorthis
| -|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
| -|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
|celestial-vault
celestial-vault
| -|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|alasano
alasano
| -|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
| -|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
|kvokka
kvokka
| -|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
|shtse8
shtse8
| -|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | | +|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| +|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| +|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| +|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| +|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| +|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| +|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| +|kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | ## Licenza diff --git a/locales/ja/README.md b/locales/ja/README.md index e81b4ebc8c..d49ef2eafa 100644 --- a/locales/ja/README.md +++ b/locales/ja/README.md @@ -185,32 +185,32 @@ Roo Codeの改善に貢献してくれたすべての貢献者に感謝します |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|elianiva
elianiva
|cannuri
cannuri
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| |feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
|lupuletic
lupuletic
| -|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|olweraltuve
olweraltuve
|jr
jr
|dtrugman
dtrugman
| -|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| |pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|aitoroses
aitoroses
|philfung
philfung
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
| -|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
| -|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
|hassoncs
hassoncs
| -|ChuKhaLi
ChuKhaLi
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
|hongzio
hongzio
| -|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
| -|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|avtc
avtc
| +|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| +|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| +|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| +|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| +|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| +|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| +|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| +|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| |asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
|slytechnical
slytechnical
| -|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
|refactorthis
refactorthis
| -|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
| -|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
|celestial-vault
celestial-vault
| -|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|alasano
alasano
| -|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
| -|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
|kvokka
kvokka
| -|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
|shtse8
shtse8
| -|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | | +|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| +|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| +|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| +|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| +|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| +|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| +|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| +|kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | ## ライセンス diff --git a/locales/ko/README.md b/locales/ko/README.md index 59cefc6062..f3de3e1b78 100644 --- a/locales/ko/README.md +++ b/locales/ko/README.md @@ -185,32 +185,32 @@ Roo Code를 더 좋게 만드는 데 도움을 준 모든 기여자에게 감사 |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|elianiva
elianiva
|cannuri
cannuri
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| |feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
|lupuletic
lupuletic
| -|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|olweraltuve
olweraltuve
|jr
jr
|dtrugman
dtrugman
| -|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| |pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|aitoroses
aitoroses
|philfung
philfung
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
| -|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
| -|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
|hassoncs
hassoncs
| -|ChuKhaLi
ChuKhaLi
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
|hongzio
hongzio
| -|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
| -|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|avtc
avtc
| +|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| +|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| +|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| +|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| +|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| +|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| +|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| +|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| |asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
|slytechnical
slytechnical
| -|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
|refactorthis
refactorthis
| -|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
| -|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
|celestial-vault
celestial-vault
| -|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|alasano
alasano
| -|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
| -|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
|kvokka
kvokka
| -|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
|shtse8
shtse8
| -|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | | +|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| +|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| +|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| +|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| +|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| +|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| +|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| +|kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | ## 라이선스 diff --git a/locales/nl/README.md b/locales/nl/README.md index 14cf9b679a..88a023c1c4 100644 --- a/locales/nl/README.md +++ b/locales/nl/README.md @@ -186,32 +186,32 @@ Dank aan alle bijdragers die Roo Code beter hebben gemaakt! |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|elianiva
elianiva
|cannuri
cannuri
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| |feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
|lupuletic
lupuletic
| -|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|olweraltuve
olweraltuve
|jr
jr
|dtrugman
dtrugman
| -|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| |pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|aitoroses
aitoroses
|philfung
philfung
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
| -|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
| -|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
|hassoncs
hassoncs
| -|ChuKhaLi
ChuKhaLi
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
|hongzio
hongzio
| -|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
| -|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|avtc
avtc
| +|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| +|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| +|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| +|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| +|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| +|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| +|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| +|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| |asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
|slytechnical
slytechnical
| -|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
|refactorthis
refactorthis
| -|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
| -|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
|celestial-vault
celestial-vault
| -|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|alasano
alasano
| -|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
| -|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
|kvokka
kvokka
| -|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
|shtse8
shtse8
| -|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | | +|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| +|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| +|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| +|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| +|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| +|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| +|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| +|kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | ## Licentie diff --git a/locales/pl/README.md b/locales/pl/README.md index 3ad91d31a0..7250673fe4 100644 --- a/locales/pl/README.md +++ b/locales/pl/README.md @@ -185,32 +185,32 @@ Dziękujemy wszystkim naszym współtwórcom, którzy pomogli ulepszyć Roo Code |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|elianiva
elianiva
|cannuri
cannuri
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| |feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
|lupuletic
lupuletic
| -|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|olweraltuve
olweraltuve
|jr
jr
|dtrugman
dtrugman
| -|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| |pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|aitoroses
aitoroses
|philfung
philfung
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
| -|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
| -|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
|hassoncs
hassoncs
| -|ChuKhaLi
ChuKhaLi
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
|hongzio
hongzio
| -|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
| -|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|avtc
avtc
| +|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| +|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| +|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| +|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| +|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| +|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| +|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| +|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| |asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
|slytechnical
slytechnical
| -|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
|refactorthis
refactorthis
| -|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
| -|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
|celestial-vault
celestial-vault
| -|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|alasano
alasano
| -|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
| -|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
|kvokka
kvokka
| -|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
|shtse8
shtse8
| -|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | | +|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| +|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| +|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| +|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| +|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| +|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| +|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| +|kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | ## Licencja diff --git a/locales/pt-BR/README.md b/locales/pt-BR/README.md index e69f499d8f..b135f69cab 100644 --- a/locales/pt-BR/README.md +++ b/locales/pt-BR/README.md @@ -185,32 +185,32 @@ Obrigado a todos os nossos contribuidores que ajudaram a tornar o Roo Code melho |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|elianiva
elianiva
|cannuri
cannuri
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| |feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
|lupuletic
lupuletic
| -|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|olweraltuve
olweraltuve
|jr
jr
|dtrugman
dtrugman
| -|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| |pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|aitoroses
aitoroses
|philfung
philfung
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
| -|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
| -|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
|hassoncs
hassoncs
| -|ChuKhaLi
ChuKhaLi
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
|hongzio
hongzio
| -|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
| -|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|avtc
avtc
| +|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| +|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| +|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| +|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| +|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| +|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| +|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| +|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| |asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
|slytechnical
slytechnical
| -|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
|refactorthis
refactorthis
| -|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
| -|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
|celestial-vault
celestial-vault
| -|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|alasano
alasano
| -|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
| -|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
|kvokka
kvokka
| -|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
|shtse8
shtse8
| -|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | | +|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| +|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| +|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| +|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| +|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| +|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| +|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| +|kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | ## Licença diff --git a/locales/ru/README.md b/locales/ru/README.md index 34a36bb546..3faabf6c8c 100644 --- a/locales/ru/README.md +++ b/locales/ru/README.md @@ -187,32 +187,32 @@ code --install-extension bin/roo-cline-.vsix |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|elianiva
elianiva
|cannuri
cannuri
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| |feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
|lupuletic
lupuletic
| -|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|olweraltuve
olweraltuve
|jr
jr
|dtrugman
dtrugman
| -|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| |pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|aitoroses
aitoroses
|philfung
philfung
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
| -|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
| -|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
|hassoncs
hassoncs
| -|ChuKhaLi
ChuKhaLi
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
|hongzio
hongzio
| -|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
| -|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|avtc
avtc
| +|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| +|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| +|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| +|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| +|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| +|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| +|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| +|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| |asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
|slytechnical
slytechnical
| -|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
|refactorthis
refactorthis
| -|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
| -|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
|celestial-vault
celestial-vault
| -|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|alasano
alasano
| -|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
| -|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
|kvokka
kvokka
| -|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
|shtse8
shtse8
| -|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | | +|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| +|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| +|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| +|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| +|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| +|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| +|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| +|kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | ## Лицензия diff --git a/locales/tr/README.md b/locales/tr/README.md index 1fa99e506a..73ef665214 100644 --- a/locales/tr/README.md +++ b/locales/tr/README.md @@ -185,32 +185,32 @@ Roo Code'u daha iyi hale getirmeye yardımcı olan tüm katkıda bulunanlara te |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|elianiva
elianiva
|cannuri
cannuri
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| |feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
|lupuletic
lupuletic
| -|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|olweraltuve
olweraltuve
|jr
jr
|dtrugman
dtrugman
| -|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| |pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|aitoroses
aitoroses
|philfung
philfung
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
| -|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
| -|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
|hassoncs
hassoncs
| -|ChuKhaLi
ChuKhaLi
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
|hongzio
hongzio
| -|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
| -|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|avtc
avtc
| +|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| +|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| +|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| +|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| +|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| +|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| +|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| +|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| |asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
|slytechnical
slytechnical
| -|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
|refactorthis
refactorthis
| -|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
| -|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
|celestial-vault
celestial-vault
| -|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|alasano
alasano
| -|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
| -|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
|kvokka
kvokka
| -|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
|shtse8
shtse8
| -|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | | +|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| +|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| +|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| +|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| +|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| +|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| +|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| +|kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | ## Lisans diff --git a/locales/vi/README.md b/locales/vi/README.md index 3c0efe26bd..9dc142992b 100644 --- a/locales/vi/README.md +++ b/locales/vi/README.md @@ -185,32 +185,32 @@ Cảm ơn tất cả những người đóng góp đã giúp cải thiện Roo C |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|elianiva
elianiva
|cannuri
cannuri
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| |feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
|lupuletic
lupuletic
| -|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|olweraltuve
olweraltuve
|jr
jr
|dtrugman
dtrugman
| -|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| |pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|aitoroses
aitoroses
|philfung
philfung
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
| -|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
| -|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
|hassoncs
hassoncs
| -|ChuKhaLi
ChuKhaLi
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
|hongzio
hongzio
| -|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
| -|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|avtc
avtc
| +|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| +|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| +|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| +|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| +|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| +|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| +|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| +|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| |asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
|slytechnical
slytechnical
| -|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
|refactorthis
refactorthis
| -|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
| -|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
|celestial-vault
celestial-vault
| -|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|alasano
alasano
| -|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
| -|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
|kvokka
kvokka
| -|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
|shtse8
shtse8
| -|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | | +|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| +|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| +|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| +|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| +|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| +|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| +|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| +|kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | ## Giấy Phép diff --git a/locales/zh-CN/README.md b/locales/zh-CN/README.md index 749ae274b7..58cb5e3536 100644 --- a/locales/zh-CN/README.md +++ b/locales/zh-CN/README.md @@ -185,32 +185,32 @@ code --install-extension bin/roo-cline-.vsix |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|elianiva
elianiva
|cannuri
cannuri
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| |feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
|lupuletic
lupuletic
| -|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|olweraltuve
olweraltuve
|jr
jr
|dtrugman
dtrugman
| -|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| |pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|aitoroses
aitoroses
|philfung
philfung
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
| -|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
| -|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
|hassoncs
hassoncs
| -|ChuKhaLi
ChuKhaLi
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
|hongzio
hongzio
| -|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
| -|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|avtc
avtc
| +|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| +|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| +|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| +|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| +|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| +|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| +|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| +|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| |asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
|slytechnical
slytechnical
| -|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
|refactorthis
refactorthis
| -|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
| -|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
|celestial-vault
celestial-vault
| -|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|alasano
alasano
| -|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
| -|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
|kvokka
kvokka
| -|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
|shtse8
shtse8
| -|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | | +|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| +|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| +|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| +|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| +|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| +|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| +|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| +|kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | ## 许可证 diff --git a/locales/zh-TW/README.md b/locales/zh-TW/README.md index d3bf71db4b..55e2541f02 100644 --- a/locales/zh-TW/README.md +++ b/locales/zh-TW/README.md @@ -186,32 +186,32 @@ code --install-extension bin/roo-cline-.vsix |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|monotykamary
monotykamary
|elianiva
elianiva
|cannuri
cannuri
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| |feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
|lupuletic
lupuletic
| -|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|olweraltuve
olweraltuve
|jr
jr
|dtrugman
dtrugman
| -|nbihan-mediware
nbihan-mediware
|PeterDaveHello
PeterDaveHello
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| +|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| |pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|aitoroses
aitoroses
|philfung
philfung
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
| -|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
| -|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
|hassoncs
hassoncs
| -|ChuKhaLi
ChuKhaLi
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
|hongzio
hongzio
| -|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
|lightrabbit
lightrabbit
| -|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
|avtc
avtc
| +|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| +|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| +|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| +|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| +|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| +|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| +|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| +|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| |asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
|slytechnical
slytechnical
| -|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
|refactorthis
refactorthis
| -|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
| -|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
|celestial-vault
celestial-vault
| -|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
|chadgauth
chadgauth
| -|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
|alasano
alasano
| -|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
| -|NamesMT
NamesMT
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| -|mr-ryan-james
mr-ryan-james
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
|kvokka
kvokka
| -|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
|shtse8
shtse8
| -|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | | +|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| +|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| +|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| +|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| +|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| +|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| +|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| +|kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | ## 授權 From 61a381af559a11d5d7277a729b23deb4360063d1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 25 May 2025 23:02:08 -0400 Subject: [PATCH 010/104] Changeset version bump (#3983) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Matt Rubens --- .changeset/hot-lies-flash.md | 5 ----- .changeset/thick-streets-give.md | 5 ----- .changeset/v3.18.4.md | 12 ------------ CHANGELOG.md | 9 +++++++++ src/package.json | 2 +- 5 files changed, 10 insertions(+), 23 deletions(-) delete mode 100644 .changeset/hot-lies-flash.md delete mode 100644 .changeset/thick-streets-give.md delete mode 100644 .changeset/v3.18.4.md diff --git a/.changeset/hot-lies-flash.md b/.changeset/hot-lies-flash.md deleted file mode 100644 index dcd91e33e1..0000000000 --- a/.changeset/hot-lies-flash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": patch ---- - -Add metadata to create message diff --git a/.changeset/thick-streets-give.md b/.changeset/thick-streets-give.md deleted file mode 100644 index 293ad8583c..0000000000 --- a/.changeset/thick-streets-give.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": minor ---- - -Update `xaiModels` and `xaiDefaultModelId` in `src/shared/api.ts` diff --git a/.changeset/v3.18.4.md b/.changeset/v3.18.4.md deleted file mode 100644 index b6ed94af08..0000000000 --- a/.changeset/v3.18.4.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -"roo-cline": patch ---- - -- Fix settings saving logic to ensure the saved settings are used (thanks @daniel-lxs!) -- Fix handling BOM when user rejects apply_diff (thanks @avtc!) -- Fix wrongfully clearing input on autoApprove (thanks @Ruakij!) -- Fix correct spawnSync parameters for pnpm check in bootstrap.mjs (thanks @ChuKhaLi!) -- Batch code segments when using ollama to report progress (thanks @daniel-lxs!) -- Remove hardcoded line breaks in the about section (thanks @mrubens!) -- Update xAI models and default model ID (thanks @PeterDaveHello!) -- Add metadata to create message (thanks @dtrugman!) diff --git a/CHANGELOG.md b/CHANGELOG.md index 227691297b..4a2bf1195e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Roo Code Changelog +## [3.18.4] - 2025-05-25 + +- Fix codebase indexing settings saving and Ollama indexing (thanks @daniel-lxs!) +- Fix handling BOM when user rejects apply_diff (thanks @avtc!) +- Fix wrongfully clearing input on auto-approve (thanks @Ruakij!) +- Fix correct spawnSync parameters for pnpm check in bootstrap.mjs (thanks @ChuKhaLi!) +- Update xAI models and default model ID (thanks @PeterDaveHello!) +- Add metadata to create message (thanks @dtrugman!) + ## [3.18.3] - 2025-05-24 - Add reasoning support for Claude 4 and Gemini 2.5 Flash on OpenRouter, plus a fix for o1-pro diff --git a/src/package.json b/src/package.json index c98d7f8537..c76298e59a 100644 --- a/src/package.json +++ b/src/package.json @@ -3,7 +3,7 @@ "displayName": "%extension.displayName%", "description": "%extension.description%", "publisher": "RooVeterinaryInc", - "version": "3.18.3", + "version": "3.18.4", "icon": "assets/icons/icon.png", "galleryBanner": { "color": "#617A91", From ce6cc6fa970bd962d26b49ebdbcdccb7e8311407 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Mon, 26 May 2025 14:55:53 -0400 Subject: [PATCH 011/104] Adjust the read_file prompt based on whether partial reads are enabled (#3995) --- .../__snapshots__/system.test.ts.snap | 809 +++++++++++------- .../__tests__/custom-system-prompt.test.ts | 9 + src/core/prompts/__tests__/system.test.ts | 71 ++ src/core/prompts/system.ts | 4 + src/core/prompts/tools/index.ts | 2 + src/core/prompts/tools/read-file.ts | 43 +- src/core/prompts/tools/types.ts | 1 + src/core/task/Task.ts | 8 +- src/core/webview/generateSystemPrompt.ts | 2 + 9 files changed, 615 insertions(+), 334 deletions(-) diff --git a/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap b/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap index 4885b93866..40ee385f58 100644 --- a/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap +++ b/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap @@ -36,16 +36,12 @@ Always use the actual tool name as the XML tag name for proper parsing and execu # Tools ## read_file -Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. By specifying start_line and end_line parameters, you can efficiently read specific portions of large files without loading the entire file into memory. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. +Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Parameters: - path: (required) The path of the file to read (relative to the current workspace directory /test/path) -- start_line: (optional) The starting line number to read from (1-based). If not provided, it starts from the beginning of the file. -- end_line: (optional) The ending line number to read to (1-based, inclusive). If not provided, it reads to the end of the file. Usage: File path here -Starting line number (optional) -Ending line number (optional) Examples: @@ -55,28 +51,6 @@ Examples: frontend-config.json -2. Reading the first 1000 lines of a large log file: - -logs/application.log -1000 - - -3. Reading lines 500-1000 of a CSV file: - -data/large-dataset.csv -500 -1000 - - -4. Reading a specific function in a source file: - -src/app.ts -46 -68 - - -Note: When both start_line and end_line are provided, this tool efficiently streams only the requested lines, making it suitable for processing large files like logs, CSV files, and other large datasets without memory issues. - ## fetch_instructions Description: Request to fetch instructions to perform a task Parameters: @@ -509,16 +483,12 @@ Always use the actual tool name as the XML tag name for proper parsing and execu # Tools ## read_file -Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. By specifying start_line and end_line parameters, you can efficiently read specific portions of large files without loading the entire file into memory. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. +Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Parameters: - path: (required) The path of the file to read (relative to the current workspace directory /test/path) -- start_line: (optional) The starting line number to read from (1-based). If not provided, it starts from the beginning of the file. -- end_line: (optional) The ending line number to read to (1-based, inclusive). If not provided, it reads to the end of the file. Usage: File path here -Starting line number (optional) -Ending line number (optional) Examples: @@ -528,28 +498,6 @@ Examples: frontend-config.json -2. Reading the first 1000 lines of a large log file: - -logs/application.log -1000 - - -3. Reading lines 500-1000 of a CSV file: - -data/large-dataset.csv -500 -1000 - - -4. Reading a specific function in a source file: - -src/app.ts -46 -68 - - -Note: When both start_line and end_line are provided, this tool efficiently streams only the requested lines, making it suitable for processing large files like logs, CSV files, and other large datasets without memory issues. - ## fetch_instructions Description: Request to fetch instructions to perform a task Parameters: @@ -982,16 +930,12 @@ Always use the actual tool name as the XML tag name for proper parsing and execu # Tools ## read_file -Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. By specifying start_line and end_line parameters, you can efficiently read specific portions of large files without loading the entire file into memory. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. +Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Parameters: - path: (required) The path of the file to read (relative to the current workspace directory /test/path) -- start_line: (optional) The starting line number to read from (1-based). If not provided, it starts from the beginning of the file. -- end_line: (optional) The ending line number to read to (1-based, inclusive). If not provided, it reads to the end of the file. Usage: File path here -Starting line number (optional) -Ending line number (optional) Examples: @@ -1001,28 +945,6 @@ Examples: frontend-config.json -2. Reading the first 1000 lines of a large log file: - -logs/application.log -1000 - - -3. Reading lines 500-1000 of a CSV file: - -data/large-dataset.csv -500 -1000 - - -4. Reading a specific function in a source file: - -src/app.ts -46 -68 - - -Note: When both start_line and end_line are provided, this tool efficiently streams only the requested lines, making it suitable for processing large files like logs, CSV files, and other large datasets without memory issues. - ## fetch_instructions Description: Request to fetch instructions to perform a task Parameters: @@ -1455,16 +1377,12 @@ Always use the actual tool name as the XML tag name for proper parsing and execu # Tools ## read_file -Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. By specifying start_line and end_line parameters, you can efficiently read specific portions of large files without loading the entire file into memory. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. +Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Parameters: - path: (required) The path of the file to read (relative to the current workspace directory /test/path) -- start_line: (optional) The starting line number to read from (1-based). If not provided, it starts from the beginning of the file. -- end_line: (optional) The ending line number to read to (1-based, inclusive). If not provided, it reads to the end of the file. Usage: File path here -Starting line number (optional) -Ending line number (optional) Examples: @@ -1474,28 +1392,6 @@ Examples: frontend-config.json -2. Reading the first 1000 lines of a large log file: - -logs/application.log -1000 - - -3. Reading lines 500-1000 of a CSV file: - -data/large-dataset.csv -500 -1000 - - -4. Reading a specific function in a source file: - -src/app.ts -46 -68 - - -Note: When both start_line and end_line are provided, this tool efficiently streams only the requested lines, making it suitable for processing large files like logs, CSV files, and other large datasets without memory issues. - ## fetch_instructions Description: Request to fetch instructions to perform a task Parameters: @@ -1984,16 +1880,12 @@ Always use the actual tool name as the XML tag name for proper parsing and execu # Tools ## read_file -Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. By specifying start_line and end_line parameters, you can efficiently read specific portions of large files without loading the entire file into memory. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. +Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Parameters: - path: (required) The path of the file to read (relative to the current workspace directory /test/path) -- start_line: (optional) The starting line number to read from (1-based). If not provided, it starts from the beginning of the file. -- end_line: (optional) The ending line number to read to (1-based, inclusive). If not provided, it reads to the end of the file. Usage: File path here -Starting line number (optional) -Ending line number (optional) Examples: @@ -2003,28 +1895,6 @@ Examples: frontend-config.json -2. Reading the first 1000 lines of a large log file: - -logs/application.log -1000 - - -3. Reading lines 500-1000 of a CSV file: - -data/large-dataset.csv -500 -1000 - - -4. Reading a specific function in a source file: - -src/app.ts -46 -68 - - -Note: When both start_line and end_line are provided, this tool efficiently streams only the requested lines, making it suitable for processing large files like logs, CSV files, and other large datasets without memory issues. - ## fetch_instructions Description: Request to fetch instructions to perform a task Parameters: @@ -2525,16 +2395,12 @@ Always use the actual tool name as the XML tag name for proper parsing and execu # Tools ## read_file -Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. By specifying start_line and end_line parameters, you can efficiently read specific portions of large files without loading the entire file into memory. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. +Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Parameters: - path: (required) The path of the file to read (relative to the current workspace directory /test/path) -- start_line: (optional) The starting line number to read from (1-based). If not provided, it starts from the beginning of the file. -- end_line: (optional) The ending line number to read to (1-based, inclusive). If not provided, it reads to the end of the file. Usage: File path here -Starting line number (optional) -Ending line number (optional) Examples: @@ -2544,28 +2410,6 @@ Examples: frontend-config.json -2. Reading the first 1000 lines of a large log file: - -logs/application.log -1000 - - -3. Reading lines 500-1000 of a CSV file: - -data/large-dataset.csv -500 -1000 - - -4. Reading a specific function in a source file: - -src/app.ts -46 -68 - - -Note: When both start_line and end_line are provided, this tool efficiently streams only the requested lines, making it suitable for processing large files like logs, CSV files, and other large datasets without memory issues. - ## fetch_instructions Description: Request to fetch instructions to perform a task Parameters: @@ -3054,16 +2898,12 @@ Always use the actual tool name as the XML tag name for proper parsing and execu # Tools ## read_file -Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. By specifying start_line and end_line parameters, you can efficiently read specific portions of large files without loading the entire file into memory. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. +Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Parameters: - path: (required) The path of the file to read (relative to the current workspace directory /test/path) -- start_line: (optional) The starting line number to read from (1-based). If not provided, it starts from the beginning of the file. -- end_line: (optional) The ending line number to read to (1-based, inclusive). If not provided, it reads to the end of the file. Usage: File path here -Starting line number (optional) -Ending line number (optional) Examples: @@ -3073,28 +2913,6 @@ Examples: frontend-config.json -2. Reading the first 1000 lines of a large log file: - -logs/application.log -1000 - - -3. Reading lines 500-1000 of a CSV file: - -data/large-dataset.csv -500 -1000 - - -4. Reading a specific function in a source file: - -src/app.ts -46 -68 - - -Note: When both start_line and end_line are provided, this tool efficiently streams only the requested lines, making it suitable for processing large files like logs, CSV files, and other large datasets without memory issues. - ## fetch_instructions Description: Request to fetch instructions to perform a task Parameters: @@ -3615,16 +3433,12 @@ Always use the actual tool name as the XML tag name for proper parsing and execu # Tools ## read_file -Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. By specifying start_line and end_line parameters, you can efficiently read specific portions of large files without loading the entire file into memory. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. +Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Parameters: - path: (required) The path of the file to read (relative to the current workspace directory /test/path) -- start_line: (optional) The starting line number to read from (1-based). If not provided, it starts from the beginning of the file. -- end_line: (optional) The ending line number to read to (1-based, inclusive). If not provided, it reads to the end of the file. Usage: File path here -Starting line number (optional) -Ending line number (optional) Examples: @@ -3634,28 +3448,6 @@ Examples: frontend-config.json -2. Reading the first 1000 lines of a large log file: - -logs/application.log -1000 - - -3. Reading lines 500-1000 of a CSV file: - -data/large-dataset.csv -500 -1000 - - -4. Reading a specific function in a source file: - -src/app.ts -46 -68 - - -Note: When both start_line and end_line are provided, this tool efficiently streams only the requested lines, making it suitable for processing large files like logs, CSV files, and other large datasets without memory issues. - ## fetch_instructions Description: Request to fetch instructions to perform a task Parameters: @@ -4130,16 +3922,12 @@ Always use the actual tool name as the XML tag name for proper parsing and execu # Tools ## read_file -Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. By specifying start_line and end_line parameters, you can efficiently read specific portions of large files without loading the entire file into memory. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. +Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Parameters: - path: (required) The path of the file to read (relative to the current workspace directory /test/path) -- start_line: (optional) The starting line number to read from (1-based). If not provided, it starts from the beginning of the file. -- end_line: (optional) The ending line number to read to (1-based, inclusive). If not provided, it reads to the end of the file. Usage: File path here -Starting line number (optional) -Ending line number (optional) Examples: @@ -4149,28 +3937,6 @@ Examples: frontend-config.json -2. Reading the first 1000 lines of a large log file: - -logs/application.log -1000 - - -3. Reading lines 500-1000 of a CSV file: - -data/large-dataset.csv -500 -1000 - - -4. Reading a specific function in a source file: - -src/app.ts -46 -68 - - -Note: When both start_line and end_line are provided, this tool efficiently streams only the requested lines, making it suitable for processing large files like logs, CSV files, and other large datasets without memory issues. - ## fetch_instructions Description: Request to fetch instructions to perform a task Parameters: @@ -4680,16 +4446,12 @@ Always use the actual tool name as the XML tag name for proper parsing and execu # Tools ## read_file -Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. By specifying start_line and end_line parameters, you can efficiently read specific portions of large files without loading the entire file into memory. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. +Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Parameters: - path: (required) The path of the file to read (relative to the current workspace directory /test/path) -- start_line: (optional) The starting line number to read from (1-based). If not provided, it starts from the beginning of the file. -- end_line: (optional) The ending line number to read to (1-based, inclusive). If not provided, it reads to the end of the file. Usage: File path here -Starting line number (optional) -Ending line number (optional) Examples: @@ -4699,28 +4461,6 @@ Examples: frontend-config.json -2. Reading the first 1000 lines of a large log file: - -logs/application.log -1000 - - -3. Reading lines 500-1000 of a CSV file: - -data/large-dataset.csv -500 -1000 - - -4. Reading a specific function in a source file: - -src/app.ts -46 -68 - - -Note: When both start_line and end_line are provided, this tool efficiently streams only the requested lines, making it suitable for processing large files like logs, CSV files, and other large datasets without memory issues. - ## fetch_instructions Description: Request to fetch instructions to perform a task Parameters: @@ -5144,16 +4884,12 @@ Always use the actual tool name as the XML tag name for proper parsing and execu # Tools ## read_file -Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. By specifying start_line and end_line parameters, you can efficiently read specific portions of large files without loading the entire file into memory. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. +Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Parameters: - path: (required) The path of the file to read (relative to the current workspace directory /test/path) -- start_line: (optional) The starting line number to read from (1-based). If not provided, it starts from the beginning of the file. -- end_line: (optional) The ending line number to read to (1-based, inclusive). If not provided, it reads to the end of the file. Usage: File path here -Starting line number (optional) -Ending line number (optional) Examples: @@ -5163,28 +4899,6 @@ Examples: frontend-config.json -2. Reading the first 1000 lines of a large log file: - -logs/application.log -1000 - - -3. Reading lines 500-1000 of a CSV file: - -data/large-dataset.csv -500 -1000 - - -4. Reading a specific function in a source file: - -src/app.ts -46 -68 - - -Note: When both start_line and end_line are provided, this tool efficiently streams only the requested lines, making it suitable for processing large files like logs, CSV files, and other large datasets without memory issues. - ## fetch_instructions Description: Request to fetch instructions to perform a task Parameters: @@ -5525,16 +5239,12 @@ Always use the actual tool name as the XML tag name for proper parsing and execu # Tools ## read_file -Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. By specifying start_line and end_line parameters, you can efficiently read specific portions of large files without loading the entire file into memory. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. +Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Parameters: - path: (required) The path of the file to read (relative to the current workspace directory /test/path) -- start_line: (optional) The starting line number to read from (1-based). If not provided, it starts from the beginning of the file. -- end_line: (optional) The ending line number to read to (1-based, inclusive). If not provided, it reads to the end of the file. Usage: File path here -Starting line number (optional) -Ending line number (optional) Examples: @@ -5544,28 +5254,6 @@ Examples: frontend-config.json -2. Reading the first 1000 lines of a large log file: - -logs/application.log -1000 - - -3. Reading lines 500-1000 of a CSV file: - -data/large-dataset.csv -500 -1000 - - -4. Reading a specific function in a source file: - -src/app.ts -46 -68 - - -Note: When both start_line and end_line are provided, this tool efficiently streams only the requested lines, making it suitable for processing large files like logs, CSV files, and other large datasets without memory issues. - ## fetch_instructions Description: Request to fetch instructions to perform a task Parameters: @@ -6048,6 +5736,479 @@ Mock mode-specific rules Mock generic rules" `; +exports[`addCustomInstructions should include partial read instructions when partialReadsEnabled is true 1`] = ` +"You are Roo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices. + +==== + +MARKDOWN RULES + +ALL responses MUST show ANY \`language construct\` OR filename reference as clickable, exactly as [\`filename OR language.declaration()\`](relative/file/path.ext:line); line is required for \`syntax\` and optional for filename links. This applies to ALL markdown responses and ALSO those in + +==== + +TOOL USE + +You have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use. + +# Tool Use Formatting + +Tool uses are formatted using XML-style tags. The tool name itself becomes the XML tag name. Each parameter is enclosed within its own set of tags. Here's the structure: + + +value1 +value2 +... + + +For example, to use the read_file tool: + + +src/main.js + + +Always use the actual tool name as the XML tag name for proper parsing and execution. + +# Tools + +## read_file +Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. By specifying start_line and end_line parameters, you can efficiently read specific portions of large files without loading the entire file into memory. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. +Parameters: +- path: (required) The path of the file to read (relative to the current workspace directory /test/path) +- start_line: (optional) The starting line number to read from (1-based). If not provided, it starts from the beginning of the file. +- end_line: (optional) The ending line number to read to (1-based, inclusive). If not provided, it reads to the end of the file. +Usage: + +File path here +Starting line number (optional) +Ending line number (optional) + + +Examples: + +1. Reading an entire file: + +frontend-config.json + + +2. Reading the first 1000 lines of a large log file: + +logs/application.log +1000 + + +3. Reading lines 500-1000 of a CSV file: + +data/large-dataset.csv +500 +1000 + + +4. Reading a specific function in a source file: + +src/app.ts +46 +68 + + +Note: When both start_line and end_line are provided, this tool efficiently streams only the requested lines, making it suitable for processing large files like logs, CSV files, and other large datasets without memory issues. + +## fetch_instructions +Description: Request to fetch instructions to perform a task +Parameters: +- task: (required) The task to get instructions for. This can take the following values: + create_mcp_server + create_mode + +Example: Requesting instructions to create an MCP Server + + +create_mcp_server + + +## search_files +Description: Request to perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context. +Parameters: +- path: (required) The path of the directory to search in (relative to the current workspace directory /test/path). This directory will be recursively searched. +- regex: (required) The regular expression pattern to search for. Uses Rust regex syntax. +- file_pattern: (optional) Glob pattern to filter files (e.g., '*.ts' for TypeScript files). If not provided, it will search all files (*). +Usage: + +Directory path here +Your regex pattern here +file pattern here (optional) + + +Example: Requesting to search for all .ts files in the current directory + +. +.* +*.ts + + +## list_files +Description: Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not. +Parameters: +- path: (required) The path of the directory to list contents for (relative to the current workspace directory /test/path) +- recursive: (optional) Whether to list files recursively. Use true for recursive listing, false or omit for top-level only. +Usage: + +Directory path here +true or false (optional) + + +Example: Requesting to list all files in the current directory + +. +false + + +## list_code_definition_names +Description: Request to list definition names (classes, functions, methods, etc.) from source code. This tool can analyze either a single file or all files at the top level of a specified directory. It provides insights into the codebase structure and important constructs, encapsulating high-level concepts and relationships that are crucial for understanding the overall architecture. +Parameters: +- path: (required) The path of the file or directory (relative to the current working directory /test/path) to analyze. When given a directory, it lists definitions from all top-level source files. +Usage: + +Directory path here + + +Examples: + +1. List definitions from a specific file: + +src/main.ts + + +2. List definitions from all files in a directory: + +src/ + + +## write_to_file +Description: Request to write content to a file. This tool is primarily used for **creating new files** or for scenarios where a **complete rewrite of an existing file is intentionally required**. If the file exists, it will be overwritten. If it doesn't exist, it will be created. This tool will automatically create any directories needed to write the file. +Parameters: +- path: (required) The path of the file to write to (relative to the current workspace directory /test/path) +- content: (required) The content to write to the file. When performing a full rewrite of an existing file or creating a new one, ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file. +- line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing. +Usage: + +File path here + +Your file content here + +total number of lines in the file, including empty lines + + +Example: Requesting to write to frontend-config.json + +frontend-config.json + +{ + "apiEndpoint": "https://api.example.com", + "theme": { + "primaryColor": "#007bff", + "secondaryColor": "#6c757d", + "fontFamily": "Arial, sans-serif" + }, + "features": { + "darkMode": true, + "notifications": true, + "analytics": false + }, + "version": "1.0.0" +} + +14 + + +## insert_content +Description: Use this tool specifically for adding new lines of content into a file without modifying existing content. Specify the line number to insert before, or use line 0 to append to the end. Ideal for adding imports, functions, configuration blocks, log entries, or any multi-line text block. + +Parameters: +- path: (required) File path relative to workspace directory /test/path +- line: (required) Line number where content will be inserted (1-based) + Use 0 to append at end of file + Use any positive number to insert before that line +- content: (required) The content to insert at the specified line + +Example for inserting imports at start of file: + +src/utils.ts +1 + +// Add imports at start of file +import { sum } from './math'; + + + +Example for appending to the end of file: + +src/utils.ts +0 + +// This is the end of the file + + + + +## search_and_replace +Description: Use this tool to find and replace specific text strings or patterns (using regex) within a file. It's suitable for targeted replacements across multiple locations within the file. Supports literal text and regex patterns, case sensitivity options, and optional line ranges. Shows a diff preview before applying changes. + +Required Parameters: +- path: The path of the file to modify (relative to the current workspace directory /test/path) +- search: The text or pattern to search for +- replace: The text to replace matches with + +Optional Parameters: +- start_line: Starting line number for restricted replacement (1-based) +- end_line: Ending line number for restricted replacement (1-based) +- use_regex: Set to "true" to treat search as a regex pattern (default: false) +- ignore_case: Set to "true" to ignore case when matching (default: false) + +Notes: +- When use_regex is true, the search parameter is treated as a regular expression pattern +- When ignore_case is true, the search is case-insensitive regardless of regex mode + +Examples: + +1. Simple text replacement: + +example.ts +oldText +newText + + +2. Case-insensitive regex pattern: + +example.ts +oldw+ +new$& +true +true + + +## execute_command +Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Prefer relative commands and paths that avoid location sensitivity for terminal consistency, e.g: \`touch ./testdata/example.file\`, \`dir ./examples/model1/data/yaml\`, or \`go test ./cmd/front --config ./cmd/front/config.yml\`. If directed by the user, you may open a terminal in a different directory by using the \`cwd\` parameter. +Parameters: +- command: (required) The CLI command to execute. This should be valid for the current operating system. Ensure the command is properly formatted and does not contain any harmful instructions. +- cwd: (optional) The working directory to execute the command in (default: /test/path) +Usage: + +Your command here +Working directory path (optional) + + +Example: Requesting to execute npm run dev + +npm run dev + + +Example: Requesting to execute ls in a specific directory if directed + +ls -la +/home/user/projects + + +## ask_followup_question +Description: Ask the user a question to gather additional information needed to complete the task. This tool should be used when you encounter ambiguities, need clarification, or require more details to proceed effectively. It allows for interactive problem-solving by enabling direct communication with the user. Use this tool judiciously to maintain a balance between gathering necessary information and avoiding excessive back-and-forth. +Parameters: +- question: (required) The question to ask the user. This should be a clear, specific question that addresses the information you need. +- follow_up: (required) A list of 2-4 suggested answers that logically follow from the question, ordered by priority or logical sequence. Each suggestion must: + 1. Be provided in its own tag + 2. Be specific, actionable, and directly related to the completed task + 3. Be a complete answer to the question - the user should not need to provide additional information or fill in any missing details. DO NOT include placeholders with brackets or parentheses. +Usage: + +Your question here + + +Your suggested answer here + + + + +Example: Requesting to ask the user for the path to the frontend-config.json file + +What is the path to the frontend-config.json file? + +./src/frontend-config.json +./config/frontend-config.json +./frontend-config.json + + + +## attempt_completion +Description: After each tool use, the user will respond with the result of that tool use, i.e. if it succeeded or failed, along with any reasons for failure. Once you've received the results of tool uses and can confirm that the task is complete, use this tool to present the result of your work to the user. Optionally you may provide a CLI command to showcase the result of your work. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again. +IMPORTANT NOTE: This tool CANNOT be used until you've confirmed from the user that any previous tool uses were successful. Failure to do so will result in code corruption and system failure. Before using this tool, you must ask yourself in tags if you've confirmed from the user that any previous tool uses were successful. If not, then DO NOT use this tool. +Parameters: +- result: (required) The result of the task. Formulate this result in a way that is final and does not require further input from the user. Don't end your result with questions or offers for further assistance. +- command: (optional) A CLI command to execute to show a live demo of the result to the user. For example, use \`open index.html\` to display a created html website, or \`open localhost:3000\` to display a locally running development server. But DO NOT use commands like \`echo\` or \`cat\` that merely print text. This command should be valid for the current operating system. Ensure the command is properly formatted and does not contain any harmful instructions. +Usage: + + +Your final result description here + +Command to demonstrate result (optional) + + +Example: Requesting to attempt completion with a result and command + + +I've updated the CSS + +open index.html + + +## switch_mode +Description: Request to switch to a different mode. This tool allows modes to request switching to another mode when needed, such as switching to Code mode to make code changes. The user must approve the mode switch. +Parameters: +- mode_slug: (required) The slug of the mode to switch to (e.g., "code", "ask", "architect") +- reason: (optional) The reason for switching modes +Usage: + +Mode slug here +Reason for switching here + + +Example: Requesting to switch to code mode + +code +Need to make code changes + + +## new_task +Description: This will let you create a new task instance in the chosen mode using your provided message. + +Parameters: +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect"). +- message: (required) The initial user message or instructions for this new task. + +Usage: + +your-mode-slug-here +Your initial instructions here + + +Example: + +code +Implement a new feature for the application. + + + +# Tool Use Guidelines + +1. In tags, assess what information you already have and what information you need to proceed with the task. +2. Choose the most appropriate tool based on the task and the tool descriptions provided. Assess if you need additional information to proceed, and which of the available tools would be most effective for gathering this information. For example using the list_files tool is more effective than running a command like \`ls\` in the terminal. It's critical that you think about each available tool and use the one that best fits the current step in the task. +3. If multiple actions are needed, use one tool at a time per message to accomplish the task iteratively, with each tool use being informed by the result of the previous tool use. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result. +4. Formulate your tool use using the XML format specified for each tool. +5. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include: + - Information about whether the tool succeeded or failed, along with any reasons for failure. + - Linter errors that may have arisen due to the changes you made, which you'll need to address. + - New terminal output in reaction to the changes, which you may need to consider or act upon. + - Any other relevant feedback or information related to the tool use. +6. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user. + +It is crucial to proceed step-by-step, waiting for the user's message after each tool use before moving forward with the task. This approach allows you to: +1. Confirm the success of each step before proceeding. +2. Address any issues or errors that arise immediately. +3. Adapt your approach based on new information or unexpected results. +4. Ensure that each action builds correctly on the previous ones. + +By waiting for and carefully considering the user's response after each tool use, you can react accordingly and make informed decisions about how to proceed with the task. This iterative process helps ensure the overall success and accuracy of your work. + + + +==== + +CAPABILITIES + +- You have access to tools that let you execute CLI commands on the user's computer, list files, view source code definitions, regex search, read and write files, and ask follow-up questions. These tools help you effectively accomplish a wide range of tasks, such as writing code, making edits or improvements to existing files, understanding the current state of a project, performing system operations, and much more. +- When the user initially gives you a task, a recursive list of all filepaths in the current workspace directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current workspace directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. +- You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring. +- You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task. + - For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the write_to_file tool to apply the changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed. +- You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance. + +==== + +MODES + +- Test modes section + +==== + +RULES + +- The project base directory is: /test/path +- All file paths must be relative to this directory. However, commands may change directories in terminals, so respect working directory specified by the response to . +- You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '/test/path', so be sure to pass in the correct 'path' parameter when using tools that require a path. +- Do not use the ~ character or $HOME to refer to the home directory. +- Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '/test/path', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '/test/path'). For example, if you needed to run \`npm install\` in a project outside of '/test/path', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`. +- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using write_to_file to make informed changes. +- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when writing files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser. +- For editing files, you have access to these tools: write_to_file (for creating new files or complete file rewrites), insert_content (for adding lines to existing files), search_and_replace (for finding and replacing individual pieces of text). +- The insert_content tool adds lines of text to files at a specific line number, such as adding a new function to a JavaScript file or inserting a new route in a Python file. Use line number 0 to append at the end of the file, or any positive number to insert before that line. +- The search_and_replace tool finds and replaces text or regex in files. This tool allows you to search for a specific regex pattern or text and replace it with another value. Be cautious when using this tool to ensure you are replacing the correct text. It can support multiple operations at once. +- You should always prefer using other editing tools over write_to_file when making changes to existing files since write_to_file is much slower and cannot handle large files. +- When using the write_to_file tool to modify a file, use the tool directly with the desired content. You do not need to display the content before using the tool. ALWAYS provide the COMPLETE file content in your response. This is NON-NEGOTIABLE. Partial updates or placeholders like '// rest of code unchanged' are STRICTLY FORBIDDEN. You MUST include ALL parts of the file, even if they haven't been modified. Failure to do so will result in incomplete or broken code, severely impacting the user's project. +- Some modes have restrictions on which files they can edit. If you attempt to edit a restricted file, the operation will be rejected with a FileRestrictionError that will specify which file patterns are allowed for the current mode. +- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write. + * For example, in architect mode trying to edit app.js would be rejected because architect mode can only edit files matching "\\.md$" +- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices. +- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively. When you've completed your task, you must use the attempt_completion tool to present the result to the user. The user may provide feedback, which you can use to make improvements and try again. +- You are only allowed to ask the user questions using the ask_followup_question tool. Use this tool only when you need additional details to complete a task, and be sure to use a clear and concise question that will help you move forward with the task. When you ask a question, provide the user with 2-4 suggested answers based on your question so they don't need to do so much typing. The suggestions should be specific, actionable, and directly related to the completed task. They should be ordered by priority or logical sequence. However if you can use the available tools to avoid having to ask the user questions, you should do so. For example, if the user mentions a file that may be in an outside directory like the Desktop, you should use the list_files tool to list the files in the Desktop and check if the file they are talking about is there, rather than asking the user to provide the file path themselves. +- When executing commands, if you don't see the expected output, assume the terminal executed the command successfully and proceed with the task. The user's terminal may be unable to stream the output back properly. If you absolutely need to see the actual terminal output, use the ask_followup_question tool to request the user to copy and paste it back to you. +- The user may provide a file's contents directly in their message, in which case you shouldn't use the read_file tool to get the file contents again since you already have it. +- Your goal is to try to accomplish the user's task, NOT engage in a back and forth conversation. +- NEVER end attempt_completion result with a question or request to engage in further conversation! Formulate the end of your result in a way that is final and does not require further input from the user. +- You are STRICTLY FORBIDDEN from starting your messages with "Great", "Certainly", "Okay", "Sure". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say "Great, I've updated the CSS" but instead something like "I've updated the CSS". It is important you be clear and technical in your messages. +- When presented with images, utilize your vision capabilities to thoroughly examine them and extract meaningful information. Incorporate these insights into your thought process as you accomplish the user's task. +- At the end of each user message, you will automatically receive environment_details. This information is not written by the user themselves, but is auto-generated to provide potentially relevant context about the project structure and environment. While this information can be valuable for understanding the project context, do not treat it as a direct part of the user's request or response. Use it to inform your actions and decisions, but don't assume the user is explicitly asking about or referring to this information unless they clearly do so in their message. When using environment_details, explain your actions clearly to ensure the user understands, as they may not be aware of these details. +- Before executing commands, check the "Actively Running Terminals" section in environment_details. If present, consider how these active processes might impact your task. For example, if a local development server is already running, you wouldn't need to start it again. If no active terminals are listed, proceed with command execution as normal. +- MCP operations should be used one at a time, similar to other tool usage. Wait for confirmation of success before proceeding with additional operations. +- It is critical you wait for the user's response after each tool use, in order to confirm the success of the tool use. For example, if asked to make a todo app, you would create a file, wait for the user's response it was created successfully, then create another file if needed, wait for the user's response it was created successfully, etc. + +==== + +SYSTEM INFORMATION + +Operating System: Linux +Default Shell: /bin/zsh +Home Directory: /home/user +Current Workspace Directory: /test/path + +The Current Workspace Directory is the active VS Code project directory, and is therefore the default directory for all tool operations. New terminals will be created in the current workspace directory, however if you change directories in a terminal it will then have a different working directory; changing directories in a terminal does not modify the workspace directory, because you do not have access to change the workspace directory. When the user initially gives you a task, a recursive list of all filepaths in the current workspace directory ('/test/path') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current workspace directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop. + +==== + +OBJECTIVE + +You accomplish a given task iteratively, breaking it down into clear steps and working through them methodically. + +1. Analyze the user's task and set clear, achievable goals to accomplish it. Prioritize these goals in a logical order. +2. Work through these goals sequentially, utilizing available tools one at a time as necessary. Each goal should correspond to a distinct step in your problem-solving process. You will be informed on the work completed and what's remaining as you go. +3. Remember, you have extensive capabilities with access to a wide range of tools that can be used in powerful and clever ways as necessary to accomplish each goal. Before calling a tool, do some analysis within tags. First, analyze the file structure provided in environment_details to gain context and insights for proceeding effectively. Then, think about which of the provided tools is the most relevant tool to accomplish the user's task. Next, go through each of the required parameters of the relevant tool and determine if the user has directly provided or given enough information to infer a value. When deciding if the parameter can be inferred, carefully consider all the context to see if it supports a specific value. If all of the required parameters are present or can be reasonably inferred, close the thinking tag and proceed with the tool use. BUT, if one of the values for a required parameter is missing, DO NOT invoke the tool (not even with fillers for the missing params) and instead, ask the user to provide the missing parameters using the ask_followup_question tool. DO NOT ask for more information on optional parameters if it is not provided. +4. Once you've completed the user's task, you must use the attempt_completion tool to present the result of the task to the user. You may also provide a CLI command to showcase the result of your task; this can be particularly useful for web development tasks, where you can run e.g. \`open index.html\` to show the website you've built. +5. The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance. + + +==== + +USER'S CUSTOM INSTRUCTIONS + +The following additional instructions are provided by the user, and should be followed to the best of your ability without interfering with the TOOL USE guidelines. + +Language Preference: +You should always speak and think in the "en" language. + +Rules: +# Rules from .clinerules-code: +Mock mode-specific rules +# Rules from .clinerules: +Mock generic rules" +`; + exports[`addCustomInstructions should include preferred language when provided 1`] = ` " ==== diff --git a/src/core/prompts/__tests__/custom-system-prompt.test.ts b/src/core/prompts/__tests__/custom-system-prompt.test.ts index 977ab051a0..e7d1ae08d7 100644 --- a/src/core/prompts/__tests__/custom-system-prompt.test.ts +++ b/src/core/prompts/__tests__/custom-system-prompt.test.ts @@ -76,6 +76,9 @@ describe("File-Based Custom System Prompt", () => { undefined, // diffEnabled undefined, // experiments true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled ) // Should contain default sections @@ -110,6 +113,9 @@ describe("File-Based Custom System Prompt", () => { undefined, // diffEnabled undefined, // experiments true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled ) // Should contain role definition and file-based system prompt @@ -153,6 +159,9 @@ describe("File-Based Custom System Prompt", () => { undefined, // diffEnabled undefined, // experiments true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled ) // Should contain custom role definition and file-based system prompt diff --git a/src/core/prompts/__tests__/system.test.ts b/src/core/prompts/__tests__/system.test.ts index 3647d2d859..015ef43c01 100644 --- a/src/core/prompts/__tests__/system.test.ts +++ b/src/core/prompts/__tests__/system.test.ts @@ -211,6 +211,9 @@ describe("SYSTEM_PROMPT", () => { undefined, // diffEnabled experiments, true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled ) expect(prompt).toMatchSnapshot() @@ -231,6 +234,9 @@ describe("SYSTEM_PROMPT", () => { undefined, // diffEnabled experiments, true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled ) expect(prompt).toMatchSnapshot() @@ -253,6 +259,9 @@ describe("SYSTEM_PROMPT", () => { undefined, // diffEnabled experiments, true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled ) expect(prompt).toMatchSnapshot() @@ -273,6 +282,9 @@ describe("SYSTEM_PROMPT", () => { undefined, // diffEnabled experiments, true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled ) expect(prompt).toMatchSnapshot() @@ -293,6 +305,9 @@ describe("SYSTEM_PROMPT", () => { undefined, // diffEnabled experiments, true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled ) expect(prompt).toMatchSnapshot() @@ -313,6 +328,9 @@ describe("SYSTEM_PROMPT", () => { true, // diffEnabled experiments, true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled ) expect(prompt).toContain("apply_diff") @@ -334,6 +352,9 @@ describe("SYSTEM_PROMPT", () => { false, // diffEnabled experiments, true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled ) expect(prompt).not.toContain("apply_diff") @@ -355,6 +376,9 @@ describe("SYSTEM_PROMPT", () => { undefined, // diffEnabled experiments, true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled ) expect(prompt).not.toContain("apply_diff") @@ -403,6 +427,9 @@ describe("SYSTEM_PROMPT", () => { undefined, // diffEnabled undefined, // experiments true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled ) expect(prompt).toContain("Language Preference:") @@ -461,6 +488,9 @@ describe("SYSTEM_PROMPT", () => { undefined, // diffEnabled experiments, true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled ) // Role definition should be at the top @@ -496,6 +526,9 @@ describe("SYSTEM_PROMPT", () => { undefined, // diffEnabled undefined, // experiments false, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled ) // Role definition from promptComponent should be at the top @@ -526,6 +559,9 @@ describe("SYSTEM_PROMPT", () => { undefined, // diffEnabled undefined, // experiments false, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled ) // Should use the default mode's role definition @@ -570,6 +606,9 @@ describe("addCustomInstructions", () => { undefined, // diffEnabled undefined, // experiments true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled ) expect(prompt).toMatchSnapshot() @@ -590,6 +629,9 @@ describe("addCustomInstructions", () => { undefined, // diffEnabled undefined, // experiments true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled ) expect(prompt).toMatchSnapshot() @@ -612,6 +654,9 @@ describe("addCustomInstructions", () => { undefined, // diffEnabled undefined, // experiments true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled ) expect(prompt).toContain("Creating an MCP Server") @@ -635,12 +680,38 @@ describe("addCustomInstructions", () => { undefined, // diffEnabled undefined, // experiments false, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + undefined, // partialReadsEnabled ) expect(prompt).not.toContain("Creating an MCP Server") expect(prompt).toMatchSnapshot() }) + it("should include partial read instructions when partialReadsEnabled is true", async () => { + const prompt = await SYSTEM_PROMPT( + mockContext, + "/test/path", + false, // supportsComputerUse + undefined, // mcpHub + undefined, // diffStrategy + undefined, // browserViewportSize + defaultModeSlug, // mode + undefined, // customModePrompts + undefined, // customModes, + undefined, // globalCustomInstructions + undefined, // diffEnabled + undefined, // experiments + true, // enableMcpServerCreation + undefined, // language + undefined, // rooIgnoreInstructions + true, // partialReadsEnabled + ) + + expect(prompt).toMatchSnapshot() + }) + it("should prioritize mode-specific rules for code mode", async () => { const instructions = await addCustomInstructions("", "", "/test/path", defaultModeSlug) expect(instructions).toMatchSnapshot() diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index 96221ae91f..7a4d152ef9 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -45,6 +45,7 @@ async function generatePrompt( enableMcpServerCreation?: boolean, language?: string, rooIgnoreInstructions?: string, + partialReadsEnabled?: boolean, ): Promise { if (!context) { throw new Error("Extension context is required for generating system prompt") @@ -82,6 +83,7 @@ ${getToolDescriptionsForMode( mcpHub, customModeConfigs, experiments, + partialReadsEnabled, )} ${getToolUseGuidelinesSection()} @@ -119,6 +121,7 @@ export const SYSTEM_PROMPT = async ( enableMcpServerCreation?: boolean, language?: string, rooIgnoreInstructions?: string, + partialReadsEnabled?: boolean, ): Promise => { if (!context) { throw new Error("Extension context is required for generating system prompt") @@ -185,5 +188,6 @@ ${customInstructions}` enableMcpServerCreation, language, rooIgnoreInstructions, + partialReadsEnabled, ) } diff --git a/src/core/prompts/tools/index.ts b/src/core/prompts/tools/index.ts index 4b3f796919..675fc8f524 100644 --- a/src/core/prompts/tools/index.ts +++ b/src/core/prompts/tools/index.ts @@ -56,6 +56,7 @@ export function getToolDescriptionsForMode( mcpHub?: McpHub, customModes?: ModeConfig[], experiments?: Record, + partialReadsEnabled?: boolean, ): string { const config = getModeConfig(mode, customModes) const args: ToolArgs = { @@ -64,6 +65,7 @@ export function getToolDescriptionsForMode( diffStrategy, browserViewportSize, mcpHub, + partialReadsEnabled, } const tools = new Set() diff --git a/src/core/prompts/tools/read-file.ts b/src/core/prompts/tools/read-file.ts index 3c90a89fa8..9581f68bce 100644 --- a/src/core/prompts/tools/read-file.ts +++ b/src/core/prompts/tools/read-file.ts @@ -1,17 +1,39 @@ import { ToolArgs } from "./types" export function getReadFileDescription(args: ToolArgs): string { - return `## read_file -Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code. By specifying start_line and end_line parameters, you can efficiently read specific portions of large files without loading the entire file into memory. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. + // Base description without partial read instructions + let description = `## read_file +Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code.` + + // Add partial read instructions only when partial reads are active + if (args.partialReadsEnabled) { + description += ` By specifying start_line and end_line parameters, you can efficiently read specific portions of large files without loading the entire file into memory.` + } + + description += ` Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string. Parameters: -- path: (required) The path of the file to read (relative to the current workspace directory ${args.cwd}) +- path: (required) The path of the file to read (relative to the current workspace directory ${args.cwd})` + + // Add start_line and end_line parameters only when partial reads are active + if (args.partialReadsEnabled) { + description += ` - start_line: (optional) The starting line number to read from (1-based). If not provided, it starts from the beginning of the file. -- end_line: (optional) The ending line number to read to (1-based, inclusive). If not provided, it reads to the end of the file. +- end_line: (optional) The ending line number to read to (1-based, inclusive). If not provided, it reads to the end of the file.` + } + + description += ` Usage: -File path here +File path here` + + // Add start_line and end_line in usage only when partial reads are active + if (args.partialReadsEnabled) { + description += ` Starting line number (optional) -Ending line number (optional) +Ending line number (optional)` + } + + description += ` Examples: @@ -19,7 +41,11 @@ Examples: 1. Reading an entire file: frontend-config.json - +` + + // Add partial read examples only when partial reads are active + if (args.partialReadsEnabled) { + description += ` 2. Reading the first 1000 lines of a large log file: @@ -42,4 +68,7 @@ Examples: Note: When both start_line and end_line are provided, this tool efficiently streams only the requested lines, making it suitable for processing large files like logs, CSV files, and other large datasets without memory issues.` + } + + return description } diff --git a/src/core/prompts/tools/types.ts b/src/core/prompts/tools/types.ts index f2b890abdf..ec78c6cf1a 100644 --- a/src/core/prompts/tools/types.ts +++ b/src/core/prompts/tools/types.ts @@ -8,4 +8,5 @@ export type ToolArgs = { browserViewportSize?: string mcpHub?: McpHub toolOptions?: any + partialReadsEnabled?: boolean } diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 231a6049ad..adbf871d73 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -1451,18 +1451,19 @@ export class Task extends EventEmitter { const rooIgnoreInstructions = this.rooIgnoreController?.getInstructions() + const state = await this.providerRef.deref()?.getState() const { browserViewportSize, mode, + customModes, customModePrompts, customInstructions, experiments, enableMcpServerCreation, browserToolEnabled, language, - } = (await this.providerRef.deref()?.getState()) ?? {} - - const { customModes } = (await this.providerRef.deref()?.getState()) ?? {} + maxReadFileLine, + } = state ?? {} return await (async () => { const provider = this.providerRef.deref() @@ -1487,6 +1488,7 @@ export class Task extends EventEmitter { enableMcpServerCreation, language, rooIgnoreInstructions, + maxReadFileLine !== -1, ) })() } diff --git a/src/core/webview/generateSystemPrompt.ts b/src/core/webview/generateSystemPrompt.ts index f676fa18f6..1a64e2291c 100644 --- a/src/core/webview/generateSystemPrompt.ts +++ b/src/core/webview/generateSystemPrompt.ts @@ -20,6 +20,7 @@ export const generateSystemPrompt = async (provider: ClineProvider, message: Web enableMcpServerCreation, browserToolEnabled, language, + maxReadFileLine, } = await provider.getState() const diffStrategy = new MultiSearchReplaceDiffStrategy(fuzzyMatchThreshold) @@ -67,6 +68,7 @@ export const generateSystemPrompt = async (provider: ClineProvider, message: Web enableMcpServerCreation, language, rooIgnoreInstructions, + maxReadFileLine !== -1, ) return systemPrompt From e66136f1aa0a114061a0f5e33dbd4a5a196de3ea Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Mon, 26 May 2025 12:06:45 -0700 Subject: [PATCH 012/104] Add a new @roo-code/types package and use it everywhere (#3912) --- .../scripts/overwrite_changeset_changelog.py | 82 - .github/workflows/code-qa.yml | 118 +- .husky/pre-commit | 8 - apps/vscode-e2e/package.json | 6 +- apps/vscode-e2e/src/suite/extension.test.ts | 8 +- apps/vscode-e2e/src/suite/index.ts | 13 +- apps/vscode-e2e/src/suite/modes.test.ts | 10 +- apps/vscode-e2e/src/suite/subtasks.test.ts | 9 +- apps/vscode-e2e/src/suite/task.test.ts | 5 +- apps/vscode-e2e/src/types/global.d.ts | 8 + apps/vscode-e2e/tsconfig.esm.json | 8 + apps/vscode-e2e/tsconfig.json | 2 +- apps/vscode-nightly/esbuild.mjs | 31 +- apps/vscode-nightly/package.json | 5 +- e2e/src/suite/condensing.test.ts | 243 --- knip.json | 2 +- package.json | 11 +- packages/build/src/esbuild.ts | 66 +- packages/build/src/index.ts | 2 +- {src/exports => packages/types}/README.md | 13 +- packages/types/eslint.config.mjs | 4 + packages/types/package.json | 37 + packages/types/src/__tests__/index.test.ts | 17 + .../interface.ts => packages/types/src/api.ts | 97 +- packages/types/src/index.ts | 2 + .../index.ts => packages/types/src/types.ts | 57 +- packages/types/tsconfig.json | 9 + packages/types/tsup.config.ts | 11 + pnpm-lock.yaml | 1097 +++++----- src/activate/CodeActionProvider.ts | 3 +- src/activate/handleTask.ts | 2 +- src/activate/registerCodeActions.ts | 3 +- src/activate/registerCommands.ts | 4 +- src/activate/registerTerminalActions.ts | 3 +- src/api/index.ts | 58 +- src/api/providers/__tests__/gemini.test.ts | 4 +- src/api/providers/anthropic-vertex.ts | 4 +- src/api/providers/anthropic.ts | 10 +- .../base-openai-compatible-provider.ts | 4 +- src/api/providers/base-provider.ts | 2 +- src/api/providers/bedrock.ts | 19 +- src/api/providers/fake-ai.ts | 7 +- src/api/providers/fetchers/glama.ts | 5 +- src/api/providers/fetchers/litellm.ts | 1 + src/api/providers/fetchers/openrouter.ts | 6 +- src/api/providers/fetchers/requesty.ts | 5 +- src/api/providers/fetchers/unbound.ts | 2 +- src/api/providers/gemini.ts | 7 +- src/api/providers/glama.ts | 2 +- src/api/providers/human-relay.ts | 5 +- src/api/providers/index.ts | 22 + src/api/providers/lmstudio.ts | 8 +- src/api/providers/mistral.ts | 6 +- src/api/providers/ollama.ts | 10 +- src/api/providers/openai-native.ts | 7 +- src/api/providers/openai.ts | 11 +- src/api/providers/requesty.ts | 21 +- src/api/providers/router-provider.ts | 5 +- src/api/providers/vertex.ts | 6 +- src/api/providers/vscode-lm.ts | 10 +- .../__tests__/image-cleaning.test.ts | 5 +- .../transform/__tests__/model-params.test.ts | 3 +- src/api/transform/__tests__/reasoning.test.ts | 3 +- src/api/transform/image-cleaning.ts | 3 +- src/api/transform/model-params.ts | 9 +- src/api/transform/reasoning.ts | 3 +- .../parseAssistantMessage.ts | 3 +- .../parseAssistantMessageV2.ts | 3 +- .../presentAssistantMessage.ts | 3 +- src/core/config/ContextProxy.ts | 13 +- src/core/config/CustomModesManager.ts | 8 +- src/core/config/ProviderSettingsManager.ts | 7 +- .../config/__tests__/ContextProxy.test.ts | 5 +- .../__tests__/CustomModesManager.test.ts | 10 +- .../__tests__/CustomModesSettings.test.ts | 4 +- src/core/config/__tests__/ModeConfig.test.ts | 3 +- .../__tests__/ProviderSettingsManager.test.ts | 3 +- .../config/__tests__/importExport.test.ts | 3 +- src/core/config/importExport.ts | 5 +- .../diff/strategies/multi-search-replace.ts | 3 +- src/core/environment/getEnvironmentDetails.ts | 4 +- src/core/prompts/__tests__/system.test.ts | 8 +- .../prompts/sections/custom-instructions.ts | 6 +- src/core/prompts/sections/modes.ts | 4 +- src/core/prompts/system.ts | 29 +- src/core/prompts/tools/index.ts | 5 +- .../__tests__/sliding-window.test.ts | 8 +- src/core/task-persistence/taskMessages.ts | 3 +- src/core/task-persistence/taskMetadata.ts | 4 +- src/core/task/Task.ts | 27 +- src/core/task/__tests__/Task.test.ts | 4 +- .../__tests__/ToolRepetitionDetector.test.ts | 3 +- .../__tests__/executeCommandTool.test.ts | 3 +- .../tools/__tests__/validateToolUse.test.ts | 5 +- src/core/tools/executeCommandTool.ts | 4 +- src/core/tools/validateToolUse.ts | 5 +- src/core/webview/ClineProvider.ts | 9 +- .../webview/__tests__/ClineProvider.test.ts | 9 +- src/core/webview/webviewMessageHandler.ts | 4 +- src/esbuild.mjs | 19 +- src/exports/log.ts | 32 - src/exports/roo-code.d.ts | 1889 ----------------- src/exports/types.ts | 1675 --------------- src/extension.ts | 8 +- src/{exports => extension}/api.ts | 51 +- .../ipc.ts => extension/ipc-server.ts} | 10 +- .../diagnostics/__tests__/diagnostics.test.ts | 3 +- src/integrations/theme/getTheme.ts | 2 +- src/package.json | 14 +- src/schemas/__tests__/index.test.ts | 41 - src/scripts/generate-types.mts | 34 - src/shared/ExtensionMessage.ts | 16 +- src/shared/HistoryItem.ts | 3 - src/shared/WebviewMessage.ts | 5 +- src/shared/__tests__/api.test.ts | 11 +- .../__tests__/checkExistApiConfig.test.ts | 5 +- .../__tests__/combineApiRequests.test.ts | 3 +- .../__tests__/combineCommandSequences.test.ts | 2 +- src/shared/__tests__/experiments.test.ts | 6 +- src/shared/__tests__/getApiMetrics.test.ts | 3 +- src/shared/__tests__/modes.test.ts | 8 +- src/shared/api.ts | 5 +- src/shared/checkExistApiConfig.ts | 2 +- src/shared/combineApiRequests.ts | 2 +- src/shared/combineCommandSequences.ts | 2 +- src/{utils => shared}/cost.ts | 2 +- src/shared/experiments.ts | 5 +- src/shared/getApiMetrics.ts | 4 +- src/shared/language.ts | 6 +- src/shared/modes.ts | 13 +- src/shared/package.ts | 19 + src/shared/tools.ts | 4 +- src/utils/__tests__/cost.test.ts | 7 +- src/utils/__tests__/enhance-prompt.test.ts | 5 +- src/utils/commands.ts | 4 +- src/utils/single-completion-handler.ts | 3 +- src/utils/storage.ts | 2 +- src/utils/type-fu.ts | 7 - src/vitest.config.ts | 6 + turbo.json | 29 +- webview-ui/jest.config.cjs | 2 +- webview-ui/package.json | 3 + webview-ui/src/App.tsx | 4 +- .../src/components/chat/Announcement.tsx | 2 +- .../chat/AutoApprovedRequestLimitWarning.tsx | 6 +- .../src/components/chat/BrowserSessionRow.tsx | 6 +- webview-ui/src/components/chat/ChatRow.tsx | 8 +- .../src/components/chat/ChatTextArea.tsx | 8 +- webview-ui/src/components/chat/ChatView.tsx | 34 +- .../src/components/chat/CommandExecution.tsx | 9 +- .../components/chat/ContextCondenseRow.tsx | 3 +- .../src/components/chat/ContextMenu.tsx | 2 +- webview-ui/src/components/chat/Mention.tsx | 2 +- .../src/components/chat/TaskActions.tsx | 3 +- webview-ui/src/components/chat/TaskHeader.tsx | 5 +- .../chat/__tests__/Announcement.test.tsx | 2 +- .../chat/__tests__/ChatTextArea.test.tsx | 2 +- .../chat/__tests__/TaskHeader.test.tsx | 2 +- .../src/components/common/CodeAccordian.tsx | 3 +- .../src/components/common/TelemetryBanner.tsx | 10 +- webview-ui/src/components/mcp/McpErrorRow.tsx | 2 +- .../src/components/mcp/McpResourceRow.tsx | 2 +- webview-ui/src/components/mcp/McpToolRow.tsx | 4 +- webview-ui/src/components/mcp/McpView.tsx | 2 +- .../src/components/prompts/PromptsView.tsx | 36 +- webview-ui/src/components/settings/About.tsx | 4 +- .../components/settings/ApiConfigManager.tsx | 2 +- .../src/components/settings/ApiOptions.tsx | 6 +- .../components/settings/AutoApproveToggle.tsx | 2 +- .../components/settings/CodeIndexSettings.tsx | 28 +- .../settings/ExperimentalSettings.tsx | 17 +- .../components/settings/LanguageSettings.tsx | 9 +- .../src/components/settings/ModelInfoView.tsx | 8 +- .../src/components/settings/ModelPicker.tsx | 2 +- .../src/components/settings/SettingsView.tsx | 14 +- .../components/settings/TerminalSettings.tsx | 2 +- .../components/settings/ThinkingBudget.tsx | 10 +- .../settings/__tests__/ApiOptions.test.tsx | 22 +- .../settings/__tests__/ModelPicker.test.tsx | 2 +- .../__tests__/ThinkingBudget.test.tsx | 2 +- .../src/components/settings/constants.ts | 8 +- .../settings/providers/Anthropic.tsx | 2 +- .../components/settings/providers/Bedrock.tsx | 2 +- .../settings/providers/BedrockCustomArn.tsx | 2 +- .../components/settings/providers/Chutes.tsx | 2 +- .../settings/providers/DeepSeek.tsx | 2 +- .../components/settings/providers/Gemini.tsx | 2 +- .../components/settings/providers/Glama.tsx | 4 +- .../components/settings/providers/Groq.tsx | 2 +- .../settings/providers/LMStudio.tsx | 4 +- .../components/settings/providers/LiteLLM.tsx | 12 +- .../components/settings/providers/Mistral.tsx | 4 +- .../components/settings/providers/Ollama.tsx | 5 +- .../components/settings/providers/OpenAI.tsx | 2 +- .../settings/providers/OpenAICompatible.tsx | 9 +- .../settings/providers/OpenRouter.tsx | 4 +- .../settings/providers/Requesty.tsx | 4 +- .../components/settings/providers/Unbound.tsx | 4 +- .../settings/providers/VSCodeLM.tsx | 5 +- .../components/settings/providers/Vertex.tsx | 2 +- .../src/components/settings/providers/XAI.tsx | 2 +- webview-ui/src/components/settings/types.ts | 2 +- .../hooks/__tests__/useSelectedModel.test.ts | 2 +- .../ui/hooks/useOpenRouterModelProviders.ts | 5 +- .../components/ui/hooks/useRouterModels.ts | 7 +- .../components/ui/hooks/useSelectedModel.ts | 9 +- .../src/components/welcome/WelcomeView.tsx | 14 +- .../src/context/ExtensionStateContext.tsx | 27 +- .../__tests__/ExtensionStateContext.test.tsx | 7 +- webview-ui/src/oauth/urls.ts | 2 +- webview-ui/src/utils/TelemetryClient.ts | 2 +- webview-ui/src/utils/context-mentions.ts | 6 +- webview-ui/src/utils/mcp.ts | 2 +- webview-ui/src/utils/validate.ts | 4 +- webview-ui/src/utils/vscode.ts | 3 +- webview-ui/tsconfig.json | 2 +- webview-ui/vite.config.ts | 2 +- 217 files changed, 1609 insertions(+), 5315 deletions(-) delete mode 100644 .github/scripts/overwrite_changeset_changelog.py create mode 100644 apps/vscode-e2e/src/types/global.d.ts create mode 100644 apps/vscode-e2e/tsconfig.esm.json delete mode 100644 e2e/src/suite/condensing.test.ts rename {src/exports => packages/types}/README.md (75%) create mode 100644 packages/types/eslint.config.mjs create mode 100644 packages/types/package.json create mode 100644 packages/types/src/__tests__/index.test.ts rename src/exports/interface.ts => packages/types/src/api.ts (83%) create mode 100644 packages/types/src/index.ts rename src/schemas/index.ts => packages/types/src/types.ts (94%) create mode 100644 packages/types/tsconfig.json create mode 100644 packages/types/tsup.config.ts create mode 100644 src/api/providers/index.ts delete mode 100644 src/exports/log.ts delete mode 100644 src/exports/roo-code.d.ts delete mode 100644 src/exports/types.ts rename src/{exports => extension}/api.ts (90%) rename src/{exports/ipc.ts => extension/ipc-server.ts} (95%) delete mode 100644 src/schemas/__tests__/index.test.ts delete mode 100644 src/scripts/generate-types.mts delete mode 100644 src/shared/HistoryItem.ts rename src/{utils => shared}/cost.ts (97%) create mode 100644 src/shared/package.ts delete mode 100644 src/utils/type-fu.ts diff --git a/.github/scripts/overwrite_changeset_changelog.py b/.github/scripts/overwrite_changeset_changelog.py deleted file mode 100644 index fcec082d60..0000000000 --- a/.github/scripts/overwrite_changeset_changelog.py +++ /dev/null @@ -1,82 +0,0 @@ -""" -This script updates a specific version's release notes section in CHANGELOG.md with new content -or reformats existing content. - -The script: -1. Takes a version number, changelog path, and optionally new content as input from environment variables -2. Finds the section in the changelog for the specified version -3. Either: - a) Replaces the content with new content if provided, or - b) Reformats existing content by: - - Removing the first two lines of the changeset format - - Ensuring version numbers are wrapped in square brackets -4. Writes the updated changelog back to the file - -Environment Variables: - CHANGELOG_PATH: Path to the changelog file (defaults to 'CHANGELOG.md') - VERSION: The version number to update/format - PREV_VERSION: The previous version number (used to locate section boundaries) - NEW_CONTENT: Optional new content to insert for this version -""" - -#!/usr/bin/env python3 - -import os - -CHANGELOG_PATH = os.environ.get("CHANGELOG_PATH", "CHANGELOG.md") -VERSION = os.environ["VERSION"] -PREV_VERSION = os.environ.get("PREV_VERSION", "") -NEW_CONTENT = os.environ.get("NEW_CONTENT", "") - - -def overwrite_changelog_section(changelog_text: str, new_content: str): - # Find the section for the specified version - version_pattern = f"## {VERSION}\n" - prev_version_pattern = f"## [{PREV_VERSION}]\n" - print(f"latest version: {VERSION}") - print(f"prev_version: {PREV_VERSION}") - - notes_start_index = changelog_text.find(version_pattern) + len(version_pattern) - notes_end_index = ( - changelog_text.find(prev_version_pattern, notes_start_index) - if PREV_VERSION and prev_version_pattern in changelog_text - else len(changelog_text) - ) - - if new_content: - return ( - changelog_text[:notes_start_index] - + f"{new_content}\n" - + changelog_text[notes_end_index:] - ) - else: - changeset_lines = changelog_text[notes_start_index:notes_end_index].split("\n") - # Remove the first two lines from the regular changeset format, ex: \n### Patch Changes - parsed_lines = "\n".join(changeset_lines[2:]) - updated_changelog = ( - changelog_text[:notes_start_index] - + parsed_lines - + changelog_text[notes_end_index:] - ) - updated_changelog = updated_changelog.replace( - f"## {VERSION}", f"## [{VERSION}]" - ) - return updated_changelog - - -with open(CHANGELOG_PATH, "r") as f: - changelog_content = f.read() - -new_changelog = overwrite_changelog_section(changelog_content, NEW_CONTENT) -print( - "----------------------------------------------------------------------------------" -) -print(new_changelog) -print( - "----------------------------------------------------------------------------------" -) -# Write back to CHANGELOG.md -with open(CHANGELOG_PATH, "w") as f: - f.write(new_changelog) - -print(f"{CHANGELOG_PATH} updated successfully!") diff --git a/.github/workflows/code-qa.yml b/.github/workflows/code-qa.yml index 1a001921d3..0fbd581fe7 100644 --- a/.github/workflows/code-qa.yml +++ b/.github/workflows/code-qa.yml @@ -13,27 +13,6 @@ env: PNPM_VERSION: 10.8.1 jobs: - compile: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - version: ${{ env.PNPM_VERSION }} - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: ${{ env.NODE_VERSION }} - cache: 'pnpm' - - name: Install dependencies - run: pnpm install - - name: Check types - run: pnpm check-types - - name: Lint - run: pnpm lint - check-translations: runs-on: ubuntu-latest steps: @@ -72,58 +51,48 @@ jobs: - name: Run knip checks run: pnpm knip - test-extension: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, windows-latest] - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - version: ${{ env.PNPM_VERSION }} - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: ${{ env.NODE_VERSION }} - cache: 'pnpm' - - name: Install dependencies - run: pnpm install - - name: Run unit tests - working-directory: src - run: pnpm test - - test-webview: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, windows-latest] - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - version: ${{ env.PNPM_VERSION }} - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: ${{ env.NODE_VERSION }} - cache: 'pnpm' - - name: Install dependencies - run: pnpm install - - name: Run unit tests - working-directory: webview-ui - run: pnpm test - - unit-test: - needs: [test-extension, test-webview] + compile: runs-on: ubuntu-latest steps: - - name: NO-OP - run: echo "All unit tests passed." + - name: Checkout code + uses: actions/checkout@v4 + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: ${{ env.PNPM_VERSION }} + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'pnpm' + - name: Install dependencies + run: pnpm install + - name: Lint + run: pnpm lint + - name: Check types + run: pnpm check-types + + platform-unit-test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: ${{ env.PNPM_VERSION }} + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'pnpm' + - name: Install dependencies + run: pnpm install + - name: Run unit tests + run: pnpm test check-openrouter-api-key: runs-on: ubuntu-latest @@ -164,3 +133,10 @@ jobs: - name: Run integration tests working-directory: apps/vscode-e2e run: xvfb-run -a pnpm test:ci + + unit-test: + needs: [platform-unit-test] # [platform-unit-test, integration-test] + runs-on: ubuntu-latest + steps: + - name: NO-OP + run: echo "All tests passed." diff --git a/.husky/pre-commit b/.husky/pre-commit index a7b784fcb9..a0e3a53df5 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -16,14 +16,6 @@ else fi fi -$pnpm_cmd --filter roo-cline generate-types - -if [ -n "$(git diff --name-only src/exports/roo-code.d.ts)" ]; then - echo "Error: There are unstaged changes to roo-code.d.ts after running 'pnpm --filter roo-cline generate-types'." - echo "Please review and stage the changes before committing." - exit 1 -fi - # Detect if running on Windows and use npx.cmd, otherwise use npx. if [ "$OS" = "Windows_NT" ]; then npx_cmd="npx.cmd" diff --git a/apps/vscode-e2e/package.json b/apps/vscode-e2e/package.json index 92278b3fa1..33768c1795 100644 --- a/apps/vscode-e2e/package.json +++ b/apps/vscode-e2e/package.json @@ -3,16 +3,16 @@ "private": true, "scripts": { "lint": "eslint src --ext=ts --max-warnings=0", - "check-types": "tsc --noEmit", + "check-types": "tsc -p tsconfig.esm.json --noEmit", "format": "prettier --write src", - "test:ci": "pnpm --filter roo-cline build:development && pnpm test:run", + "test:ci": "pnpm -w bundle && pnpm --filter @roo-code/vscode-webview build && pnpm test:run", "test:run": "rimraf out && tsc -p tsconfig.json && npx dotenvx run -f .env.local -- node ./out/runTest.js", "clean": "rimraf out .turbo" }, "devDependencies": { "@roo-code/config-eslint": "workspace:^", "@roo-code/config-typescript": "workspace:^", - "@roo-code/types": "^1.12.0", + "@roo-code/types": "workspace:^", "@types/mocha": "^10.0.10", "@types/node": "^22.14.1", "@types/vscode": "^1.95.0", diff --git a/apps/vscode-e2e/src/suite/extension.test.ts b/apps/vscode-e2e/src/suite/extension.test.ts index 54544a2627..3283dfcc56 100644 --- a/apps/vscode-e2e/src/suite/extension.test.ts +++ b/apps/vscode-e2e/src/suite/extension.test.ts @@ -1,8 +1,6 @@ import * as assert from "assert" import * as vscode from "vscode" -import { Package } from "@roo-code/types" - suite("Roo Code Extension", () => { test("Commands should be registered", async () => { const expectedCommands = [ @@ -36,12 +34,10 @@ suite("Roo Code Extension", () => { "terminalExplainCommand", ] - const commands = new Set( - (await vscode.commands.getCommands(true)).filter((cmd) => cmd.startsWith(Package.name)), - ) + const commands = new Set((await vscode.commands.getCommands(true)).filter((cmd) => cmd.startsWith("roo-cline"))) for (const command of expectedCommands) { - assert.ok(commands.has(`${Package.name}.${command}`), `Command ${command} should be registered`) + assert.ok(commands.has(`roo-cline.${command}`), `Command ${command} should be registered`) } }) }) diff --git a/apps/vscode-e2e/src/suite/index.ts b/apps/vscode-e2e/src/suite/index.ts index 009b7d2777..b6f0fa9bed 100644 --- a/apps/vscode-e2e/src/suite/index.ts +++ b/apps/vscode-e2e/src/suite/index.ts @@ -3,16 +3,12 @@ import Mocha from "mocha" import { glob } from "glob" import * as vscode from "vscode" -import { type RooCodeAPI, Package } from "@roo-code/types" +import type { RooCodeAPI } from "@roo-code/types" import { waitFor } from "./utils" -declare global { - let api: RooCodeAPI -} - export async function run() { - const extension = vscode.extensions.getExtension(`${Package.publisher}.${Package.name}`) + const extension = vscode.extensions.getExtension("RooVeterinaryInc.roo-cline") if (!extension) { throw new Error("Extension not found") @@ -23,13 +19,12 @@ export async function run() { await api.setConfiguration({ apiProvider: "openrouter" as const, openRouterApiKey: process.env.OPENROUTER_API_KEY!, - openRouterModelId: "google/gemini-2.0-flash-001", + openRouterModelId: "openai/gpt-4.1", }) - await vscode.commands.executeCommand(`${Package.name}.SidebarProvider.focus`) + await vscode.commands.executeCommand("roo-cline.SidebarProvider.focus") await waitFor(() => api.isReady()) - // @ts-expect-error - Expose the API to the tests. globalThis.api = api // Add all the tests to the runner. diff --git a/apps/vscode-e2e/src/suite/modes.test.ts b/apps/vscode-e2e/src/suite/modes.test.ts index f022f344a7..edc93d4c9d 100644 --- a/apps/vscode-e2e/src/suite/modes.test.ts +++ b/apps/vscode-e2e/src/suite/modes.test.ts @@ -1,23 +1,17 @@ import * as assert from "assert" -import type { RooCodeAPI, ClineMessage } from "@roo-code/types" +import type { ClineMessage } from "@roo-code/types" import { waitUntilCompleted } from "./utils" suite("Roo Code Modes", () => { test("Should handle switching modes correctly", async () => { - // @ts-expect-error - Expose the API to the tests. - const api = globalThis.api as RooCodeAPI - - /** - * Switch modes. - */ + const api = globalThis.api const switchModesPrompt = "For each mode (Architect, Ask, Debug) respond with the mode name and what it specializes in after switching to that mode." const messages: ClineMessage[] = [] - const modeSwitches: string[] = [] api.on("taskModeSwitched", (_taskId, mode) => { diff --git a/apps/vscode-e2e/src/suite/subtasks.test.ts b/apps/vscode-e2e/src/suite/subtasks.test.ts index 00de623f34..adf1b2be89 100644 --- a/apps/vscode-e2e/src/suite/subtasks.test.ts +++ b/apps/vscode-e2e/src/suite/subtasks.test.ts @@ -1,13 +1,12 @@ import * as assert from "assert" -import type { RooCodeAPI, ClineMessage } from "@roo-code/types" +import type { ClineMessage } from "@roo-code/types" import { sleep, waitFor, waitUntilCompleted } from "./utils" suite.skip("Roo Code Subtasks", () => { test("Should handle subtask cancellation and resumption correctly", async () => { - // @ts-expect-error - Expose the API to the tests. - const api = globalThis.api as RooCodeAPI + const api = globalThis.api const messages: Record = {} @@ -49,7 +48,7 @@ suite.skip("Roo Code Subtasks", () => { // The parent task should not have resumed yet, so we shouldn't see // "Parent task resumed". assert.ok( - messages[parentTaskId].find(({ type, text }) => type === "say" && text === "Parent task resumed") === + messages[parentTaskId]?.find(({ type, text }) => type === "say" && text === "Parent task resumed") === undefined, "Parent task should not have resumed after subtask cancellation", ) @@ -63,7 +62,7 @@ suite.skip("Roo Code Subtasks", () => { // The parent task should still not have resumed. assert.ok( - messages[parentTaskId].find(({ type, text }) => type === "say" && text === "Parent task resumed") === + messages[parentTaskId]?.find(({ type, text }) => type === "say" && text === "Parent task resumed") === undefined, "Parent task should not have resumed after subtask cancellation", ) diff --git a/apps/vscode-e2e/src/suite/task.test.ts b/apps/vscode-e2e/src/suite/task.test.ts index 96fb51fe53..e97c3b4f1e 100644 --- a/apps/vscode-e2e/src/suite/task.test.ts +++ b/apps/vscode-e2e/src/suite/task.test.ts @@ -1,13 +1,12 @@ import * as assert from "assert" -import type { RooCodeAPI, ClineMessage } from "@roo-code/types" +import type { ClineMessage } from "@roo-code/types" import { waitUntilCompleted } from "./utils" suite("Roo Code Task", () => { test("Should handle prompt and response correctly", async () => { - // @ts-expect-error - Expose the API to the tests. - const api = globalThis.api as RooCodeAPI + const api = globalThis.api const messages: ClineMessage[] = [] diff --git a/apps/vscode-e2e/src/types/global.d.ts b/apps/vscode-e2e/src/types/global.d.ts new file mode 100644 index 0000000000..c2b11bf335 --- /dev/null +++ b/apps/vscode-e2e/src/types/global.d.ts @@ -0,0 +1,8 @@ +import type { RooCodeAPI } from "@roo-code/types" + +declare global { + // eslint-disable-next-line no-var + var api: RooCodeAPI +} + +export {} diff --git a/apps/vscode-e2e/tsconfig.esm.json b/apps/vscode-e2e/tsconfig.esm.json new file mode 100644 index 0000000000..e2f212fab9 --- /dev/null +++ b/apps/vscode-e2e/tsconfig.esm.json @@ -0,0 +1,8 @@ +{ + "extends": "@roo-code/config-typescript/base.json", + "compilerOptions": { + "outDir": "out" + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/apps/vscode-e2e/tsconfig.json b/apps/vscode-e2e/tsconfig.json index 4439b32b39..c991819bbb 100644 --- a/apps/vscode-e2e/tsconfig.json +++ b/apps/vscode-e2e/tsconfig.json @@ -11,6 +11,6 @@ "useUnknownInCatchVariables": false, "outDir": "out" }, - "include": ["src", "../src/exports/roo-code.d.ts"], + "include": ["src"], "exclude": [".vscode-test", "**/node_modules/**", "out"] } diff --git a/apps/vscode-nightly/esbuild.mjs b/apps/vscode-nightly/esbuild.mjs index 0ca286e69e..ccc999e78b 100644 --- a/apps/vscode-nightly/esbuild.mjs +++ b/apps/vscode-nightly/esbuild.mjs @@ -9,16 +9,17 @@ const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) async function main() { + const name = "extension-nightly" const production = process.argv.includes("--production") const minify = production const sourcemap = !production const overrideJson = JSON.parse(fs.readFileSync(path.join(__dirname, "package.nightly.json"), "utf8")) - console.log(`[main] name: ${overrideJson.name}`) - console.log(`[main] version: ${overrideJson.version}`) + console.log(`[${name}] name: ${overrideJson.name}`) + console.log(`[${name}] version: ${overrideJson.version}`) const gitSha = getGitSha() - console.log(`[main] gitSha: ${gitSha}`) + console.log(`[${name}] gitSha: ${gitSha}`) /** * @type {import('esbuild').BuildOptions} @@ -43,12 +44,22 @@ async function main() { const buildDir = path.join(__dirname, "build") const distDir = path.join(buildDir, "dist") + console.log(`[${name}] srcDir: ${srcDir}`) + console.log(`[${name}] buildDir: ${buildDir}`) + console.log(`[${name}] distDir: ${distDir}`) + + // Clean build directory before starting new build + if (fs.existsSync(buildDir)) { + console.log(`[${name}] Cleaning build directory: ${buildDir}`) + fs.rmSync(buildDir, { recursive: true, force: true }) + } + /** * @type {import('esbuild').Plugin[]} */ const plugins = [ { - name: "copy-files", + name: "copyPaths", setup(build) { build.onEnd(() => { copyPaths( @@ -69,7 +80,7 @@ async function main() { }, }, { - name: "generate-package-json", + name: "generatePackageJson", setup(build) { build.onEnd(() => { const packageJson = JSON.parse(fs.readFileSync(path.join(srcDir, "package.json"), "utf8")) @@ -81,7 +92,7 @@ async function main() { }) fs.writeFileSync(path.join(buildDir, "package.json"), JSON.stringify(generatedPackageJson, null, 2)) - console.log(`[generate-package-json] Generated package.json`) + console.log(`[generatePackageJson] Generated package.json`) let count = 0 @@ -92,7 +103,7 @@ async function main() { } }) - console.log(`[copy-src] Copied ${count} package.nls*.json files to ${buildDir}`) + console.log(`[generatePackageJson] Copied ${count} package.nls*.json files to ${buildDir}`) const nlsPkg = JSON.parse(fs.readFileSync(path.join(srcDir, "package.nls.json"), "utf8")) @@ -105,18 +116,18 @@ async function main() { JSON.stringify({ ...nlsPkg, ...nlsNightlyPkg }, null, 2), ) - console.log(`[copy-src] Generated package.nls.json`) + console.log(`[generatePackageJson] Generated package.nls.json`) }) }, }, { - name: "copy-wasms", + name: "copyWasms", setup(build) { build.onEnd(() => copyWasms(srcDir, distDir)) }, }, { - name: "copy-locales", + name: "copyLocales", setup(build) { build.onEnd(() => copyLocales(srcDir, distDir)) }, diff --git a/apps/vscode-nightly/package.json b/apps/vscode-nightly/package.json index 8413d1455b..56872a2aeb 100644 --- a/apps/vscode-nightly/package.json +++ b/apps/vscode-nightly/package.json @@ -4,9 +4,8 @@ "private": true, "packageManager": "pnpm@10.8.1", "scripts": { - "bundle": "pnpm clean && pnpm --filter @roo-code/build build && node esbuild.mjs", - "build": "pnpm bundle --production && pnpm --filter @roo-code/vscode-webview build --mode nightly", - "vsix": "pnpm build && cd build && mkdirp ../../../bin && npx vsce package --no-dependencies --out ../../../bin", + "bundle:nightly": "node esbuild.mjs", + "vsix:nightly": "cd build && mkdirp ../../../bin && npx vsce package --no-dependencies --out ../../../bin", "clean": "rimraf build .turbo" }, "devDependencies": { diff --git a/e2e/src/suite/condensing.test.ts b/e2e/src/suite/condensing.test.ts deleted file mode 100644 index 0d5e796349..0000000000 --- a/e2e/src/suite/condensing.test.ts +++ /dev/null @@ -1,243 +0,0 @@ -import { suite, test, before, after } from "mocha" -import * as assert from "assert" -import { type RooCodeAPI } from "@roo-code/types" -import { waitFor, sleep } from "./utils" // Assuming utils.ts is in the same directory or path is adjusted - -// Define an interface for globalThis that includes the 'api' property -interface GlobalWithApi extends NodeJS.Global { - api: RooCodeAPI -} - -// Cast globalThis to our new interface -const g = globalThis as unknown as GlobalWithApi - -// Define a minimal interface for task messages for type safety in callbacks -interface TestTaskMessage { - role: string - content: string | unknown // Content can be complex - isSummary?: boolean - // Allow other properties - [key: string]: unknown -} - -suite("Context Condensing Integration Tests", () => { - let initialConfig: ReturnType - - before(async () => { - // Ensure API is ready before starting tests - await waitFor(() => g.api && g.api.isReady()) - initialConfig = g.api.getConfiguration() - }) - - after(async () => { - // Restore initial configuration after tests - if (initialConfig) { - // Type issue: RooCodeSettings might not include new props. - // This will cause a type error if initialConfig contains new props not in RooCodeSettings. - // For now, we assume initialConfig is a valid RooCodeSettings or types need update. - await g.api.setConfiguration(initialConfig) - } - }) - - suite("Settings Persistence", () => { - test("should persist condensingApiConfigId when set", async () => { - const testConfigId = "test-condensing-api-config" - // @ts-expect-error - Argument of type '{ condensingApiConfigId: string; }' is not assignable to parameter of type 'RooCodeSettings'. - await g.api.setConfiguration({ condensingApiConfigId: testConfigId }) - await sleep(100) - const updatedConfig = g.api.getConfiguration() - assert.strictEqual( - // @ts-expect-error - Property 'condensingApiConfigId' does not exist on type 'RooCodeSettings'. - updatedConfig.condensingApiConfigId, - testConfigId, - "condensingApiConfigId did not persist", - ) - }) - - test("should persist customCondensingPrompt when set", async () => { - const testPrompt = "This is a custom condensing prompt for testing." - // @ts-expect-error - Argument of type '{ customCondensingPrompt: string; }' is not assignable to parameter of type 'RooCodeSettings'. - await g.api.setConfiguration({ customCondensingPrompt: testPrompt }) - await sleep(100) - const updatedConfig = g.api.getConfiguration() - assert.strictEqual( - // @ts-expect-error - Property 'customCondensingPrompt' does not exist on type 'RooCodeSettings'. - updatedConfig.customCondensingPrompt, - testPrompt, - "customCondensingPrompt did not persist", - ) - }) - - test("should clear customCondensingPrompt when set to empty string", async () => { - const initialPrompt = "A prompt to be cleared." - // @ts-expect-error - Argument of type '{ customCondensingPrompt: string; }' is not assignable to parameter of type 'RooCodeSettings'. - await g.api.setConfiguration({ customCondensingPrompt: initialPrompt }) - await sleep(100) - let updatedConfig = g.api.getConfiguration() - // @ts-expect-error - Property 'customCondensingPrompt' does not exist on type 'RooCodeSettings'. - assert.strictEqual(updatedConfig.customCondensingPrompt, initialPrompt, "Initial prompt was not set") - - // @ts-expect-error - Argument of type '{ customCondensingPrompt: string; }' is not assignable to parameter of type 'RooCodeSettings'. - await g.api.setConfiguration({ customCondensingPrompt: "" }) - await sleep(100) - updatedConfig = g.api.getConfiguration() - // @ts-expect-error - Property 'customCondensingPrompt' does not exist on type 'RooCodeSettings'. - assert.strictEqual(updatedConfig.customCondensingPrompt, "", "customCondensingPrompt was not cleared") - }) - - test("should clear customCondensingPrompt when set to undefined", async () => { - const initialPrompt = "Another prompt to be cleared." - // @ts-expect-error - Argument of type '{ customCondensingPrompt: string; }' is not assignable to parameter of type 'RooCodeSettings'. - await g.api.setConfiguration({ customCondensingPrompt: initialPrompt }) - await sleep(100) - let updatedConfig = g.api.getConfiguration() - assert.strictEqual( - // @ts-expect-error - Property 'customCondensingPrompt' does not exist on type 'RooCodeSettings'. - updatedConfig.customCondensingPrompt, - initialPrompt, - "Initial prompt for undefined test was not set", - ) - - // @ts-expect-error - Argument of type '{ customCondensingPrompt: undefined; }' is not assignable to parameter of type 'RooCodeSettings'. - await g.api.setConfiguration({ customCondensingPrompt: undefined }) - await sleep(100) - updatedConfig = g.api.getConfiguration() - // @ts-expect-error - Property 'customCondensingPrompt' does not exist on type 'RooCodeSettings'. - const currentPrompt = updatedConfig.customCondensingPrompt - assert.ok( - currentPrompt === "" || currentPrompt === undefined || currentPrompt === null, - "customCondensingPrompt was not cleared by undefined", - ) - }) - }) - - suite("Message Handling (Conceptual - Covered by Settings Persistence)", () => { - test.skip("should correctly update backend state from webview messages", () => { - assert.ok(true, "Skipping direct webview message test, covered by settings persistence.") - }) - }) - - suite("API Configuration Resolution and Prompt Customization", () => { - let taskId: string | undefined - - beforeEach(async () => { - // @ts-expect-error - Property 'tasks' does not exist on type 'RooCodeAPI'. - const taskResponse = await g.api.tasks.createTask({ - initialMessage: "This is the first message for a new task.", - }) - taskId = taskResponse.taskId - assert.ok(taskId, "Task ID should be created") - await sleep(500) - }) - - afterEach(async () => { - if (taskId) { - taskId = undefined - } - // This directive was unused, meaning setConfiguration(initialConfig) is fine. - await g.api.setConfiguration(initialConfig) - await sleep(100) - }) - - test("should trigger condensation with default settings", async function () { - this.timeout(60000) - assert.ok(taskId, "Task ID must be defined for this test") - - for (let i = 0; i < 5; i++) { - // @ts-expect-error - Property 'tasks' does not exist on type 'RooCodeAPI'. - await g.api.tasks.sendMessage({ - taskId: taskId!, - message: `This is message number ${i + 2} in the conversation.`, - messageType: "user", - }) - await sleep(2000) - } - - // @ts-expect-error - Property 'tasks' does not exist on type 'RooCodeAPI'. - const task = await g.api.tasks.getTask(taskId!) - assert.ok(task, "Task should be retrievable") - const hasSummary = task.messages.some((msg: TestTaskMessage) => msg.isSummary === true) - console.log( - `Task messages for default settings test (taskId: ${taskId}):`, - JSON.stringify(task.messages, null, 2), - ) - console.log(`Has summary (default settings): ${hasSummary}`) - assert.ok( - true, - "Condensation process completed with default settings (actual summary check is complex for e2e).", - ) - }) - - test("should trigger condensation with custom condensing API config", async function () { - this.timeout(60000) - assert.ok(taskId, "Task ID must be defined for this test") - - const customCondensingConfigId = "condensing-test-provider" - // This directive was unused. The error is on the property itself. - await g.api.setConfiguration({ - // @ts-expect-error - condensingApiConfigId is not a known property in RooCodeSettings. - condensingApiConfigId: customCondensingConfigId, - }) - await sleep(100) - - for (let i = 0; i < 5; i++) { - // @ts-expect-error - Property 'tasks' does not exist on type 'RooCodeAPI'. - await g.api.tasks.sendMessage({ - taskId: taskId!, - message: `Message ${i + 2} with custom API config.`, - messageType: "user", - }) - await sleep(2000) - } - // @ts-expect-error - Property 'tasks' does not exist on type 'RooCodeAPI'. - const task = await g.api.tasks.getTask(taskId!) - assert.ok(task, "Task should be retrievable with custom API config") - const hasSummary = task.messages.some((msg: TestTaskMessage) => msg.isSummary === true) - console.log( - `Task messages for custom API config test (taskId: ${taskId}):`, - JSON.stringify(task.messages, null, 2), - ) - console.log(`Has summary (custom API config): ${hasSummary}`) - assert.ok( - true, - "Condensation process completed with custom API config (specific handler verification is complex for e2e).", - ) - }) - - test("should trigger condensation with custom condensing prompt", async function () { - this.timeout(60000) - assert.ok(taskId, "Task ID must be defined for this test") - - const customPrompt = "E2E Test: Summarize this conversation very briefly." - // @ts-expect-error - Argument of type '{ customCondensingPrompt: string; }' is not assignable to parameter of type 'RooCodeSettings'. - await g.api.setConfiguration({ customCondensingPrompt: customPrompt }) - await sleep(100) - - for (let i = 0; i < 5; i++) { - // @ts-expect-error - Property 'tasks' does not exist on type 'RooCodeAPI'. - await g.api.tasks.sendMessage({ - taskId: taskId!, - message: `Message ${i + 2} with custom prompt.`, - messageType: "user", - }) - await sleep(2000) - } - - // @ts-expect-error - Property 'tasks' does not exist on type 'RooCodeAPI'. - const task = await g.api.tasks.getTask(taskId!) - assert.ok(task, "Task should be retrievable with custom prompt") - const summaryMessage = task.messages.find((msg: TestTaskMessage) => msg.isSummary === true) - console.log( - `Task messages for custom prompt test (taskId: ${taskId}):`, - JSON.stringify(task.messages, null, 2), - ) - if (summaryMessage) { - console.log("Summary content with custom prompt:", summaryMessage.content) - } - assert.ok( - true, - "Condensation process completed with custom prompt (prompt content verification is complex for e2e).", - ) - }) - }) -}) diff --git a/knip.json b/knip.json index ac26aa5339..aefa19ad9c 100644 --- a/knip.json +++ b/knip.json @@ -12,8 +12,8 @@ "bin/**", "apps/vscode-e2e/**", "evals/**", + "src/extension/**", "src/activate/**", - "src/exports/**", "src/workers/**", "src/schemas/ipc.ts", "src/extension.ts", diff --git a/package.json b/package.json index e488cc4987..4580be5ce3 100644 --- a/package.json +++ b/package.json @@ -13,14 +13,13 @@ "check-types": "turbo check-types --log-order grouped --output-logs new-only", "test": "turbo test --log-order grouped --output-logs new-only", "format": "turbo format --log-order grouped --output-logs new-only", + "bundle": "turbo bundle --log-order grouped --output-logs new-only", + "bundle:nightly": "turbo bundle:nightly --log-order grouped --output-logs new-only", + "build": "turbo vsix --log-order grouped --output-logs new-only", + "build:nightly": "turbo vsix:nightly --log-order grouped --output-logs new-only", "clean": "turbo clean --log-order grouped --output-logs new-only && rimraf dist out bin .vite-port .turbo", - "build": "pnpm --filter roo-cline vsix", - "compile": "pnpm --filter roo-cline bundle", - "vsix": "pnpm --filter roo-cline vsix", - "build:nightly": "pnpm --filter @roo-code/vscode-nightly vsix", - "generate-types": "pnpm --filter roo-cline generate-types", "changeset:version": "cp CHANGELOG.md src/CHANGELOG.md && changeset version && cp -vf src/CHANGELOG.md .", - "knip": "pnpm --filter @roo-code/build build && knip --include files", + "knip": "knip --include files", "update-contributors": "node scripts/update-contributors.js" }, "devDependencies": { diff --git a/packages/build/src/esbuild.ts b/packages/build/src/esbuild.ts index 898b7417a7..e91275447d 100644 --- a/packages/build/src/esbuild.ts +++ b/packages/build/src/esbuild.ts @@ -3,27 +3,7 @@ import * as path from "path" import { ViewsContainer, Views, Menus, Configuration, contributesSchema } from "./types.js" -export function copyPaths(copyPaths: [string, string][], srcDir: string, dstDir: string) { - copyPaths.forEach(([srcRelPath, dstRelPath]) => { - const stats = fs.lstatSync(path.join(srcDir, srcRelPath)) - - if (stats.isDirectory()) { - if (fs.existsSync(path.join(dstDir, dstRelPath))) { - fs.rmSync(path.join(dstDir, dstRelPath), { recursive: true }) - } - - fs.mkdirSync(path.join(dstDir, dstRelPath), { recursive: true }) - - const count = copyDir(path.join(srcDir, srcRelPath), path.join(dstDir, dstRelPath), 0) - console.log(`[copyPaths] Copied ${count} files from ${srcRelPath} to ${dstRelPath}`) - } else { - fs.copyFileSync(path.join(srcDir, srcRelPath), path.join(dstDir, dstRelPath)) - console.log(`[copyPaths] Copied ${srcRelPath} to ${dstRelPath}`) - } - }) -} - -export function copyDir(srcDir: string, dstDir: string, count: number): number { +function copyDir(srcDir: string, dstDir: string, count: number): number { const entries = fs.readdirSync(srcDir, { withFileTypes: true }) for (const entry of entries) { @@ -42,6 +22,50 @@ export function copyDir(srcDir: string, dstDir: string, count: number): number { return count } +function rmDir(dirPath: string, maxRetries: number = 3): void { + for (let attempt = 1; attempt <= maxRetries; attempt++) { + try { + fs.rmSync(dirPath, { recursive: true, force: true }) + return + } catch (error) { + const isLastAttempt = attempt === maxRetries + const isEnotemptyError = error instanceof Error && "code" in error && (error.code === 'ENOTEMPTY' || error.code === 'EBUSY') + + if (isLastAttempt || !isEnotemptyError) { + throw error // Re-throw if it's the last attempt or not a locking error. + } + + // Wait with exponential backoff before retrying. + const delay = Math.min(100 * Math.pow(2, attempt - 1), 1000) // Cap at 1s. + console.warn(`[rmDir] Attempt ${attempt} failed for ${dirPath}, retrying in ${delay}ms...`) + + // Synchronous sleep for simplicity in build scripts. + const start = Date.now() + while (Date.now() - start < delay) { /* Busy wait */ } + } + } +} + +export function copyPaths(copyPaths: [string, string][], srcDir: string, dstDir: string) { + copyPaths.forEach(([srcRelPath, dstRelPath]) => { + const stats = fs.lstatSync(path.join(srcDir, srcRelPath)) + + if (stats.isDirectory()) { + if (fs.existsSync(path.join(dstDir, dstRelPath))) { + rmDir(path.join(dstDir, dstRelPath)) + } + + fs.mkdirSync(path.join(dstDir, dstRelPath), { recursive: true }) + + const count = copyDir(path.join(srcDir, srcRelPath), path.join(dstDir, dstRelPath), 0) + console.log(`[copyPaths] Copied ${count} files from ${srcRelPath} to ${dstRelPath}`) + } else { + fs.copyFileSync(path.join(srcDir, srcRelPath), path.join(dstDir, dstRelPath)) + console.log(`[copyPaths] Copied ${srcRelPath} to ${dstRelPath}`) + } + }) +} + export function copyWasms(srcDir: string, distDir: string): void { const nodeModulesDir = path.join(srcDir, "node_modules") diff --git a/packages/build/src/index.ts b/packages/build/src/index.ts index bcb4e2d039..edbc994a2d 100644 --- a/packages/build/src/index.ts +++ b/packages/build/src/index.ts @@ -1,2 +1,2 @@ export { getGitSha } from "./git.js" -export { copyPaths, copyDir, copyWasms, copyLocales, setupLocaleWatcher, generatePackageJson } from "./esbuild.js" +export { copyPaths, copyWasms, copyLocales, setupLocaleWatcher, generatePackageJson } from "./esbuild.js" diff --git a/src/exports/README.md b/packages/types/README.md similarity index 75% rename from src/exports/README.md rename to packages/types/README.md index ee79b160bb..c8fee89bcc 100644 --- a/src/exports/README.md +++ b/packages/types/README.md @@ -1,15 +1,16 @@ # Roo Code API -The Roo Code extension exposes an API that can be used by other extensions. To use this API in your extension: +The Roo Code extension exposes an API that can be used by other extensions. +To use this API in your extension: -1. Copy `src/extension-api/roo-code.d.ts` to your extension's source directory. -2. Include `roo-code.d.ts` in your extension's compilation. -3. Get access to the API with the following code: +1. Install `@roo-code/types` with npm, pnpm, or yarn. +2. Import the `RooCodeAPI` type. +3. Load the extension API. ```typescript -import { RooCodeAPI, Package } from "path/to/roo-code" +import { RooCodeAPI } from "@roo-code/types" -const extension = vscode.extensions.getExtension(`${Package.publisher}.${Package.name}`) +const extension = vscode.extensions.getExtension("RooVeterinaryInc.roo-cline") if (!extension?.isActive) { throw new Error("Extension is not activated") diff --git a/packages/types/eslint.config.mjs b/packages/types/eslint.config.mjs new file mode 100644 index 0000000000..694bf73664 --- /dev/null +++ b/packages/types/eslint.config.mjs @@ -0,0 +1,4 @@ +import { config } from "@roo-code/config-eslint/base" + +/** @type {import("eslint").Linter.Config} */ +export default [...config] diff --git a/packages/types/package.json b/packages/types/package.json new file mode 100644 index 0000000000..3cacff9f61 --- /dev/null +++ b/packages/types/package.json @@ -0,0 +1,37 @@ +{ + "name": "@roo-code/types", + "description": "Roo Code foundational types and schemas.", + "private": true, + "type": "module", + "main": "./dist/index.cjs", + "exports": { + ".": { + "types": "./src/index.ts", + "import": "./src/index.ts", + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + } + }, + "files": [ + "dist" + ], + "scripts": { + "lint": "eslint src --ext=ts --max-warnings=0", + "check-types": "tsc --noEmit", + "test": "vitest --globals --run", + "build": "tsup", + "clean": "rimraf dist .turbo" + }, + "dependencies": { + "zod": "^3.24.2" + }, + "devDependencies": { + "@roo-code/config-eslint": "workspace:^", + "@roo-code/config-typescript": "workspace:^", + "@types/node": "^22.15.20", + "tsup": "^8.3.5", + "vitest": "^3.1.3" + } +} diff --git a/packages/types/src/__tests__/index.test.ts b/packages/types/src/__tests__/index.test.ts new file mode 100644 index 0000000000..c3df37fa97 --- /dev/null +++ b/packages/types/src/__tests__/index.test.ts @@ -0,0 +1,17 @@ +// npx vitest run src/__tests__/index.test.ts + +import { GLOBAL_STATE_KEYS } from "../index.js" + +describe("GLOBAL_STATE_KEYS", () => { + it("should contain provider settings keys", () => { + expect(GLOBAL_STATE_KEYS).toContain("autoApprovalEnabled") + }) + + it("should contain provider settings keys", () => { + expect(GLOBAL_STATE_KEYS).toContain("anthropicBaseUrl") + }) + + it("should not contain secret state keys", () => { + expect(GLOBAL_STATE_KEYS).not.toContain("openRouterApiKey") + }) +}) diff --git a/src/exports/interface.ts b/packages/types/src/api.ts similarity index 83% rename from src/exports/interface.ts rename to packages/types/src/api.ts index d8423511da..c098111e6c 100644 --- a/src/exports/interface.ts +++ b/packages/types/src/api.ts @@ -1,59 +1,37 @@ -import { EventEmitter } from "events" -import { Socket } from "node:net" - -/** - * Types - */ +import type { EventEmitter } from "events" +import type { Socket } from "net" import type { - GlobalSettings, - ProviderName, - ProviderSettings, + RooCodeSettings, ProviderSettingsEntry, + ProviderSettings, ClineMessage, TokenUsage, - RooCodeEvents, - IpcMessage, + ToolUsage, + ToolName, TaskCommand, TaskEvent, -} from "./types" + IpcMessage, +} from "./index.js" +import { IpcMessageType } from "./index.js" -export type { - GlobalSettings, - ProviderName, - ProviderSettings, - ProviderSettingsEntry, - ClineMessage, - TokenUsage, - RooCodeEvents, - IpcMessage, - TaskCommand, - TaskEvent, +// TODO: Make sure this matches `RooCodeEvents` from `@roo-code/types`. +export interface RooCodeAPIEvents { + message: [data: { taskId: string; action: "created" | "updated"; message: ClineMessage }] + taskCreated: [taskId: string] + taskStarted: [taskId: string] + taskModeSwitched: [taskId: string, mode: string] + taskPaused: [taskId: string] + taskUnpaused: [taskId: string] + taskAskResponded: [taskId: string] + taskAborted: [taskId: string] + taskSpawned: [parentTaskId: string, childTaskId: string] + taskCompleted: [taskId: string, tokenUsage: TokenUsage, toolUsage: ToolUsage] + taskTokenUsageUpdated: [taskId: string, tokenUsage: TokenUsage] + taskToolFailed: [taskId: string, toolName: ToolName, error: string] } -/** - * Enums - */ - -import { RooCodeEventName, IpcOrigin, IpcMessageType } from "../schemas" - -export { RooCodeEventName, IpcOrigin, IpcMessageType } - -/** - * Constants - */ - -import { providerNames, Package } from "../schemas" - -export { providerNames, Package } - -/** - * RooCodeAPI - */ - -export type RooCodeSettings = GlobalSettings & ProviderSettings - -export interface RooCodeAPI extends EventEmitter { +export interface RooCodeAPI extends EventEmitter { /** * Starts a new task with an optional initial message and images. * @param task Optional initial task message. @@ -71,84 +49,70 @@ export interface RooCodeAPI extends EventEmitter { images?: string[] newTab?: boolean }): Promise - /** * Resumes a task with the given ID. * @param taskId The ID of the task to resume. * @throws Error if the task is not found in the task history. */ resumeTask(taskId: string): Promise - /** * Checks if a task with the given ID is in the task history. * @param taskId The ID of the task to check. * @returns True if the task is in the task history, false otherwise. */ isTaskInHistory(taskId: string): Promise - /** * Returns the current task stack. * @returns An array of task IDs. */ getCurrentTaskStack(): string[] - /** * Clears the current task. */ clearCurrentTask(lastMessage?: string): Promise - /** * Cancels the current task. */ cancelCurrentTask(): Promise - /** * Sends a message to the current task. * @param message Optional message to send. * @param images Optional array of image data URIs (e.g., "data:image/webp;base64,..."). */ sendMessage(message?: string, images?: string[]): Promise - /** * Simulates pressing the primary button in the chat interface. */ pressPrimaryButton(): Promise - /** * Simulates pressing the secondary button in the chat interface. */ pressSecondaryButton(): Promise - /** * Returns true if the API is ready to use. */ isReady(): boolean - /** * Returns the current configuration. * @returns The current configuration. */ getConfiguration(): RooCodeSettings - /** * Sets the configuration for the current task. * @param values An object containing key-value pairs to set. */ setConfiguration(values: RooCodeSettings): Promise - /** * Returns a list of all configured profile names * @returns Array of profile names */ getProfiles(): string[] - /** * Returns the profile entry for a given name * @param name The name of the profile * @returns The profile entry, or undefined if the profile does not exist */ getProfileEntry(name: string): ProviderSettingsEntry | undefined - /** * Creates a new API configuration profile * @param name The name of the profile @@ -158,7 +122,6 @@ export interface RooCodeAPI extends EventEmitter { * @throws Error if the profile already exists */ createProfile(name: string, profile?: ProviderSettings, activate?: boolean): Promise - /** * Updates an existing API configuration profile * @param name The name of the profile @@ -168,7 +131,6 @@ export interface RooCodeAPI extends EventEmitter { * @throws Error if the profile does not exist */ updateProfile(name: string, profile: ProviderSettings, activate?: boolean): Promise - /** * Creates a new API configuration profile or updates an existing one * @param name The name of the profile @@ -177,20 +139,17 @@ export interface RooCodeAPI extends EventEmitter { * @returns The ID of the upserted profile */ upsertProfile(name: string, profile: ProviderSettings, activate?: boolean): Promise - /** * Deletes a profile by name * @param name The name of the profile to delete * @throws Error if the profile does not exist */ deleteProfile(name: string): Promise - /** * Returns the name of the currently active profile * @returns The profile name, or undefined if no profile is active */ getActiveProfile(): string | undefined - /** * Changes the active API configuration profile * @param name The name of the profile to activate @@ -199,10 +158,6 @@ export interface RooCodeAPI extends EventEmitter { setActiveProfile(name: string): Promise } -/** - * RooCodeIpcServer - */ - export type IpcServerEvents = { [IpcMessageType.Connect]: [clientId: string] [IpcMessageType.Disconnect]: [clientId: string] @@ -212,12 +167,8 @@ export type IpcServerEvents = { export interface RooCodeIpcServer extends EventEmitter { listen(): void - broadcast(message: IpcMessage): void - send(client: string | Socket, message: IpcMessage): void - get socketPath(): string - get isListening(): boolean } diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts new file mode 100644 index 0000000000..b3656fb63a --- /dev/null +++ b/packages/types/src/index.ts @@ -0,0 +1,2 @@ +export * from "./types.js" +export * from "./api.js" diff --git a/src/schemas/index.ts b/packages/types/src/types.ts similarity index 94% rename from src/schemas/index.ts rename to packages/types/src/types.ts index 4fb893ae1f..0bb2f71de2 100644 --- a/src/schemas/index.ts +++ b/packages/types/src/types.ts @@ -1,30 +1,16 @@ -// Updates to this file will automatically propgate to src/exports/types.ts -// via a pre-commit hook. If you want to update the types before committing you -// can run `pnpm generate-types`. - import { z } from "zod" -import { Equals, Keys, AssertEqual } from "../utils/type-fu" - /** - * Extension + * TS */ -import { publisher, name, version } from "../package.json" +export type Keys = keyof T -// These ENV variables can be defined by ESBuild when building the extension -// in order to override the values in package.json. This allows us to build -// different extension variants with the same package.json file. -// The build process still needs to emit a modified package.json for consumption -// by VSCode, but that build artifact is not used during the transpile step of -// the build, so we still need this override mechanism. -export const Package = { - publisher, - name: process.env.PKG_NAME || name, - version: process.env.PKG_VERSION || version, - outputChannel: process.env.PKG_OUTPUT_CHANNEL || "Roo-Code", - sha: process.env.PKG_SHA, -} as const +export type Values = T[keyof T] + +export type Equals = (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? true : false + +export type AssertEqual = T /** * CodeAction @@ -244,7 +230,7 @@ export const codebaseIndexModelsSchema = z.object({ export type CodebaseIndexModels = z.infer export const codebaseIndexProviderSchema = z.object({ - codeIndexOpenAiKey: z.string().optional(), + codeIndexOpenAiKey: z.string().optional(), codeIndexQdrantApiKey: z.string().optional(), }) @@ -661,7 +647,7 @@ export const providerSettingsSchema = z.object({ ...groqSchema.shape, ...chutesSchema.shape, ...litellmSchema.shape, - ...codebaseIndexProviderSchema.shape + ...codebaseIndexProviderSchema.shape, }) export type ProviderSettings = z.infer @@ -1356,28 +1342,3 @@ export const ipcMessageSchema = z.discriminatedUnion("type", [ ]) export type IpcMessage = z.infer - -/** - * TypeDefinition - */ - -export type TypeDefinition = { - schema: z.ZodTypeAny - identifier: string -} - -export const typeDefinitions: TypeDefinition[] = [ - { schema: globalSettingsSchema, identifier: "GlobalSettings" }, - { schema: providerNamesSchema, identifier: "ProviderName" }, - { schema: providerSettingsSchema, identifier: "ProviderSettings" }, - { schema: providerSettingsEntrySchema, identifier: "ProviderSettingsEntry" }, - { schema: clineMessageSchema, identifier: "ClineMessage" }, - { schema: tokenUsageSchema, identifier: "TokenUsage" }, - { schema: rooCodeEventsSchema, identifier: "RooCodeEvents" }, - { schema: ipcMessageSchema, identifier: "IpcMessage" }, - { schema: taskCommandSchema, identifier: "TaskCommand" }, - { schema: taskEventSchema, identifier: "TaskEvent" }, -] - -// Also export as default for ESM compatibility. -export default { typeDefinitions } diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json new file mode 100644 index 0000000000..a66434e570 --- /dev/null +++ b/packages/types/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@roo-code/config-typescript/base.json", + "compilerOptions": { + "types": ["vitest/globals"], + "outDir": "dist" + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/types/tsup.config.ts b/packages/types/tsup.config.ts new file mode 100644 index 0000000000..9c96eb1901 --- /dev/null +++ b/packages/types/tsup.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "tsup" + +export default defineConfig({ + entry: ["src/index.ts"], + format: ["cjs", "esm"], + dts: true, + clean: false, + splitting: false, + sourcemap: true, + outDir: "dist", +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1cc1b60fe6..d7fc259cab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -63,8 +63,8 @@ importers: specifier: workspace:^ version: link:../../packages/config-typescript '@roo-code/types': - specifier: ^1.12.0 - version: 1.12.0 + specifier: workspace:^ + version: link:../../packages/types '@types/mocha': specifier: ^10.0.10 version: 10.0.10 @@ -153,6 +153,28 @@ importers: packages/config-typescript: {} + packages/types: + dependencies: + zod: + specifier: ^3.24.2 + version: 3.24.4 + devDependencies: + '@roo-code/config-eslint': + specifier: workspace:^ + version: link:../config-eslint + '@roo-code/config-typescript': + specifier: workspace:^ + version: link:../config-typescript + '@types/node': + specifier: ^22.15.20 + version: 22.15.20 + tsup: + specifier: ^8.3.5 + version: 8.5.0(jiti@2.4.2)(postcss@8.5.3)(tsx@4.19.4)(typescript@5.8.3)(yaml@2.8.0) + vitest: + specifier: ^3.1.3 + version: 3.1.3(@types/debug@4.1.12)(@types/node@22.15.20)(jiti@2.4.2)(jsdom@20.0.3)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0) + src: dependencies: '@anthropic-ai/bedrock-sdk': @@ -166,22 +188,25 @@ importers: version: 0.7.0 '@aws-sdk/client-bedrock-runtime': specifier: ^3.779.0 - version: 3.808.0 + version: 3.817.0 '@aws-sdk/credential-providers': specifier: ^3.806.0 - version: 3.808.0 + version: 3.817.0 '@google/genai': specifier: ^0.13.0 version: 0.13.0 '@mistralai/mistralai': specifier: ^1.3.6 - version: 1.6.0(zod@3.24.4) + version: 1.6.1(zod@3.24.4) '@modelcontextprotocol/sdk': specifier: ^1.9.0 - version: 1.11.2 + version: 1.12.0 '@qdrant/js-client-rest': specifier: ^1.14.0 version: 1.14.0(typescript@5.8.3) + '@roo-code/types': + specifier: workspace:^ + version: link:../packages/types '@types/lodash.debounce': specifier: ^4.0.9 version: 4.0.9 @@ -259,10 +284,10 @@ importers: version: 12.0.0 openai: specifier: ^4.78.1 - version: 4.98.0(ws@8.18.2)(zod@3.24.4) + version: 4.103.0(ws@8.18.2)(zod@3.24.4) os-name: specifier: ^6.0.0 - version: 6.0.0 + version: 6.1.0 p-limit: specifier: ^6.2.0 version: 6.2.0 @@ -277,7 +302,7 @@ importers: version: 4.1.0 posthog-node: specifier: ^4.7.0 - version: 4.17.1 + version: 4.17.2 pretty-bytes: specifier: ^6.1.1 version: 6.1.1 @@ -383,7 +408,7 @@ importers: version: 10.0.10 '@types/node': specifier: 20.x - version: 20.17.47 + version: 20.17.50 '@types/node-cache': specifier: ^4.1.3 version: 4.2.5 @@ -422,7 +447,7 @@ importers: version: 11.0.2 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.17.47)(babel-plugin-macros@3.1.0) + version: 29.7.0(@types/node@20.17.50)(babel-plugin-macros@3.1.0) jest-simple-dot-reporter: specifier: ^1.0.5 version: 1.0.5 @@ -434,7 +459,7 @@ importers: version: 14.0.4 npm-run-all2: specifier: ^8.0.1 - version: 8.0.1 + version: 8.0.3 ovsx: specifier: 0.10.2 version: 0.10.2 @@ -443,10 +468,10 @@ importers: version: 6.0.1 ts-jest: specifier: ^29.2.5 - version: 29.3.3(@babel/core@7.27.1)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.1))(esbuild@0.25.4)(jest@29.7.0(@types/node@20.17.47)(babel-plugin-macros@3.1.0))(typescript@5.8.3) + version: 29.3.3(@babel/core@7.27.1)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.1))(esbuild@0.25.4)(jest@29.7.0(@types/node@20.17.50)(babel-plugin-macros@3.1.0))(typescript@5.8.3) tsup: specifier: ^8.4.0 - version: 8.4.0(jiti@2.4.2)(postcss@8.5.3)(tsx@4.19.4)(typescript@5.8.3)(yaml@2.8.0) + version: 8.5.0(jiti@2.4.2)(postcss@8.5.3)(tsx@4.19.4)(typescript@5.8.3)(yaml@2.8.0) tsx: specifier: ^4.19.3 version: 4.19.4 @@ -455,7 +480,7 @@ importers: version: 5.8.3 vitest: specifier: ^3.1.3 - version: 3.1.3(@types/debug@4.1.12)(@types/node@20.17.47)(jiti@2.4.2)(jsdom@20.0.3)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0) + version: 3.1.3(@types/debug@4.1.12)(@types/node@20.17.50)(jiti@2.4.2)(jsdom@20.0.3)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0) zod-to-ts: specifier: ^1.2.0 version: 1.2.0(typescript@5.8.3)(zod@3.24.4) @@ -504,6 +529,9 @@ importers: '@radix-ui/react-tooltip': specifier: ^1.1.8 version: 1.2.6(@types/react-dom@18.3.7(@types/react@18.3.21))(@types/react@18.3.21)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@roo-code/types': + specifier: workspace:^ + version: link:../packages/types '@tailwindcss/vite': specifier: ^4.0.0 version: 4.1.6(vite@6.3.5(@types/node@18.19.100)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0)) @@ -766,56 +794,56 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-bedrock-runtime@3.808.0': - resolution: {integrity: sha512-OzjqAlevqurwAPiBGO++90pvpJCyjK6UrQH2av7oTwAwWYpY/wqVCGjch/pkme6G2+o76FjPvUKxfEcBu+5pKQ==} + '@aws-sdk/client-bedrock-runtime@3.817.0': + resolution: {integrity: sha512-fG3QAjIEq7P0a134E2P8r4qw/V6rL0X5voUPIcXte1oNKUXUjNXJb21N/NGmcDLCUVWvYXb24dD0YXyQ2kwZdA==} engines: {node: '>=18.0.0'} - '@aws-sdk/client-cognito-identity@3.808.0': - resolution: {integrity: sha512-M9pdFQ+Efl1O4No6R7uMEOkidKVUiNsmN13EyzuIOGech9g+RF+LgDn3n8+PuC7EIgndQVe6sQ6w39sPQdBkww==} + '@aws-sdk/client-cognito-identity@3.817.0': + resolution: {integrity: sha512-MNGwOJDQU0jpvsLLPSuPQDhPtDzFTc/k7rLmiKoPrIlgb3Y8pSF4crpJ+ZH3+xod2NWyyOVMEMQeMaKFFdMaKw==} engines: {node: '>=18.0.0'} - '@aws-sdk/client-sso@3.808.0': - resolution: {integrity: sha512-NxGomD0x9q30LPOXf4x7haOm6l2BJdLEzpiC/bPEXUkf2+4XudMQumMA/hDfErY5hCE19mFAouoO465m3Gl3JQ==} + '@aws-sdk/client-sso@3.817.0': + resolution: {integrity: sha512-fCh5rUHmWmWDvw70NNoWpE5+BRdtNi45kDnIoeoszqVg7UKF79SlG+qYooUT52HKCgDNHqgbWaXxMOSqd2I/OQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/core@3.808.0': - resolution: {integrity: sha512-+nTmxJVIPtAarGq9Fd/uU2qU/Ngfb9EntT0/kwXdKKMI0wU9fQNWi10xSTVeqOtzWERbQpOJgBAdta+v3W7cng==} + '@aws-sdk/core@3.816.0': + resolution: {integrity: sha512-Lx50wjtyarzKpMFV6V+gjbSZDgsA/71iyifbClGUSiNPoIQ4OCV0KVOmAAj7mQRVvGJqUMWKVM+WzK79CjbjWA==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-cognito-identity@3.808.0': - resolution: {integrity: sha512-AbsD/qHyQmyZ+CqJNOaGlnwZaXu8HfndfEiLsIJU/dIf9Wbt7ZtsHSAI/x78awxGohDneMZ6c5vuaRGYL7Z04g==} + '@aws-sdk/credential-provider-cognito-identity@3.817.0': + resolution: {integrity: sha512-+dzgWGmdmMNDdeSF+VvONN+hwqoGKX5A6Z3+siMO4CIoKWN7u5nDOx/JLjTGdVQji3522pJjJ+o9veQJNWOMRg==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-env@3.808.0': - resolution: {integrity: sha512-snPRQnwG9PV4kYHQimo1tenf7P974RcdxkHUThzWSxPEV7HpjxTFYNWGlKbOKBhL4AcgeCVeiZ/j+zveF2lEPA==} + '@aws-sdk/credential-provider-env@3.816.0': + resolution: {integrity: sha512-wUJZwRLe+SxPxRV9AENYBLrJZRrNIo+fva7ZzejsC83iz7hdfq6Rv6B/aHEdPwG/nQC4+q7UUvcRPlomyrpsBA==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-http@3.808.0': - resolution: {integrity: sha512-gNXjlx3BIUeX7QpVqxbjBxG6zm45lC39QvUIo92WzEJd2OTPcR8TU0OTTsgq/lpn2FrKcISj5qXvhWykd41+CA==} + '@aws-sdk/credential-provider-http@3.816.0': + resolution: {integrity: sha512-gcWGzMQ7yRIF+ljTkR8Vzp7727UY6cmeaPrFQrvcFB8PhOqWpf7g0JsgOf5BSaP8CkkSQcTQHc0C5ZYAzUFwPg==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-ini@3.808.0': - resolution: {integrity: sha512-Y53CW0pCvFQQEvtVFwExCCMbTg+6NOl8b3YOuZVzPmVmDoW7M1JIn9IScesqoGERXL3VoXny6nYTsZj+vfpp7Q==} + '@aws-sdk/credential-provider-ini@3.817.0': + resolution: {integrity: sha512-kyEwbQyuXE+phWVzloMdkFv6qM6NOon+asMXY5W0fhDKwBz9zQLObDRWBrvQX9lmqq8BbDL1sCfZjOh82Y+RFw==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-node@3.808.0': - resolution: {integrity: sha512-lASHlXJ6U5Cpnt9Gs+mWaaSmWcEibr1AFGhp+5UNvfyd+UU2Oiwgbo7rYXygmaVDGkbfXEiTkgYtoNOBSddnWQ==} + '@aws-sdk/credential-provider-node@3.817.0': + resolution: {integrity: sha512-b5mz7av0Lhavs1Bz3Zb+jrs0Pki93+8XNctnVO0drBW98x1fM4AR38cWvGbM/w9F9Q0/WEH3TinkmrMPrP4T/w==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-process@3.808.0': - resolution: {integrity: sha512-ZLqp+xsQUatoo8pMozcfLwf/pwfXeIk0w3n0Lo/rWBgT3RcdECmmPCRcnkYBqxHQyE66aS9HiJezZUwMYPqh6w==} + '@aws-sdk/credential-provider-process@3.816.0': + resolution: {integrity: sha512-9Tm+AxMoV2Izvl5b9tyMQRbBwaex8JP06HN7ZeCXgC5sAsSN+o8dsThnEhf8jKN+uBpT6CLWKN1TXuUMrAmW1A==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-sso@3.808.0': - resolution: {integrity: sha512-gWZByAokHX+aps1+syIW/hbKUBrjE2RpPRd/RGQvrBbVVgwsJzsHKsW0zy1B6mgARPG6IahmSUMjNkBCVsiAgw==} + '@aws-sdk/credential-provider-sso@3.817.0': + resolution: {integrity: sha512-gFUAW3VmGvdnueK1bh6TOcRX+j99Xm0men1+gz3cA4RE+rZGNy1Qjj8YHlv0hPwI9OnTPZquvPzA5fkviGREWg==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-web-identity@3.808.0': - resolution: {integrity: sha512-SsGa1Gfa05aJM/qYOtHmfg0OKKW6Fl6kyMCcai63jWDVDYy0QSHcesnqRayJolISkdsVK6bqoWoFcPxiopcFcg==} + '@aws-sdk/credential-provider-web-identity@3.817.0': + resolution: {integrity: sha512-A2kgkS9g6NY0OMT2f2EdXHpL17Ym81NhbGnQ8bRXPqESIi7TFypFD2U6osB2VnsFv+MhwM+Ke4PKXSmLun22/A==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-providers@3.808.0': - resolution: {integrity: sha512-JJvY/gcet+tFw7dGifhTMJ2jfLXCJBR2Tu2rY/ePi+HVUrR//TnWmcm8qGvT1nWiCQ7w9NEhMlJgqKEIM/MkVQ==} + '@aws-sdk/credential-providers@3.817.0': + resolution: {integrity: sha512-i6Q2MyktWHG4YG+EmLlnXTgNVjW9/yeNHSKzF55GTho5fjqfU+t9beJfuMWclanRCifamm3N5e5OCm52rVDdTQ==} engines: {node: '>=18.0.0'} '@aws-sdk/eventstream-handler-node@3.804.0': @@ -838,20 +866,20 @@ packages: resolution: {integrity: sha512-zqHOrvLRdsUdN/ehYfZ9Tf8svhbiLLz5VaWUz22YndFv6m9qaAcijkpAOlKexsv3nLBMJdSdJ6GUTAeIy3BZzw==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-user-agent@3.808.0': - resolution: {integrity: sha512-VckV6l5cf/rL3EtgzSHVTTD4mI0gd8UxDDWbKJsxbQ2bpNPDQG2L1wWGLaolTSzjEJ5f3ijDwQrNDbY9l85Mmg==} + '@aws-sdk/middleware-user-agent@3.816.0': + resolution: {integrity: sha512-bHRSlWZ0xDsFR8E2FwDb//0Ff6wMkVx4O+UKsfyNlAbtqCiiHRt5ANNfKPafr95cN2CCxLxiPvFTFVblQM5TsQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/nested-clients@3.808.0': - resolution: {integrity: sha512-NparPojwoBul7XPCasy4psFMJbw7Ys4bz8lVB93ljEUD4VV7mM7zwK27Uhz20B8mBFGmFEoAprPsVymJcK9Vcw==} + '@aws-sdk/nested-clients@3.817.0': + resolution: {integrity: sha512-vQ2E06A48STJFssueJQgxYD8lh1iGJoLJnHdshRDWOQb8gy1wVQR+a7MkPGhGR6lGoS0SCnF/Qp6CZhnwLsqsQ==} engines: {node: '>=18.0.0'} '@aws-sdk/region-config-resolver@3.808.0': resolution: {integrity: sha512-9x2QWfphkARZY5OGkl9dJxZlSlYM2l5inFeo2bKntGuwg4A4YUe5h7d5yJ6sZbam9h43eBrkOdumx03DAkQF9A==} engines: {node: '>=18.0.0'} - '@aws-sdk/token-providers@3.808.0': - resolution: {integrity: sha512-PsfKanHmnyO7FxowXqxbLQ+QjURCdSGxyhUiSdZbfvlvme/wqaMyIoMV/i4jppndksoSdPbW2kZXjzOqhQF+ew==} + '@aws-sdk/token-providers@3.817.0': + resolution: {integrity: sha512-CYN4/UO0VaqyHf46ogZzNrVX7jI3/CfiuktwKlwtpKA6hjf2+ivfgHSKzPpgPBcSEfiibA/26EeLuMnB6cpSrQ==} engines: {node: '>=18.0.0'} '@aws-sdk/types@3.804.0': @@ -869,8 +897,8 @@ packages: '@aws-sdk/util-user-agent-browser@3.804.0': resolution: {integrity: sha512-KfW6T6nQHHM/vZBBdGn6fMyG/MgX5lq82TDdX4HRQRRuHKLgBWGpKXqqvBwqIaCdXwWHgDrg2VQups6GqOWW2A==} - '@aws-sdk/util-user-agent-node@3.808.0': - resolution: {integrity: sha512-5UmB6u7RBSinXZAVP2iDgqyeVA/odO2SLEcrXaeTCw8ICXEoqF0K+GL36T4iDbzCBOAIugOZ6OcQX5vH3ck5UA==} + '@aws-sdk/util-user-agent-node@3.816.0': + resolution: {integrity: sha512-Q6dxmuj4hL7pudhrneWEQ7yVHIQRBFr0wqKLF1opwOi1cIePuoEbPyJ2jkel6PDEv1YMfvsAKaRshp6eNA8VHg==} engines: {node: '>=18.0.0'} peerDependencies: aws-crt: '>=1.0.0' @@ -1579,16 +1607,16 @@ packages: '@microsoft/fast-web-utilities@5.4.1': resolution: {integrity: sha512-ReWYncndjV3c8D8iq9tp7NcFNc1vbVHvcBFPME2nNFKNbS1XCesYZGlIlf3ot5EmuOXPlrzUHOWzQ2vFpIkqDg==} - '@mistralai/mistralai@1.6.0': - resolution: {integrity: sha512-PQwGV3+n7FbE7Dp3Vnd8DAa3ffx6WuVV966Gfmf4QvzwcO3Mvxpz0SnJ/PjaZcsCwApBCZpNyQzvarAKEQLKeQ==} + '@mistralai/mistralai@1.6.1': + resolution: {integrity: sha512-NFAMamNFSAaLT4YhDrqEjhJALJXSheZdA5jXT6gG5ICCJRk9+WQx7vRQO1sIZNIRP+xpPyROpa7X6ZcufiucIA==} peerDependencies: zod: '>= 3' '@mixmark-io/domino@2.2.0': resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==} - '@modelcontextprotocol/sdk@1.11.2': - resolution: {integrity: sha512-H9vwztj5OAqHg9GockCQC06k1natgcxWQSRpQcPJf6i5+MWBzfKkRtxGbjQf0X2ihii0ffLZCRGbYV2f2bjNCQ==} + '@modelcontextprotocol/sdk@1.12.0': + resolution: {integrity: sha512-m//7RlINx1F3sz3KqwY1WWzVgTcYX52HYk4bJ1hkBXV3zccAEth+jRvG8DBRrdaQuRsPAJOx2MH3zaHNCKL7Zg==} engines: {node: '>=18'} '@mswjs/interceptors@0.38.6': @@ -1635,8 +1663,8 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@puppeteer/browsers@2.10.4': - resolution: {integrity: sha512-9DxbZx+XGMNdjBynIs4BRSz+M3iRDeB7qRcAr6UORFLphCIM2x3DXgOucvADiifcqCE4XePFUKcnaAMyGbrDlQ==} + '@puppeteer/browsers@2.10.5': + resolution: {integrity: sha512-eifa0o+i8dERnngJwKrfp3dEq7ia5XFyoqB17S4gK8GhsQE4/P8nxOfQSE0zQHxzzLo/cmF+7+ywEQ7wK7Fb+w==} engines: {node: '>=18'} hasBin: true @@ -2190,9 +2218,6 @@ packages: cpu: [x64] os: [win32] - '@roo-code/types@1.12.0': - resolution: {integrity: sha512-djdZ4lzsiOc+umX357JvcSwRlAMm05P+8DU58IFyZERmEh8wkm4TglDuaaRVGtQSHw9YGFikqfruLtZSEb7zJQ==} - '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} @@ -2237,66 +2262,66 @@ packages: resolution: {integrity: sha512-wRlta7GuLWpTqtFfGo+nZyOO1vEvewdNR1R4rTxpC8XU6vG/NDyrFBhwLZsqg1NUoR1noVaXJPC/7ZK47QCySw==} engines: {node: '>=14.0.0'} - '@smithy/abort-controller@4.0.2': - resolution: {integrity: sha512-Sl/78VDtgqKxN2+1qduaVE140XF+Xg+TafkncspwM4jFP/LHr76ZHmIY/y3V1M0mMLNk+Je6IGbzxy23RSToMw==} + '@smithy/abort-controller@4.0.3': + resolution: {integrity: sha512-AqXFf6DXnuRBXy4SoK/n1mfgHaKaq36bmkphmD1KO0nHq6xK/g9KHSW4HEsPQUBCGdIEfuJifGHwxFXPIFay9Q==} engines: {node: '>=18.0.0'} - '@smithy/config-resolver@4.1.2': - resolution: {integrity: sha512-7r6mZGwb5LmLJ+zPtkLoznf2EtwEuSWdtid10pjGl/7HefCE4mueOkrfki8JCUm99W6UfP47/r3tbxx9CfBN5A==} + '@smithy/config-resolver@4.1.3': + resolution: {integrity: sha512-N5e7ofiyYDmHxnPnqF8L4KtsbSDwyxFRfDK9bp1d9OyPO4ytRLd0/XxCqi5xVaaqB65v4woW8uey6jND6zxzxQ==} engines: {node: '>=18.0.0'} - '@smithy/core@3.3.3': - resolution: {integrity: sha512-CiJNc0b/WdnttAfQ6uMkxPQ3Z8hG/ba8wF89x9KtBBLDdZk6CX52K4F8hbe94uNbc8LDUuZFtbqfdhM3T21naw==} + '@smithy/core@3.4.0': + resolution: {integrity: sha512-dDYISQo7k0Ml/rXlFIjkTmTcQze/LxhtIRAEmZ6HJ/EI0inVxVEVnrUXJ7jPx6ZP0GHUhFm40iQcCgS5apXIXA==} engines: {node: '>=18.0.0'} - '@smithy/credential-provider-imds@4.0.4': - resolution: {integrity: sha512-jN6M6zaGVyB8FmNGG+xOPQB4N89M1x97MMdMnm1ESjljLS3Qju/IegQizKujaNcy2vXAvrz0en8bobe6E55FEA==} + '@smithy/credential-provider-imds@4.0.5': + resolution: {integrity: sha512-saEAGwrIlkb9XxX/m5S5hOtzjoJPEK6Qw2f9pYTbIsMPOFyGSXBBTw95WbOyru8A1vIS2jVCCU1Qhz50QWG3IA==} engines: {node: '>=18.0.0'} '@smithy/eventstream-codec@2.2.0': resolution: {integrity: sha512-8janZoJw85nJmQZc4L8TuePp2pk1nxLgkxIR0TUjKJ5Dkj5oelB9WtiSSGXCQvNsJl0VSTvK/2ueMXxvpa9GVw==} - '@smithy/eventstream-codec@4.0.2': - resolution: {integrity: sha512-p+f2kLSK7ZrXVfskU/f5dzksKTewZk8pJLPvER3aFHPt76C2MxD9vNatSfLzzQSQB4FNO96RK4PSXfhD1TTeMQ==} + '@smithy/eventstream-codec@4.0.3': + resolution: {integrity: sha512-V22KIPXZsE2mc4zEgYGANM/7UbL9jWlOACEolyGyMuTY+jjHJ2PQ0FdopOTS1CS7u6PlAkALmypkv2oQ4aftcg==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-browser@4.0.2': - resolution: {integrity: sha512-CepZCDs2xgVUtH7ZZ7oDdZFH8e6Y2zOv8iiX6RhndH69nlojCALSKK+OXwZUgOtUZEUaZ5e1hULVCHYbCn7pug==} + '@smithy/eventstream-serde-browser@4.0.3': + resolution: {integrity: sha512-oe1d/tfCGVZBMX8O6HApaM4G+fF9JNdyLP7tWXt00epuL/kLOdp/4o9VqheLFeJaXgao+9IaBgs/q/oM48hxzg==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-config-resolver@4.1.0': - resolution: {integrity: sha512-1PI+WPZ5TWXrfj3CIoKyUycYynYJgZjuQo8U+sphneOtjsgrttYybdqESFReQrdWJ+LKt6NEdbYzmmfDBmjX2A==} + '@smithy/eventstream-serde-config-resolver@4.1.1': + resolution: {integrity: sha512-XXCPGjRNwpFWHKQJMKIjGLfFKYULYckFnxGcWmBC2mBf3NsrvUKgqHax4NCqc0TfbDAimPDHOc6HOKtzsXK9Gw==} engines: {node: '>=18.0.0'} '@smithy/eventstream-serde-node@2.2.0': resolution: {integrity: sha512-zpQMtJVqCUMn+pCSFcl9K/RPNtQE0NuMh8sKpCdEHafhwRsjP50Oq/4kMmvxSRy6d8Jslqd8BLvDngrUtmN9iA==} engines: {node: '>=14.0.0'} - '@smithy/eventstream-serde-node@4.0.2': - resolution: {integrity: sha512-C5bJ/C6x9ENPMx2cFOirspnF9ZsBVnBMtP6BdPl/qYSuUawdGQ34Lq0dMcf42QTjUZgWGbUIZnz6+zLxJlb9aw==} + '@smithy/eventstream-serde-node@4.0.3': + resolution: {integrity: sha512-HOEbRmm9TrikCoFrypYu0J/gC4Lsk8gl5LtOz1G3laD2Jy44+ht2Pd2E9qjNQfhMJIzKDZ/gbuUH0s0v4kWQ0A==} engines: {node: '>=18.0.0'} '@smithy/eventstream-serde-universal@2.2.0': resolution: {integrity: sha512-pvoe/vvJY0mOpuF84BEtyZoYfbehiFj8KKWk1ds2AT0mTLYFVs+7sBJZmioOFdBXKd48lfrx1vumdPdmGlCLxA==} engines: {node: '>=14.0.0'} - '@smithy/eventstream-serde-universal@4.0.2': - resolution: {integrity: sha512-St8h9JqzvnbB52FtckiHPN4U/cnXcarMniXRXTKn0r4b4XesZOGiAyUdj1aXbqqn1icSqBlzzUsCl6nPB018ng==} + '@smithy/eventstream-serde-universal@4.0.3': + resolution: {integrity: sha512-ShOP512CZrYI9n+h64PJ84udzoNHUQtPddyh1j175KNTKsSnMEDNscOWJWyEoLQiuhWWw51lSa+k6ea9ZGXcRg==} engines: {node: '>=18.0.0'} '@smithy/fetch-http-handler@2.5.0': resolution: {integrity: sha512-BOWEBeppWhLn/no/JxUL/ghTfANTjT7kg3Ww2rPqTUY9R4yHPXxJ9JhMe3Z03LN3aPwiwlpDIUcVw1xDyHqEhw==} - '@smithy/fetch-http-handler@5.0.2': - resolution: {integrity: sha512-+9Dz8sakS9pe7f2cBocpJXdeVjMopUDLgZs1yWeu7h++WqSbjUYv/JAJwKwXw1HV6gq1jyWjxuyn24E2GhoEcQ==} + '@smithy/fetch-http-handler@5.0.3': + resolution: {integrity: sha512-yBZwavI31roqTndNI7ONHqesfH01JmjJK6L3uUpZAhyAmr86LN5QiPzfyZGIxQmed8VEK2NRSQT3/JX5V1njfQ==} engines: {node: '>=18.0.0'} - '@smithy/hash-node@4.0.2': - resolution: {integrity: sha512-VnTpYPnRUE7yVhWozFdlxcYknv9UN7CeOqSrMH+V877v4oqtVYuoqhIhtSjmGPvYrYnAkaM61sLMKHvxL138yg==} + '@smithy/hash-node@4.0.3': + resolution: {integrity: sha512-W5Uhy6v/aYrgtjh9y0YP332gIQcwccQ+EcfWhllL0B9rPae42JngTTUpb8W6wuxaNFzqps4xq5klHckSSOy5fw==} engines: {node: '>=18.0.0'} - '@smithy/invalid-dependency@4.0.2': - resolution: {integrity: sha512-GatB4+2DTpgWPday+mnUkoumP54u/MDM/5u44KF9hIu8jF0uafZtQLcdfIKkIcUNuF/fBojpLEHZS/56JqPeXQ==} + '@smithy/invalid-dependency@4.0.3': + resolution: {integrity: sha512-1Bo8Ur1ZGqxvwTqBmv6DZEn0rXtwJGeqiiO2/JFcCtz3nBakOqeXbJBElXJMMzd0ghe8+eB6Dkw98nMYctgizg==} engines: {node: '>=18.0.0'} '@smithy/is-array-buffer@2.2.0': @@ -2311,112 +2336,112 @@ packages: resolution: {integrity: sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==} engines: {node: '>=18.0.0'} - '@smithy/middleware-content-length@4.0.2': - resolution: {integrity: sha512-hAfEXm1zU+ELvucxqQ7I8SszwQ4znWMbNv6PLMndN83JJN41EPuS93AIyh2N+gJ6x8QFhzSO6b7q2e6oClDI8A==} + '@smithy/middleware-content-length@4.0.3': + resolution: {integrity: sha512-NE/Zph4BP5u16bzYq2csq9qD0T6UBLeg4AuNrwNJ7Gv9uLYaGEgelZUOdRndGdMGcUfSGvNlXGb2aA2hPCwJ6g==} engines: {node: '>=18.0.0'} '@smithy/middleware-endpoint@2.5.1': resolution: {integrity: sha512-1/8kFp6Fl4OsSIVTWHnNjLnTL8IqpIb/D3sTSczrKFnrE9VMNWxnrRKNvpUHOJ6zpGD5f62TPm7+17ilTJpiCQ==} engines: {node: '>=14.0.0'} - '@smithy/middleware-endpoint@4.1.6': - resolution: {integrity: sha512-Zdieg07c3ua3ap5ungdcyNnY1OsxmsXXtKDTk28+/YbwIPju0Z1ZX9X5AnkjmDE3+AbqgvhtC/ZuCMSr6VSfPw==} + '@smithy/middleware-endpoint@4.1.7': + resolution: {integrity: sha512-KDzM7Iajo6K7eIWNNtukykRT4eWwlHjCEsULZUaSfi/SRSBK8BPRqG5FsVfp58lUxcvre8GT8AIPIqndA0ERKw==} engines: {node: '>=18.0.0'} - '@smithy/middleware-retry@4.1.7': - resolution: {integrity: sha512-lFIFUJ0E/4I0UaIDY5usNUzNKAghhxO0lDH4TZktXMmE+e4ActD9F154Si0Unc01aCPzcwd+NcOwQw6AfXXRRQ==} + '@smithy/middleware-retry@4.1.8': + resolution: {integrity: sha512-e2OtQgFzzlSG0uCjcJmi02QuFSRTrpT11Eh2EcqqDFy7DYriteHZJkkf+4AsxsrGDugAtPFcWBz1aq06sSX5fQ==} engines: {node: '>=18.0.0'} '@smithy/middleware-serde@2.3.0': resolution: {integrity: sha512-sIADe7ojwqTyvEQBe1nc/GXB9wdHhi9UwyX0lTyttmUWDJLP655ZYE1WngnNyXREme8I27KCaUhyhZWRXL0q7Q==} engines: {node: '>=14.0.0'} - '@smithy/middleware-serde@4.0.5': - resolution: {integrity: sha512-yREC3q/HXqQigq29xX3hiy6tFi+kjPKXoYUQmwQdgPORLbQ0n6V2Z/Iw9Nnlu66da9fM/WhDtGvYvqwecrCljQ==} + '@smithy/middleware-serde@4.0.6': + resolution: {integrity: sha512-YECyl7uNII+jCr/9qEmCu8xYL79cU0fqjo0qxpcVIU18dAPHam/iYwcknAu4Jiyw1uN+sAx7/SMf/Kmef/Jjsg==} engines: {node: '>=18.0.0'} '@smithy/middleware-stack@2.2.0': resolution: {integrity: sha512-Qntc3jrtwwrsAC+X8wms8zhrTr0sFXnyEGhZd9sLtsJ/6gGQKFzNB+wWbOcpJd7BR8ThNCoKt76BuQahfMvpeA==} engines: {node: '>=14.0.0'} - '@smithy/middleware-stack@4.0.2': - resolution: {integrity: sha512-eSPVcuJJGVYrFYu2hEq8g8WWdJav3sdrI4o2c6z/rjnYDd3xH9j9E7deZQCzFn4QvGPouLngH3dQ+QVTxv5bOQ==} + '@smithy/middleware-stack@4.0.3': + resolution: {integrity: sha512-baeV7t4jQfQtFxBADFmnhmqBmqR38dNU5cvEgHcMK/Kp3D3bEI0CouoX2Sr/rGuntR+Eg0IjXdxnGGTc6SbIkw==} engines: {node: '>=18.0.0'} '@smithy/node-config-provider@2.3.0': resolution: {integrity: sha512-0elK5/03a1JPWMDPaS726Iw6LpQg80gFut1tNpPfxFuChEEklo2yL823V94SpTZTxmKlXFtFgsP55uh3dErnIg==} engines: {node: '>=14.0.0'} - '@smithy/node-config-provider@4.1.1': - resolution: {integrity: sha512-1slS5jf5icHETwl5hxEVBj+mh6B+LbVW4yRINsGtUKH+nxM5Pw2H59+qf+JqYFCHp9jssG4vX81f5WKnjMN3Vw==} + '@smithy/node-config-provider@4.1.2': + resolution: {integrity: sha512-SUvNup8iU1v7fmM8XPk+27m36udmGCfSz+VZP5Gb0aJ3Ne0X28K/25gnsrg3X1rWlhcnhzNUUysKW/Ied46ivQ==} engines: {node: '>=18.0.0'} '@smithy/node-http-handler@2.5.0': resolution: {integrity: sha512-mVGyPBzkkGQsPoxQUbxlEfRjrj6FPyA3u3u2VXGr9hT8wilsoQdZdvKpMBFMB8Crfhv5dNkKHIW0Yyuc7eABqA==} engines: {node: '>=14.0.0'} - '@smithy/node-http-handler@4.0.4': - resolution: {integrity: sha512-/mdqabuAT3o/ihBGjL94PUbTSPSRJ0eeVTdgADzow0wRJ0rN4A27EOrtlK56MYiO1fDvlO3jVTCxQtQmK9dZ1g==} + '@smithy/node-http-handler@4.0.5': + resolution: {integrity: sha512-T7QglZC1vS7SPT44/1qSIAQEx5bFKb3LfO6zw/o4Xzt1eC5HNoH1TkS4lMYA9cWFbacUhx4hRl/blLun4EOCkg==} engines: {node: '>=18.0.0'} '@smithy/property-provider@2.2.0': resolution: {integrity: sha512-+xiil2lFhtTRzXkx8F053AV46QnIw6e7MV8od5Mi68E1ICOjCeCHw2XfLnDEUHnT9WGUIkwcqavXjfwuJbGlpg==} engines: {node: '>=14.0.0'} - '@smithy/property-provider@4.0.2': - resolution: {integrity: sha512-wNRoQC1uISOuNc2s4hkOYwYllmiyrvVXWMtq+TysNRVQaHm4yoafYQyjN/goYZS+QbYlPIbb/QRjaUZMuzwQ7A==} + '@smithy/property-provider@4.0.3': + resolution: {integrity: sha512-Wcn17QNdawJZcZZPBuMuzyBENVi1AXl4TdE0jvzo4vWX2x5df/oMlmr/9M5XAAC6+yae4kWZlOYIsNsgDrMU9A==} engines: {node: '>=18.0.0'} '@smithy/protocol-http@3.3.0': resolution: {integrity: sha512-Xy5XK1AFWW2nlY/biWZXu6/krgbaf2dg0q492D8M5qthsnU2H+UgFeZLbM76FnH7s6RO/xhQRkj+T6KBO3JzgQ==} engines: {node: '>=14.0.0'} - '@smithy/protocol-http@5.1.0': - resolution: {integrity: sha512-KxAOL1nUNw2JTYrtviRRjEnykIDhxc84qMBzxvu1MUfQfHTuBlCG7PA6EdVwqpJjH7glw7FqQoFxUJSyBQgu7g==} + '@smithy/protocol-http@5.1.1': + resolution: {integrity: sha512-Vsay2mzq05DwNi9jK01yCFtfvu9HimmgC7a4HTs7lhX12Sx8aWsH0mfz6q/02yspSp+lOB+Q2HJwi4IV2GKz7A==} engines: {node: '>=18.0.0'} '@smithy/querystring-builder@2.2.0': resolution: {integrity: sha512-L1kSeviUWL+emq3CUVSgdogoM/D9QMFaqxL/dd0X7PCNWmPXqt+ExtrBjqT0V7HLN03Vs9SuiLrG3zy3JGnE5A==} engines: {node: '>=14.0.0'} - '@smithy/querystring-builder@4.0.2': - resolution: {integrity: sha512-NTOs0FwHw1vimmQM4ebh+wFQvOwkEf/kQL6bSM1Lock+Bv4I89B3hGYoUEPkmvYPkDKyp5UdXJYu+PoTQ3T31Q==} + '@smithy/querystring-builder@4.0.3': + resolution: {integrity: sha512-UUzIWMVfPmDZcOutk2/r1vURZqavvQW0OHvgsyNV0cKupChvqg+/NKPRMaMEe+i8tP96IthMFeZOZWpV+E4RAw==} engines: {node: '>=18.0.0'} '@smithy/querystring-parser@2.2.0': resolution: {integrity: sha512-BvHCDrKfbG5Yhbpj4vsbuPV2GgcpHiAkLeIlcA1LtfpMz3jrqizP1+OguSNSj1MwBHEiN+jwNisXLGdajGDQJA==} engines: {node: '>=14.0.0'} - '@smithy/querystring-parser@4.0.2': - resolution: {integrity: sha512-v6w8wnmZcVXjfVLjxw8qF7OwESD9wnpjp0Dqry/Pod0/5vcEA3qxCr+BhbOHlxS8O+29eLpT3aagxXGwIoEk7Q==} + '@smithy/querystring-parser@4.0.3': + resolution: {integrity: sha512-K5M4ZJQpFCblOJ5Oyw7diICpFg1qhhR47m2/5Ef1PhGE19RaIZf50tjYFrxa6usqcuXyTiFPGo4d1geZdH4YcQ==} engines: {node: '>=18.0.0'} - '@smithy/service-error-classification@4.0.3': - resolution: {integrity: sha512-FTbcajmltovWMjj3tksDQdD23b2w6gH+A0DYA1Yz3iSpjDj8fmkwy62UnXcWMy4d5YoMoSyLFHMfkEVEzbiN8Q==} + '@smithy/service-error-classification@4.0.4': + resolution: {integrity: sha512-W5ScbQ1bTzgH91kNEE2CvOzM4gXlDOqdow4m8vMFSIXCel2scbHwjflpVNnC60Y3F1m5i7w2gQg9lSnR+JsJAA==} engines: {node: '>=18.0.0'} '@smithy/shared-ini-file-loader@2.4.0': resolution: {integrity: sha512-WyujUJL8e1B6Z4PBfAqC/aGY1+C7T0w20Gih3yrvJSk97gpiVfB+y7c46T4Nunk+ZngLq0rOIdeVeIklk0R3OA==} engines: {node: '>=14.0.0'} - '@smithy/shared-ini-file-loader@4.0.2': - resolution: {integrity: sha512-J9/gTWBGVuFZ01oVA6vdb4DAjf1XbDhK6sLsu3OS9qmLrS6KB5ygpeHiM3miIbj1qgSJ96GYszXFWv6ErJ8QEw==} + '@smithy/shared-ini-file-loader@4.0.3': + resolution: {integrity: sha512-vHwlrqhZGIoLwaH8vvIjpHnloShqdJ7SUPNM2EQtEox+yEDFTVQ7E+DLZ+6OhnYEgFUwPByJyz6UZaOu2tny6A==} engines: {node: '>=18.0.0'} '@smithy/signature-v4@3.1.2': resolution: {integrity: sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA==} engines: {node: '>=16.0.0'} - '@smithy/signature-v4@5.1.0': - resolution: {integrity: sha512-4t5WX60sL3zGJF/CtZsUQTs3UrZEDO2P7pEaElrekbLqkWPYkgqNW1oeiNYC6xXifBnT9dVBOnNQRvOE9riU9w==} + '@smithy/signature-v4@5.1.1': + resolution: {integrity: sha512-zy8Repr5zvT0ja+Tf5wjV/Ba6vRrhdiDcp/ww6cvqYbSEudIkziDe3uppNRlFoCViyJXdPnLcwyZdDLA4CHzSg==} engines: {node: '>=18.0.0'} '@smithy/smithy-client@2.5.1': resolution: {integrity: sha512-jrbSQrYCho0yDaaf92qWgd+7nAeap5LtHTI51KXqmpIFCceKU3K9+vIVTUH72bOJngBMqa4kyu1VJhRcSrk/CQ==} engines: {node: '>=14.0.0'} - '@smithy/smithy-client@4.2.6': - resolution: {integrity: sha512-WEqP0wQ1N/lVS4pwNK1Vk+0i6QIr66cq/xbu1dVy1tM0A0qYwAYyz0JhbquzM5pMa8s89lyDBtoGKxo7iG74GA==} + '@smithy/smithy-client@4.3.0': + resolution: {integrity: sha512-DNsRA38pN6tYHUjebmwD9e4KcgqTLldYQb2gC6K+oxXYdCTxPn6wV9+FvOa6wrU2FQEnGJoi+3GULzOTKck/tg==} engines: {node: '>=18.0.0'} '@smithy/types@2.12.0': @@ -2427,15 +2452,15 @@ packages: resolution: {integrity: sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==} engines: {node: '>=16.0.0'} - '@smithy/types@4.2.0': - resolution: {integrity: sha512-7eMk09zQKCO+E/ivsjQv+fDlOupcFUCSC/L2YUPgwhvowVGWbPQHjEFcmjt7QQ4ra5lyowS92SV53Zc6XD4+fg==} + '@smithy/types@4.3.0': + resolution: {integrity: sha512-+1iaIQHthDh9yaLhRzaoQxRk+l9xlk+JjMFxGRhNLz+m9vKOkjNeU8QuB4w3xvzHyVR/BVlp/4AXDHjoRIkfgQ==} engines: {node: '>=18.0.0'} '@smithy/url-parser@2.2.0': resolution: {integrity: sha512-hoA4zm61q1mNTpksiSWp2nEl1dt3j726HdRhiNgVJQMj7mLp7dprtF57mOB6JvEk/x9d2bsuL5hlqZbBuHQylQ==} - '@smithy/url-parser@4.0.2': - resolution: {integrity: sha512-Bm8n3j2ScqnT+kJaClSVCMeiSenK6jVAzZCNewsYWuZtnBehEz4r2qP0riZySZVfzB+03XZHJeqfmJDkeeSLiQ==} + '@smithy/url-parser@4.0.3': + resolution: {integrity: sha512-n5/DnosDu/tweOqUUNtUbu7eRIR4J/Wz9nL7V5kFYQQVb8VYdj7a4G5NJHCw6o21ul7CvZoJkOpdTnsQDLT0tQ==} engines: {node: '>=18.0.0'} '@smithy/util-base64@2.3.0': @@ -2470,16 +2495,16 @@ packages: resolution: {integrity: sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-browser@4.0.14': - resolution: {integrity: sha512-l7QnMX8VcDOH6n/fBRu4zqguSlOBZxFzWqp58dXFSARFBjNlmEDk5G/z4T7BMGr+rI0Pg8MkhmMUfEtHFgpy2g==} + '@smithy/util-defaults-mode-browser@4.0.15': + resolution: {integrity: sha512-bJJ/B8owQbHAflatSq92f9OcV8858DJBQF1Y3GRjB8psLyUjbISywszYPFw16beREHO/C3I3taW4VGH+tOuwrQ==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-node@4.0.14': - resolution: {integrity: sha512-Ujs1gsWDo3m/T63VWBTBmHLTD2UlU6J6FEokLCEp7OZQv45jcjLHoxTwgWsi8ULpsYozvH4MTWkRP+bhwr0vDg==} + '@smithy/util-defaults-mode-node@4.0.15': + resolution: {integrity: sha512-8CUrEW2Ni5q+NmYkj8wsgkfqoP7l4ZquptFbq92yQE66xevc4SxqP2zH6tMtN158kgBqBDsZ+qlrRwXWOjCR8A==} engines: {node: '>=18.0.0'} - '@smithy/util-endpoints@3.0.4': - resolution: {integrity: sha512-VfFATC1bmZLV2858B/O1NpMcL32wYo8DPPhHxYxDCodDl3f3mSZ5oJheW1IF91A0EeAADz2WsakM/hGGPGNKLg==} + '@smithy/util-endpoints@3.0.5': + resolution: {integrity: sha512-PjDpqLk24/vAl340tmtCA++Q01GRRNH9cwL9qh46NspAX9S+IQVcK+GOzPt0GLJ6KYGyn8uOgo2kvJhiThclJw==} engines: {node: '>=18.0.0'} '@smithy/util-hex-encoding@2.2.0': @@ -2502,20 +2527,20 @@ packages: resolution: {integrity: sha512-dWpyc1e1R6VoXrwLoLDd57U1z6CwNSdkM69Ie4+6uYh2GC7Vg51Qtan7ITzczuVpqezdDTKJGJB95fFvvjU/ow==} engines: {node: '>=16.0.0'} - '@smithy/util-middleware@4.0.2': - resolution: {integrity: sha512-6GDamTGLuBQVAEuQ4yDQ+ti/YINf/MEmIegrEeg7DdB/sld8BX1lqt9RRuIcABOhAGTA50bRbPzErez7SlDtDQ==} + '@smithy/util-middleware@4.0.3': + resolution: {integrity: sha512-iIsC6qZXxkD7V3BzTw3b1uK8RVC1M8WvwNxK1PKrH9FnxntCd30CSunXjL/8iJBE8Z0J14r2P69njwIpRG4FBQ==} engines: {node: '>=18.0.0'} - '@smithy/util-retry@4.0.3': - resolution: {integrity: sha512-DPuYjZQDXmKr/sNvy9Spu8R/ESa2e22wXZzSAY6NkjOLj6spbIje/Aq8rT97iUMdDj0qHMRIe+bTxvlU74d9Ng==} + '@smithy/util-retry@4.0.4': + resolution: {integrity: sha512-Aoqr9W2jDYGrI6OxljN8VmLDQIGO4VdMAUKMf9RGqLG8hn6or+K41NEy1Y5dtum9q8F7e0obYAuKl2mt/GnpZg==} engines: {node: '>=18.0.0'} '@smithy/util-stream@2.2.0': resolution: {integrity: sha512-17faEXbYWIRst1aU9SvPZyMdWmqIrduZjVOqCPMIsWFNxs5yQQgFrJL6b2SdiCzyW9mJoDjFtgi53xx7EH+BXA==} engines: {node: '>=14.0.0'} - '@smithy/util-stream@4.2.0': - resolution: {integrity: sha512-Vj1TtwWnuWqdgQI6YTUF5hQ/0jmFiOYsc51CSMgj7QfyO+RF4EnT2HNjoviNlOOmgzgvf3f5yno+EiC4vrnaWQ==} + '@smithy/util-stream@4.2.1': + resolution: {integrity: sha512-W3IR0x5DY6iVtjj5p902oNhD+Bz7vs5S+p6tppbPa509rV9BdeXZjGuRSCtVEad9FA0Mba+tNUtUmtnSI1nwUw==} engines: {node: '>=18.0.0'} '@smithy/util-uri-escape@2.2.0': @@ -3032,8 +3057,8 @@ packages: '@types/node@18.19.100': resolution: {integrity: sha512-ojmMP8SZBKprc3qGrGk8Ujpo80AXkrP7G2tOT4VWr5jlr5DHjsJF+emXJz+Wm0glmy4Js62oKMdZZ6B9Y+tEcA==} - '@types/node@20.17.47': - resolution: {integrity: sha512-3dLX0Upo1v7RvUimvxLeXqwrfyKxUINk0EAM83swP2mlSUcwV73sZy8XhNz8bcZ3VbsfQyC/y6jRdL5tgCNpDQ==} + '@types/node@20.17.50': + resolution: {integrity: sha512-Mxiq0ULv/zo1OzOhwPqOA13I81CV/W3nvd3ChtQZRT5Cwz3cr0FKo/wMSsbTqL3EXpaBAEQhva2B8ByRkOIh9A==} '@types/node@22.15.20': resolution: {integrity: sha512-A6BohGFRGHAscJsTslDCA9JG7qSJr/DWUvrvY8yi9IgnGtMxCyat7vvQ//MFa0DnLsyuS3wYTpLdw4Hf+Q5JXw==} @@ -4611,8 +4636,8 @@ packages: eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} - eventsource-parser@3.0.1: - resolution: {integrity: sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA==} + eventsource-parser@3.0.2: + resolution: {integrity: sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA==} engines: {node: '>=18.0.0'} eventsource@3.0.7: @@ -4766,6 +4791,9 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} + fix-dts-default-cjs-exports@1.0.1: + resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} + flat-cache@4.0.1: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} @@ -6466,8 +6494,8 @@ packages: resolution: {integrity: sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==} engines: {node: ^18.17.0 || >=20.5.0} - npm-run-all2@8.0.1: - resolution: {integrity: sha512-jkhE0AsELQeCtScrcJ/7mSIdk+ZsnWjvKk3KwE96HZ6+OFVB74XhxQtHT1W6kdUfn92fRnBb29Mz82j9bV2XEQ==} + npm-run-all2@8.0.3: + resolution: {integrity: sha512-0mAycidMUMThrLt8AT3LGtOMgfLaMg6/4oUKHTKMU0jDSIsdKBsKp98H8zBFcJylQC4CtOB140UUFbOlFyE9gA==} engines: {node: ^20.5.0 || >=22.0.0, npm: '>= 10'} hasBin: true @@ -6561,8 +6589,8 @@ packages: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} - openai@4.98.0: - resolution: {integrity: sha512-TmDKur1WjxxMPQAtLG5sgBSCJmX7ynTsGmewKzoDwl1fRxtbLOsiR0FA/AOAAtYUmP6azal+MYQuOENfdU+7yg==} + openai@4.103.0: + resolution: {integrity: sha512-eWcz9kdurkGOFDtd5ySS5y251H2uBgq9+1a2lTBnjMMzlexJ40Am5t6Mu76SSE87VvitPa0dkIAp75F+dZVC0g==} hasBin: true peerDependencies: ws: ^8.18.0 @@ -6584,8 +6612,8 @@ packages: resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} engines: {node: '>=18'} - os-name@6.0.0: - resolution: {integrity: sha512-bv608E0UX86atYi2GMGjDe0vF/X1TJjemNS8oEW6z22YW1Rc3QykSYoGfkQbX0zZX9H0ZB6CQP/3GTf1I5hURg==} + os-name@6.1.0: + resolution: {integrity: sha512-zBd1G8HkewNd2A8oQ8c6BN/f/c9EId7rSUueOLGu28govmUctXmM+3765GwsByv9nYUdrLqHphXlYIc86saYsg==} engines: {node: '>=18'} os-tmpdir@1.0.2: @@ -6852,8 +6880,8 @@ packages: rrweb-snapshot: optional: true - posthog-node@4.17.1: - resolution: {integrity: sha512-cVlQPOwOPjakUnrueKRCQe1m2Ku+XzKaOos7Tn/zDZkkZFeBT/byP7tbNf7LiwhaBRWFBRowZZb/MsTtSRaorg==} + posthog-node@4.17.2: + resolution: {integrity: sha512-bFmwOTk4QdYavopeHVXtyFGQ9vyLMVaNWkWocwjix+0n6sQgv7Zq5nYjYulz7ThmK18zsvNJ337ahuMLv3ulow==} engines: {node: '>=15.0.0'} preact@10.26.6: @@ -7738,8 +7766,8 @@ packages: tar-fs@2.1.2: resolution: {integrity: sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==} - tar-fs@3.0.8: - resolution: {integrity: sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg==} + tar-fs@3.0.9: + resolution: {integrity: sha512-XF4w9Xp+ZQgifKakjZYmFdkLoSWd34VGKcsTCwlNWM7QG3ZbaxnTsaBwnjFZqHRf/rROxaR8rXnbtwdvaDI+lA==} tar-stream@2.2.0: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} @@ -7919,8 +7947,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsup@8.4.0: - resolution: {integrity: sha512-b+eZbPCjz10fRryaAA7C8xlIHnf8VnsaRqydheLIqwG/Mcpfk8Z5zp3HayX7GaTygkigHl5cBUs+IhcySiIexQ==} + tsup@8.5.0: + resolution: {integrity: sha512-VmBp77lWNQq6PfuMqCHD3xWl22vEoWsKajkF8t+yMBawlUS8JzEI+vOVMeuNZIuMML8qXRizFKi9oD5glKQVcQ==} engines: {node: '>=18'} hasBin: true peerDependencies: @@ -8475,8 +8503,8 @@ packages: wide-align@1.1.5: resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} - windows-release@6.0.1: - resolution: {integrity: sha512-MS3BzG8QK33dAyqwxfYJCJ03arkwKaddUOvvnnlFdXLudflsQF6I8yAxrLBeQk4yO8wjdH/+ax0YzxJEDrOftg==} + windows-release@6.1.0: + resolution: {integrity: sha512-1lOb3qdzw6OFmOzoY0nauhLG72TpWtb5qgYPiSh/62rjc1XidBSDio2qw0pwHh17VINF217ebIkZJdFLZFn9SA==} engines: {node: '>=18'} word-wrap@1.2.5: @@ -8659,8 +8687,8 @@ snapshots: dependencies: '@anthropic-ai/sdk': 0.37.0 '@aws-crypto/sha256-js': 4.0.0 - '@aws-sdk/client-bedrock-runtime': 3.808.0 - '@aws-sdk/credential-providers': 3.808.0 + '@aws-sdk/client-bedrock-runtime': 3.817.0 + '@aws-sdk/credential-providers': 3.817.0 '@smithy/eventstream-serde-node': 2.2.0 '@smithy/fetch-http-handler': 2.5.0 '@smithy/protocol-http': 3.3.0 @@ -8748,51 +8776,51 @@ snapshots: '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-bedrock-runtime@3.808.0': + '@aws-sdk/client-bedrock-runtime@3.817.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.808.0 - '@aws-sdk/credential-provider-node': 3.808.0 + '@aws-sdk/core': 3.816.0 + '@aws-sdk/credential-provider-node': 3.817.0 '@aws-sdk/eventstream-handler-node': 3.804.0 '@aws-sdk/middleware-eventstream': 3.804.0 '@aws-sdk/middleware-host-header': 3.804.0 '@aws-sdk/middleware-logger': 3.804.0 '@aws-sdk/middleware-recursion-detection': 3.804.0 - '@aws-sdk/middleware-user-agent': 3.808.0 + '@aws-sdk/middleware-user-agent': 3.816.0 '@aws-sdk/region-config-resolver': 3.808.0 '@aws-sdk/types': 3.804.0 '@aws-sdk/util-endpoints': 3.808.0 '@aws-sdk/util-user-agent-browser': 3.804.0 - '@aws-sdk/util-user-agent-node': 3.808.0 - '@smithy/config-resolver': 4.1.2 - '@smithy/core': 3.3.3 - '@smithy/eventstream-serde-browser': 4.0.2 - '@smithy/eventstream-serde-config-resolver': 4.1.0 - '@smithy/eventstream-serde-node': 4.0.2 - '@smithy/fetch-http-handler': 5.0.2 - '@smithy/hash-node': 4.0.2 - '@smithy/invalid-dependency': 4.0.2 - '@smithy/middleware-content-length': 4.0.2 - '@smithy/middleware-endpoint': 4.1.6 - '@smithy/middleware-retry': 4.1.7 - '@smithy/middleware-serde': 4.0.5 - '@smithy/middleware-stack': 4.0.2 - '@smithy/node-config-provider': 4.1.1 - '@smithy/node-http-handler': 4.0.4 - '@smithy/protocol-http': 5.1.0 - '@smithy/smithy-client': 4.2.6 - '@smithy/types': 4.2.0 - '@smithy/url-parser': 4.0.2 + '@aws-sdk/util-user-agent-node': 3.816.0 + '@smithy/config-resolver': 4.1.3 + '@smithy/core': 3.4.0 + '@smithy/eventstream-serde-browser': 4.0.3 + '@smithy/eventstream-serde-config-resolver': 4.1.1 + '@smithy/eventstream-serde-node': 4.0.3 + '@smithy/fetch-http-handler': 5.0.3 + '@smithy/hash-node': 4.0.3 + '@smithy/invalid-dependency': 4.0.3 + '@smithy/middleware-content-length': 4.0.3 + '@smithy/middleware-endpoint': 4.1.7 + '@smithy/middleware-retry': 4.1.8 + '@smithy/middleware-serde': 4.0.6 + '@smithy/middleware-stack': 4.0.3 + '@smithy/node-config-provider': 4.1.2 + '@smithy/node-http-handler': 4.0.5 + '@smithy/protocol-http': 5.1.1 + '@smithy/smithy-client': 4.3.0 + '@smithy/types': 4.3.0 + '@smithy/url-parser': 4.0.3 '@smithy/util-base64': 4.0.0 '@smithy/util-body-length-browser': 4.0.0 '@smithy/util-body-length-node': 4.0.0 - '@smithy/util-defaults-mode-browser': 4.0.14 - '@smithy/util-defaults-mode-node': 4.0.14 - '@smithy/util-endpoints': 3.0.4 - '@smithy/util-middleware': 4.0.2 - '@smithy/util-retry': 4.0.3 - '@smithy/util-stream': 4.2.0 + '@smithy/util-defaults-mode-browser': 4.0.15 + '@smithy/util-defaults-mode-node': 4.0.15 + '@smithy/util-endpoints': 3.0.5 + '@smithy/util-middleware': 4.0.3 + '@smithy/util-retry': 4.0.4 + '@smithy/util-stream': 4.2.1 '@smithy/util-utf8': 4.0.0 '@types/uuid': 9.0.8 tslib: 2.8.1 @@ -8800,226 +8828,226 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/client-cognito-identity@3.808.0': + '@aws-sdk/client-cognito-identity@3.817.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.808.0 - '@aws-sdk/credential-provider-node': 3.808.0 + '@aws-sdk/core': 3.816.0 + '@aws-sdk/credential-provider-node': 3.817.0 '@aws-sdk/middleware-host-header': 3.804.0 '@aws-sdk/middleware-logger': 3.804.0 '@aws-sdk/middleware-recursion-detection': 3.804.0 - '@aws-sdk/middleware-user-agent': 3.808.0 + '@aws-sdk/middleware-user-agent': 3.816.0 '@aws-sdk/region-config-resolver': 3.808.0 '@aws-sdk/types': 3.804.0 '@aws-sdk/util-endpoints': 3.808.0 '@aws-sdk/util-user-agent-browser': 3.804.0 - '@aws-sdk/util-user-agent-node': 3.808.0 - '@smithy/config-resolver': 4.1.2 - '@smithy/core': 3.3.3 - '@smithy/fetch-http-handler': 5.0.2 - '@smithy/hash-node': 4.0.2 - '@smithy/invalid-dependency': 4.0.2 - '@smithy/middleware-content-length': 4.0.2 - '@smithy/middleware-endpoint': 4.1.6 - '@smithy/middleware-retry': 4.1.7 - '@smithy/middleware-serde': 4.0.5 - '@smithy/middleware-stack': 4.0.2 - '@smithy/node-config-provider': 4.1.1 - '@smithy/node-http-handler': 4.0.4 - '@smithy/protocol-http': 5.1.0 - '@smithy/smithy-client': 4.2.6 - '@smithy/types': 4.2.0 - '@smithy/url-parser': 4.0.2 + '@aws-sdk/util-user-agent-node': 3.816.0 + '@smithy/config-resolver': 4.1.3 + '@smithy/core': 3.4.0 + '@smithy/fetch-http-handler': 5.0.3 + '@smithy/hash-node': 4.0.3 + '@smithy/invalid-dependency': 4.0.3 + '@smithy/middleware-content-length': 4.0.3 + '@smithy/middleware-endpoint': 4.1.7 + '@smithy/middleware-retry': 4.1.8 + '@smithy/middleware-serde': 4.0.6 + '@smithy/middleware-stack': 4.0.3 + '@smithy/node-config-provider': 4.1.2 + '@smithy/node-http-handler': 4.0.5 + '@smithy/protocol-http': 5.1.1 + '@smithy/smithy-client': 4.3.0 + '@smithy/types': 4.3.0 + '@smithy/url-parser': 4.0.3 '@smithy/util-base64': 4.0.0 '@smithy/util-body-length-browser': 4.0.0 '@smithy/util-body-length-node': 4.0.0 - '@smithy/util-defaults-mode-browser': 4.0.14 - '@smithy/util-defaults-mode-node': 4.0.14 - '@smithy/util-endpoints': 3.0.4 - '@smithy/util-middleware': 4.0.2 - '@smithy/util-retry': 4.0.3 + '@smithy/util-defaults-mode-browser': 4.0.15 + '@smithy/util-defaults-mode-node': 4.0.15 + '@smithy/util-endpoints': 3.0.5 + '@smithy/util-middleware': 4.0.3 + '@smithy/util-retry': 4.0.4 '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.808.0': + '@aws-sdk/client-sso@3.817.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.808.0 + '@aws-sdk/core': 3.816.0 '@aws-sdk/middleware-host-header': 3.804.0 '@aws-sdk/middleware-logger': 3.804.0 '@aws-sdk/middleware-recursion-detection': 3.804.0 - '@aws-sdk/middleware-user-agent': 3.808.0 + '@aws-sdk/middleware-user-agent': 3.816.0 '@aws-sdk/region-config-resolver': 3.808.0 '@aws-sdk/types': 3.804.0 '@aws-sdk/util-endpoints': 3.808.0 '@aws-sdk/util-user-agent-browser': 3.804.0 - '@aws-sdk/util-user-agent-node': 3.808.0 - '@smithy/config-resolver': 4.1.2 - '@smithy/core': 3.3.3 - '@smithy/fetch-http-handler': 5.0.2 - '@smithy/hash-node': 4.0.2 - '@smithy/invalid-dependency': 4.0.2 - '@smithy/middleware-content-length': 4.0.2 - '@smithy/middleware-endpoint': 4.1.6 - '@smithy/middleware-retry': 4.1.7 - '@smithy/middleware-serde': 4.0.5 - '@smithy/middleware-stack': 4.0.2 - '@smithy/node-config-provider': 4.1.1 - '@smithy/node-http-handler': 4.0.4 - '@smithy/protocol-http': 5.1.0 - '@smithy/smithy-client': 4.2.6 - '@smithy/types': 4.2.0 - '@smithy/url-parser': 4.0.2 + '@aws-sdk/util-user-agent-node': 3.816.0 + '@smithy/config-resolver': 4.1.3 + '@smithy/core': 3.4.0 + '@smithy/fetch-http-handler': 5.0.3 + '@smithy/hash-node': 4.0.3 + '@smithy/invalid-dependency': 4.0.3 + '@smithy/middleware-content-length': 4.0.3 + '@smithy/middleware-endpoint': 4.1.7 + '@smithy/middleware-retry': 4.1.8 + '@smithy/middleware-serde': 4.0.6 + '@smithy/middleware-stack': 4.0.3 + '@smithy/node-config-provider': 4.1.2 + '@smithy/node-http-handler': 4.0.5 + '@smithy/protocol-http': 5.1.1 + '@smithy/smithy-client': 4.3.0 + '@smithy/types': 4.3.0 + '@smithy/url-parser': 4.0.3 '@smithy/util-base64': 4.0.0 '@smithy/util-body-length-browser': 4.0.0 '@smithy/util-body-length-node': 4.0.0 - '@smithy/util-defaults-mode-browser': 4.0.14 - '@smithy/util-defaults-mode-node': 4.0.14 - '@smithy/util-endpoints': 3.0.4 - '@smithy/util-middleware': 4.0.2 - '@smithy/util-retry': 4.0.3 + '@smithy/util-defaults-mode-browser': 4.0.15 + '@smithy/util-defaults-mode-node': 4.0.15 + '@smithy/util-endpoints': 3.0.5 + '@smithy/util-middleware': 4.0.3 + '@smithy/util-retry': 4.0.4 '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/core@3.808.0': + '@aws-sdk/core@3.816.0': dependencies: '@aws-sdk/types': 3.804.0 - '@smithy/core': 3.3.3 - '@smithy/node-config-provider': 4.1.1 - '@smithy/property-provider': 4.0.2 - '@smithy/protocol-http': 5.1.0 - '@smithy/signature-v4': 5.1.0 - '@smithy/smithy-client': 4.2.6 - '@smithy/types': 4.2.0 - '@smithy/util-middleware': 4.0.2 + '@smithy/core': 3.4.0 + '@smithy/node-config-provider': 4.1.2 + '@smithy/property-provider': 4.0.3 + '@smithy/protocol-http': 5.1.1 + '@smithy/signature-v4': 5.1.1 + '@smithy/smithy-client': 4.3.0 + '@smithy/types': 4.3.0 + '@smithy/util-middleware': 4.0.3 fast-xml-parser: 4.4.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-cognito-identity@3.808.0': + '@aws-sdk/credential-provider-cognito-identity@3.817.0': dependencies: - '@aws-sdk/client-cognito-identity': 3.808.0 + '@aws-sdk/client-cognito-identity': 3.817.0 '@aws-sdk/types': 3.804.0 - '@smithy/property-provider': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/property-provider': 4.0.3 + '@smithy/types': 4.3.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-env@3.808.0': + '@aws-sdk/credential-provider-env@3.816.0': dependencies: - '@aws-sdk/core': 3.808.0 + '@aws-sdk/core': 3.816.0 '@aws-sdk/types': 3.804.0 - '@smithy/property-provider': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/property-provider': 4.0.3 + '@smithy/types': 4.3.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.808.0': + '@aws-sdk/credential-provider-http@3.816.0': dependencies: - '@aws-sdk/core': 3.808.0 + '@aws-sdk/core': 3.816.0 '@aws-sdk/types': 3.804.0 - '@smithy/fetch-http-handler': 5.0.2 - '@smithy/node-http-handler': 4.0.4 - '@smithy/property-provider': 4.0.2 - '@smithy/protocol-http': 5.1.0 - '@smithy/smithy-client': 4.2.6 - '@smithy/types': 4.2.0 - '@smithy/util-stream': 4.2.0 + '@smithy/fetch-http-handler': 5.0.3 + '@smithy/node-http-handler': 4.0.5 + '@smithy/property-provider': 4.0.3 + '@smithy/protocol-http': 5.1.1 + '@smithy/smithy-client': 4.3.0 + '@smithy/types': 4.3.0 + '@smithy/util-stream': 4.2.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.808.0': + '@aws-sdk/credential-provider-ini@3.817.0': dependencies: - '@aws-sdk/core': 3.808.0 - '@aws-sdk/credential-provider-env': 3.808.0 - '@aws-sdk/credential-provider-http': 3.808.0 - '@aws-sdk/credential-provider-process': 3.808.0 - '@aws-sdk/credential-provider-sso': 3.808.0 - '@aws-sdk/credential-provider-web-identity': 3.808.0 - '@aws-sdk/nested-clients': 3.808.0 + '@aws-sdk/core': 3.816.0 + '@aws-sdk/credential-provider-env': 3.816.0 + '@aws-sdk/credential-provider-http': 3.816.0 + '@aws-sdk/credential-provider-process': 3.816.0 + '@aws-sdk/credential-provider-sso': 3.817.0 + '@aws-sdk/credential-provider-web-identity': 3.817.0 + '@aws-sdk/nested-clients': 3.817.0 '@aws-sdk/types': 3.804.0 - '@smithy/credential-provider-imds': 4.0.4 - '@smithy/property-provider': 4.0.2 - '@smithy/shared-ini-file-loader': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/credential-provider-imds': 4.0.5 + '@smithy/property-provider': 4.0.3 + '@smithy/shared-ini-file-loader': 4.0.3 + '@smithy/types': 4.3.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-node@3.808.0': + '@aws-sdk/credential-provider-node@3.817.0': dependencies: - '@aws-sdk/credential-provider-env': 3.808.0 - '@aws-sdk/credential-provider-http': 3.808.0 - '@aws-sdk/credential-provider-ini': 3.808.0 - '@aws-sdk/credential-provider-process': 3.808.0 - '@aws-sdk/credential-provider-sso': 3.808.0 - '@aws-sdk/credential-provider-web-identity': 3.808.0 + '@aws-sdk/credential-provider-env': 3.816.0 + '@aws-sdk/credential-provider-http': 3.816.0 + '@aws-sdk/credential-provider-ini': 3.817.0 + '@aws-sdk/credential-provider-process': 3.816.0 + '@aws-sdk/credential-provider-sso': 3.817.0 + '@aws-sdk/credential-provider-web-identity': 3.817.0 '@aws-sdk/types': 3.804.0 - '@smithy/credential-provider-imds': 4.0.4 - '@smithy/property-provider': 4.0.2 - '@smithy/shared-ini-file-loader': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/credential-provider-imds': 4.0.5 + '@smithy/property-provider': 4.0.3 + '@smithy/shared-ini-file-loader': 4.0.3 + '@smithy/types': 4.3.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-process@3.808.0': + '@aws-sdk/credential-provider-process@3.816.0': dependencies: - '@aws-sdk/core': 3.808.0 + '@aws-sdk/core': 3.816.0 '@aws-sdk/types': 3.804.0 - '@smithy/property-provider': 4.0.2 - '@smithy/shared-ini-file-loader': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/property-provider': 4.0.3 + '@smithy/shared-ini-file-loader': 4.0.3 + '@smithy/types': 4.3.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.808.0': + '@aws-sdk/credential-provider-sso@3.817.0': dependencies: - '@aws-sdk/client-sso': 3.808.0 - '@aws-sdk/core': 3.808.0 - '@aws-sdk/token-providers': 3.808.0 + '@aws-sdk/client-sso': 3.817.0 + '@aws-sdk/core': 3.816.0 + '@aws-sdk/token-providers': 3.817.0 '@aws-sdk/types': 3.804.0 - '@smithy/property-provider': 4.0.2 - '@smithy/shared-ini-file-loader': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/property-provider': 4.0.3 + '@smithy/shared-ini-file-loader': 4.0.3 + '@smithy/types': 4.3.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-web-identity@3.808.0': + '@aws-sdk/credential-provider-web-identity@3.817.0': dependencies: - '@aws-sdk/core': 3.808.0 - '@aws-sdk/nested-clients': 3.808.0 + '@aws-sdk/core': 3.816.0 + '@aws-sdk/nested-clients': 3.817.0 '@aws-sdk/types': 3.804.0 - '@smithy/property-provider': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/property-provider': 4.0.3 + '@smithy/types': 4.3.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-providers@3.808.0': + '@aws-sdk/credential-providers@3.817.0': dependencies: - '@aws-sdk/client-cognito-identity': 3.808.0 - '@aws-sdk/core': 3.808.0 - '@aws-sdk/credential-provider-cognito-identity': 3.808.0 - '@aws-sdk/credential-provider-env': 3.808.0 - '@aws-sdk/credential-provider-http': 3.808.0 - '@aws-sdk/credential-provider-ini': 3.808.0 - '@aws-sdk/credential-provider-node': 3.808.0 - '@aws-sdk/credential-provider-process': 3.808.0 - '@aws-sdk/credential-provider-sso': 3.808.0 - '@aws-sdk/credential-provider-web-identity': 3.808.0 - '@aws-sdk/nested-clients': 3.808.0 + '@aws-sdk/client-cognito-identity': 3.817.0 + '@aws-sdk/core': 3.816.0 + '@aws-sdk/credential-provider-cognito-identity': 3.817.0 + '@aws-sdk/credential-provider-env': 3.816.0 + '@aws-sdk/credential-provider-http': 3.816.0 + '@aws-sdk/credential-provider-ini': 3.817.0 + '@aws-sdk/credential-provider-node': 3.817.0 + '@aws-sdk/credential-provider-process': 3.816.0 + '@aws-sdk/credential-provider-sso': 3.817.0 + '@aws-sdk/credential-provider-web-identity': 3.817.0 + '@aws-sdk/nested-clients': 3.817.0 '@aws-sdk/types': 3.804.0 - '@smithy/config-resolver': 4.1.2 - '@smithy/core': 3.3.3 - '@smithy/credential-provider-imds': 4.0.4 - '@smithy/node-config-provider': 4.1.1 - '@smithy/property-provider': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/config-resolver': 4.1.3 + '@smithy/core': 3.4.0 + '@smithy/credential-provider-imds': 4.0.5 + '@smithy/node-config-provider': 4.1.2 + '@smithy/property-provider': 4.0.3 + '@smithy/types': 4.3.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt @@ -9027,85 +9055,85 @@ snapshots: '@aws-sdk/eventstream-handler-node@3.804.0': dependencies: '@aws-sdk/types': 3.804.0 - '@smithy/eventstream-codec': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/eventstream-codec': 4.0.3 + '@smithy/types': 4.3.0 tslib: 2.8.1 '@aws-sdk/middleware-eventstream@3.804.0': dependencies: '@aws-sdk/types': 3.804.0 - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 + '@smithy/protocol-http': 5.1.1 + '@smithy/types': 4.3.0 tslib: 2.8.1 '@aws-sdk/middleware-host-header@3.804.0': dependencies: '@aws-sdk/types': 3.804.0 - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 + '@smithy/protocol-http': 5.1.1 + '@smithy/types': 4.3.0 tslib: 2.8.1 '@aws-sdk/middleware-logger@3.804.0': dependencies: '@aws-sdk/types': 3.804.0 - '@smithy/types': 4.2.0 + '@smithy/types': 4.3.0 tslib: 2.8.1 '@aws-sdk/middleware-recursion-detection@3.804.0': dependencies: '@aws-sdk/types': 3.804.0 - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 + '@smithy/protocol-http': 5.1.1 + '@smithy/types': 4.3.0 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.808.0': + '@aws-sdk/middleware-user-agent@3.816.0': dependencies: - '@aws-sdk/core': 3.808.0 + '@aws-sdk/core': 3.816.0 '@aws-sdk/types': 3.804.0 '@aws-sdk/util-endpoints': 3.808.0 - '@smithy/core': 3.3.3 - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 + '@smithy/core': 3.4.0 + '@smithy/protocol-http': 5.1.1 + '@smithy/types': 4.3.0 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.808.0': + '@aws-sdk/nested-clients@3.817.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.808.0 + '@aws-sdk/core': 3.816.0 '@aws-sdk/middleware-host-header': 3.804.0 '@aws-sdk/middleware-logger': 3.804.0 '@aws-sdk/middleware-recursion-detection': 3.804.0 - '@aws-sdk/middleware-user-agent': 3.808.0 + '@aws-sdk/middleware-user-agent': 3.816.0 '@aws-sdk/region-config-resolver': 3.808.0 '@aws-sdk/types': 3.804.0 '@aws-sdk/util-endpoints': 3.808.0 '@aws-sdk/util-user-agent-browser': 3.804.0 - '@aws-sdk/util-user-agent-node': 3.808.0 - '@smithy/config-resolver': 4.1.2 - '@smithy/core': 3.3.3 - '@smithy/fetch-http-handler': 5.0.2 - '@smithy/hash-node': 4.0.2 - '@smithy/invalid-dependency': 4.0.2 - '@smithy/middleware-content-length': 4.0.2 - '@smithy/middleware-endpoint': 4.1.6 - '@smithy/middleware-retry': 4.1.7 - '@smithy/middleware-serde': 4.0.5 - '@smithy/middleware-stack': 4.0.2 - '@smithy/node-config-provider': 4.1.1 - '@smithy/node-http-handler': 4.0.4 - '@smithy/protocol-http': 5.1.0 - '@smithy/smithy-client': 4.2.6 - '@smithy/types': 4.2.0 - '@smithy/url-parser': 4.0.2 + '@aws-sdk/util-user-agent-node': 3.816.0 + '@smithy/config-resolver': 4.1.3 + '@smithy/core': 3.4.0 + '@smithy/fetch-http-handler': 5.0.3 + '@smithy/hash-node': 4.0.3 + '@smithy/invalid-dependency': 4.0.3 + '@smithy/middleware-content-length': 4.0.3 + '@smithy/middleware-endpoint': 4.1.7 + '@smithy/middleware-retry': 4.1.8 + '@smithy/middleware-serde': 4.0.6 + '@smithy/middleware-stack': 4.0.3 + '@smithy/node-config-provider': 4.1.2 + '@smithy/node-http-handler': 4.0.5 + '@smithy/protocol-http': 5.1.1 + '@smithy/smithy-client': 4.3.0 + '@smithy/types': 4.3.0 + '@smithy/url-parser': 4.0.3 '@smithy/util-base64': 4.0.0 '@smithy/util-body-length-browser': 4.0.0 '@smithy/util-body-length-node': 4.0.0 - '@smithy/util-defaults-mode-browser': 4.0.14 - '@smithy/util-defaults-mode-node': 4.0.14 - '@smithy/util-endpoints': 3.0.4 - '@smithy/util-middleware': 4.0.2 - '@smithy/util-retry': 4.0.3 + '@smithy/util-defaults-mode-browser': 4.0.15 + '@smithy/util-defaults-mode-node': 4.0.15 + '@smithy/util-endpoints': 3.0.5 + '@smithy/util-middleware': 4.0.3 + '@smithy/util-retry': 4.0.4 '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 transitivePeerDependencies: @@ -9114,33 +9142,34 @@ snapshots: '@aws-sdk/region-config-resolver@3.808.0': dependencies: '@aws-sdk/types': 3.804.0 - '@smithy/node-config-provider': 4.1.1 - '@smithy/types': 4.2.0 + '@smithy/node-config-provider': 4.1.2 + '@smithy/types': 4.3.0 '@smithy/util-config-provider': 4.0.0 - '@smithy/util-middleware': 4.0.2 + '@smithy/util-middleware': 4.0.3 tslib: 2.8.1 - '@aws-sdk/token-providers@3.808.0': + '@aws-sdk/token-providers@3.817.0': dependencies: - '@aws-sdk/nested-clients': 3.808.0 + '@aws-sdk/core': 3.816.0 + '@aws-sdk/nested-clients': 3.817.0 '@aws-sdk/types': 3.804.0 - '@smithy/property-provider': 4.0.2 - '@smithy/shared-ini-file-loader': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/property-provider': 4.0.3 + '@smithy/shared-ini-file-loader': 4.0.3 + '@smithy/types': 4.3.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt '@aws-sdk/types@3.804.0': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.3.0 tslib: 2.8.1 '@aws-sdk/util-endpoints@3.808.0': dependencies: '@aws-sdk/types': 3.804.0 - '@smithy/types': 4.2.0 - '@smithy/util-endpoints': 3.0.4 + '@smithy/types': 4.3.0 + '@smithy/util-endpoints': 3.0.5 tslib: 2.8.1 '@aws-sdk/util-locate-window@3.804.0': @@ -9150,16 +9179,16 @@ snapshots: '@aws-sdk/util-user-agent-browser@3.804.0': dependencies: '@aws-sdk/types': 3.804.0 - '@smithy/types': 4.2.0 + '@smithy/types': 4.3.0 bowser: 2.11.0 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.808.0': + '@aws-sdk/util-user-agent-node@3.816.0': dependencies: - '@aws-sdk/middleware-user-agent': 3.808.0 + '@aws-sdk/middleware-user-agent': 3.816.0 '@aws-sdk/types': 3.804.0 - '@smithy/node-config-provider': 4.1.1 - '@smithy/types': 4.2.0 + '@smithy/node-config-provider': 4.1.2 + '@smithy/types': 4.3.0 tslib: 2.8.1 '@aws-sdk/util-utf8-browser@3.259.0': @@ -10079,15 +10108,16 @@ snapshots: dependencies: exenv-es6: 1.1.1 - '@mistralai/mistralai@1.6.0(zod@3.24.4)': + '@mistralai/mistralai@1.6.1(zod@3.24.4)': dependencies: zod: 3.24.4 zod-to-json-schema: 3.24.5(zod@3.24.4) '@mixmark-io/domino@2.2.0': {} - '@modelcontextprotocol/sdk@1.11.2': + '@modelcontextprotocol/sdk@1.12.0': dependencies: + ajv: 6.12.6 content-type: 1.0.5 cors: 2.8.5 cross-spawn: 7.0.6 @@ -10146,14 +10176,14 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@puppeteer/browsers@2.10.4': + '@puppeteer/browsers@2.10.5': dependencies: debug: 4.4.1(supports-color@8.1.1) extract-zip: 2.0.1 progress: 2.0.3 proxy-agent: 6.5.0 semver: 7.7.2 - tar-fs: 3.0.8 + tar-fs: 3.0.9 yargs: 17.7.2 transitivePeerDependencies: - bare-buffer @@ -10166,7 +10196,7 @@ snapshots: progress: 2.0.3 proxy-agent: 6.5.0 semver: 7.7.2 - tar-fs: 3.0.8 + tar-fs: 3.0.9 unbzip2-stream: 1.4.3 yargs: 17.7.2 transitivePeerDependencies: @@ -10689,10 +10719,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.40.2': optional: true - '@roo-code/types@1.12.0': - dependencies: - zod: 3.24.4 - '@sec-ant/readable-stream@0.4.1': {} '@sevinf/maybe@0.5.0': {} @@ -10747,36 +10773,36 @@ snapshots: '@smithy/types': 2.12.0 tslib: 2.8.1 - '@smithy/abort-controller@4.0.2': + '@smithy/abort-controller@4.0.3': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.3.0 tslib: 2.8.1 - '@smithy/config-resolver@4.1.2': + '@smithy/config-resolver@4.1.3': dependencies: - '@smithy/node-config-provider': 4.1.1 - '@smithy/types': 4.2.0 + '@smithy/node-config-provider': 4.1.2 + '@smithy/types': 4.3.0 '@smithy/util-config-provider': 4.0.0 - '@smithy/util-middleware': 4.0.2 + '@smithy/util-middleware': 4.0.3 tslib: 2.8.1 - '@smithy/core@3.3.3': + '@smithy/core@3.4.0': dependencies: - '@smithy/middleware-serde': 4.0.5 - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 + '@smithy/middleware-serde': 4.0.6 + '@smithy/protocol-http': 5.1.1 + '@smithy/types': 4.3.0 '@smithy/util-body-length-browser': 4.0.0 - '@smithy/util-middleware': 4.0.2 - '@smithy/util-stream': 4.2.0 + '@smithy/util-middleware': 4.0.3 + '@smithy/util-stream': 4.2.1 '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 - '@smithy/credential-provider-imds@4.0.4': + '@smithy/credential-provider-imds@4.0.5': dependencies: - '@smithy/node-config-provider': 4.1.1 - '@smithy/property-provider': 4.0.2 - '@smithy/types': 4.2.0 - '@smithy/url-parser': 4.0.2 + '@smithy/node-config-provider': 4.1.2 + '@smithy/property-provider': 4.0.3 + '@smithy/types': 4.3.0 + '@smithy/url-parser': 4.0.3 tslib: 2.8.1 '@smithy/eventstream-codec@2.2.0': @@ -10786,22 +10812,22 @@ snapshots: '@smithy/util-hex-encoding': 2.2.0 tslib: 2.8.1 - '@smithy/eventstream-codec@4.0.2': + '@smithy/eventstream-codec@4.0.3': dependencies: '@aws-crypto/crc32': 5.2.0 - '@smithy/types': 4.2.0 + '@smithy/types': 4.3.0 '@smithy/util-hex-encoding': 4.0.0 tslib: 2.8.1 - '@smithy/eventstream-serde-browser@4.0.2': + '@smithy/eventstream-serde-browser@4.0.3': dependencies: - '@smithy/eventstream-serde-universal': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/eventstream-serde-universal': 4.0.3 + '@smithy/types': 4.3.0 tslib: 2.8.1 - '@smithy/eventstream-serde-config-resolver@4.1.0': + '@smithy/eventstream-serde-config-resolver@4.1.1': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.3.0 tslib: 2.8.1 '@smithy/eventstream-serde-node@2.2.0': @@ -10810,10 +10836,10 @@ snapshots: '@smithy/types': 2.12.0 tslib: 2.8.1 - '@smithy/eventstream-serde-node@4.0.2': + '@smithy/eventstream-serde-node@4.0.3': dependencies: - '@smithy/eventstream-serde-universal': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/eventstream-serde-universal': 4.0.3 + '@smithy/types': 4.3.0 tslib: 2.8.1 '@smithy/eventstream-serde-universal@2.2.0': @@ -10822,10 +10848,10 @@ snapshots: '@smithy/types': 2.12.0 tslib: 2.8.1 - '@smithy/eventstream-serde-universal@4.0.2': + '@smithy/eventstream-serde-universal@4.0.3': dependencies: - '@smithy/eventstream-codec': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/eventstream-codec': 4.0.3 + '@smithy/types': 4.3.0 tslib: 2.8.1 '@smithy/fetch-http-handler@2.5.0': @@ -10836,24 +10862,24 @@ snapshots: '@smithy/util-base64': 2.3.0 tslib: 2.8.1 - '@smithy/fetch-http-handler@5.0.2': + '@smithy/fetch-http-handler@5.0.3': dependencies: - '@smithy/protocol-http': 5.1.0 - '@smithy/querystring-builder': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/protocol-http': 5.1.1 + '@smithy/querystring-builder': 4.0.3 + '@smithy/types': 4.3.0 '@smithy/util-base64': 4.0.0 tslib: 2.8.1 - '@smithy/hash-node@4.0.2': + '@smithy/hash-node@4.0.3': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.3.0 '@smithy/util-buffer-from': 4.0.0 '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 - '@smithy/invalid-dependency@4.0.2': + '@smithy/invalid-dependency@4.0.3': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.3.0 tslib: 2.8.1 '@smithy/is-array-buffer@2.2.0': @@ -10868,10 +10894,10 @@ snapshots: dependencies: tslib: 2.8.1 - '@smithy/middleware-content-length@4.0.2': + '@smithy/middleware-content-length@4.0.3': dependencies: - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 + '@smithy/protocol-http': 5.1.1 + '@smithy/types': 4.3.0 tslib: 2.8.1 '@smithy/middleware-endpoint@2.5.1': @@ -10884,26 +10910,26 @@ snapshots: '@smithy/util-middleware': 2.2.0 tslib: 2.8.1 - '@smithy/middleware-endpoint@4.1.6': + '@smithy/middleware-endpoint@4.1.7': dependencies: - '@smithy/core': 3.3.3 - '@smithy/middleware-serde': 4.0.5 - '@smithy/node-config-provider': 4.1.1 - '@smithy/shared-ini-file-loader': 4.0.2 - '@smithy/types': 4.2.0 - '@smithy/url-parser': 4.0.2 - '@smithy/util-middleware': 4.0.2 + '@smithy/core': 3.4.0 + '@smithy/middleware-serde': 4.0.6 + '@smithy/node-config-provider': 4.1.2 + '@smithy/shared-ini-file-loader': 4.0.3 + '@smithy/types': 4.3.0 + '@smithy/url-parser': 4.0.3 + '@smithy/util-middleware': 4.0.3 tslib: 2.8.1 - '@smithy/middleware-retry@4.1.7': + '@smithy/middleware-retry@4.1.8': dependencies: - '@smithy/node-config-provider': 4.1.1 - '@smithy/protocol-http': 5.1.0 - '@smithy/service-error-classification': 4.0.3 - '@smithy/smithy-client': 4.2.6 - '@smithy/types': 4.2.0 - '@smithy/util-middleware': 4.0.2 - '@smithy/util-retry': 4.0.3 + '@smithy/node-config-provider': 4.1.2 + '@smithy/protocol-http': 5.1.1 + '@smithy/service-error-classification': 4.0.4 + '@smithy/smithy-client': 4.3.0 + '@smithy/types': 4.3.0 + '@smithy/util-middleware': 4.0.3 + '@smithy/util-retry': 4.0.4 tslib: 2.8.1 uuid: 9.0.1 @@ -10912,10 +10938,10 @@ snapshots: '@smithy/types': 2.12.0 tslib: 2.8.1 - '@smithy/middleware-serde@4.0.5': + '@smithy/middleware-serde@4.0.6': dependencies: - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 + '@smithy/protocol-http': 5.1.1 + '@smithy/types': 4.3.0 tslib: 2.8.1 '@smithy/middleware-stack@2.2.0': @@ -10923,9 +10949,9 @@ snapshots: '@smithy/types': 2.12.0 tslib: 2.8.1 - '@smithy/middleware-stack@4.0.2': + '@smithy/middleware-stack@4.0.3': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.3.0 tslib: 2.8.1 '@smithy/node-config-provider@2.3.0': @@ -10935,11 +10961,11 @@ snapshots: '@smithy/types': 2.12.0 tslib: 2.8.1 - '@smithy/node-config-provider@4.1.1': + '@smithy/node-config-provider@4.1.2': dependencies: - '@smithy/property-provider': 4.0.2 - '@smithy/shared-ini-file-loader': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/property-provider': 4.0.3 + '@smithy/shared-ini-file-loader': 4.0.3 + '@smithy/types': 4.3.0 tslib: 2.8.1 '@smithy/node-http-handler@2.5.0': @@ -10950,12 +10976,12 @@ snapshots: '@smithy/types': 2.12.0 tslib: 2.8.1 - '@smithy/node-http-handler@4.0.4': + '@smithy/node-http-handler@4.0.5': dependencies: - '@smithy/abort-controller': 4.0.2 - '@smithy/protocol-http': 5.1.0 - '@smithy/querystring-builder': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/abort-controller': 4.0.3 + '@smithy/protocol-http': 5.1.1 + '@smithy/querystring-builder': 4.0.3 + '@smithy/types': 4.3.0 tslib: 2.8.1 '@smithy/property-provider@2.2.0': @@ -10963,9 +10989,9 @@ snapshots: '@smithy/types': 2.12.0 tslib: 2.8.1 - '@smithy/property-provider@4.0.2': + '@smithy/property-provider@4.0.3': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.3.0 tslib: 2.8.1 '@smithy/protocol-http@3.3.0': @@ -10973,9 +10999,9 @@ snapshots: '@smithy/types': 2.12.0 tslib: 2.8.1 - '@smithy/protocol-http@5.1.0': + '@smithy/protocol-http@5.1.1': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.3.0 tslib: 2.8.1 '@smithy/querystring-builder@2.2.0': @@ -10984,9 +11010,9 @@ snapshots: '@smithy/util-uri-escape': 2.2.0 tslib: 2.8.1 - '@smithy/querystring-builder@4.0.2': + '@smithy/querystring-builder@4.0.3': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.3.0 '@smithy/util-uri-escape': 4.0.0 tslib: 2.8.1 @@ -10995,23 +11021,23 @@ snapshots: '@smithy/types': 2.12.0 tslib: 2.8.1 - '@smithy/querystring-parser@4.0.2': + '@smithy/querystring-parser@4.0.3': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.3.0 tslib: 2.8.1 - '@smithy/service-error-classification@4.0.3': + '@smithy/service-error-classification@4.0.4': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.3.0 '@smithy/shared-ini-file-loader@2.4.0': dependencies: '@smithy/types': 2.12.0 tslib: 2.8.1 - '@smithy/shared-ini-file-loader@4.0.2': + '@smithy/shared-ini-file-loader@4.0.3': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.3.0 tslib: 2.8.1 '@smithy/signature-v4@3.1.2': @@ -11024,13 +11050,13 @@ snapshots: '@smithy/util-utf8': 3.0.0 tslib: 2.8.1 - '@smithy/signature-v4@5.1.0': + '@smithy/signature-v4@5.1.1': dependencies: '@smithy/is-array-buffer': 4.0.0 - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 + '@smithy/protocol-http': 5.1.1 + '@smithy/types': 4.3.0 '@smithy/util-hex-encoding': 4.0.0 - '@smithy/util-middleware': 4.0.2 + '@smithy/util-middleware': 4.0.3 '@smithy/util-uri-escape': 4.0.0 '@smithy/util-utf8': 4.0.0 tslib: 2.8.1 @@ -11044,14 +11070,14 @@ snapshots: '@smithy/util-stream': 2.2.0 tslib: 2.8.1 - '@smithy/smithy-client@4.2.6': + '@smithy/smithy-client@4.3.0': dependencies: - '@smithy/core': 3.3.3 - '@smithy/middleware-endpoint': 4.1.6 - '@smithy/middleware-stack': 4.0.2 - '@smithy/protocol-http': 5.1.0 - '@smithy/types': 4.2.0 - '@smithy/util-stream': 4.2.0 + '@smithy/core': 3.4.0 + '@smithy/middleware-endpoint': 4.1.7 + '@smithy/middleware-stack': 4.0.3 + '@smithy/protocol-http': 5.1.1 + '@smithy/types': 4.3.0 + '@smithy/util-stream': 4.2.1 tslib: 2.8.1 '@smithy/types@2.12.0': @@ -11062,7 +11088,7 @@ snapshots: dependencies: tslib: 2.8.1 - '@smithy/types@4.2.0': + '@smithy/types@4.3.0': dependencies: tslib: 2.8.1 @@ -11072,10 +11098,10 @@ snapshots: '@smithy/types': 2.12.0 tslib: 2.8.1 - '@smithy/url-parser@4.0.2': + '@smithy/url-parser@4.0.3': dependencies: - '@smithy/querystring-parser': 4.0.2 - '@smithy/types': 4.2.0 + '@smithy/querystring-parser': 4.0.3 + '@smithy/types': 4.3.0 tslib: 2.8.1 '@smithy/util-base64@2.3.0': @@ -11117,28 +11143,28 @@ snapshots: dependencies: tslib: 2.8.1 - '@smithy/util-defaults-mode-browser@4.0.14': + '@smithy/util-defaults-mode-browser@4.0.15': dependencies: - '@smithy/property-provider': 4.0.2 - '@smithy/smithy-client': 4.2.6 - '@smithy/types': 4.2.0 + '@smithy/property-provider': 4.0.3 + '@smithy/smithy-client': 4.3.0 + '@smithy/types': 4.3.0 bowser: 2.11.0 tslib: 2.8.1 - '@smithy/util-defaults-mode-node@4.0.14': + '@smithy/util-defaults-mode-node@4.0.15': dependencies: - '@smithy/config-resolver': 4.1.2 - '@smithy/credential-provider-imds': 4.0.4 - '@smithy/node-config-provider': 4.1.1 - '@smithy/property-provider': 4.0.2 - '@smithy/smithy-client': 4.2.6 - '@smithy/types': 4.2.0 + '@smithy/config-resolver': 4.1.3 + '@smithy/credential-provider-imds': 4.0.5 + '@smithy/node-config-provider': 4.1.2 + '@smithy/property-provider': 4.0.3 + '@smithy/smithy-client': 4.3.0 + '@smithy/types': 4.3.0 tslib: 2.8.1 - '@smithy/util-endpoints@3.0.4': + '@smithy/util-endpoints@3.0.5': dependencies: - '@smithy/node-config-provider': 4.1.1 - '@smithy/types': 4.2.0 + '@smithy/node-config-provider': 4.1.2 + '@smithy/types': 4.3.0 tslib: 2.8.1 '@smithy/util-hex-encoding@2.2.0': @@ -11163,15 +11189,15 @@ snapshots: '@smithy/types': 3.7.2 tslib: 2.8.1 - '@smithy/util-middleware@4.0.2': + '@smithy/util-middleware@4.0.3': dependencies: - '@smithy/types': 4.2.0 + '@smithy/types': 4.3.0 tslib: 2.8.1 - '@smithy/util-retry@4.0.3': + '@smithy/util-retry@4.0.4': dependencies: - '@smithy/service-error-classification': 4.0.3 - '@smithy/types': 4.2.0 + '@smithy/service-error-classification': 4.0.4 + '@smithy/types': 4.3.0 tslib: 2.8.1 '@smithy/util-stream@2.2.0': @@ -11185,11 +11211,11 @@ snapshots: '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@smithy/util-stream@4.2.0': + '@smithy/util-stream@4.2.1': dependencies: - '@smithy/fetch-http-handler': 5.0.2 - '@smithy/node-http-handler': 4.0.4 - '@smithy/types': 4.2.0 + '@smithy/fetch-http-handler': 5.0.3 + '@smithy/node-http-handler': 4.0.5 + '@smithy/types': 4.3.0 '@smithy/util-base64': 4.0.0 '@smithy/util-buffer-from': 4.0.0 '@smithy/util-hex-encoding': 4.0.0 @@ -11772,7 +11798,7 @@ snapshots: dependencies: undici-types: 5.26.5 - '@types/node@20.17.47': + '@types/node@20.17.50': dependencies: undici-types: 6.19.8 @@ -11945,13 +11971,13 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.1.3(vite@6.3.5(@types/node@20.17.47)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0))': + '@vitest/mocker@3.1.3(vite@6.3.5(@types/node@20.17.50)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0))': dependencies: '@vitest/spy': 3.1.3 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 6.3.5(@types/node@20.17.47)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0) + vite: 6.3.5(@types/node@20.17.50)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0) '@vitest/mocker@3.1.3(vite@6.3.5(@types/node@22.15.20)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0))': dependencies: @@ -12805,13 +12831,13 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@20.17.47)(babel-plugin-macros@3.1.0): + create-jest@29.7.0(@types/node@20.17.50)(babel-plugin-macros@3.1.0): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.17.47)(babel-plugin-macros@3.1.0) + jest-config: 29.7.0(@types/node@20.17.50)(babel-plugin-macros@3.1.0) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -13609,11 +13635,11 @@ snapshots: eventemitter3@5.0.1: {} - eventsource-parser@3.0.1: {} + eventsource-parser@3.0.2: {} eventsource@3.0.7: dependencies: - eventsource-parser: 3.0.1 + eventsource-parser: 3.0.2 execa@5.1.1: dependencies: @@ -13826,6 +13852,12 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 + fix-dts-default-cjs-exports@1.0.1: + dependencies: + magic-string: 0.30.17 + mlly: 1.7.4 + rollup: 4.40.2 + flat-cache@4.0.1: dependencies: flatted: 3.3.3 @@ -14683,16 +14715,16 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@20.17.47)(babel-plugin-macros@3.1.0): + jest-cli@29.7.0(@types/node@20.17.50)(babel-plugin-macros@3.1.0): dependencies: '@jest/core': 29.7.0(babel-plugin-macros@3.1.0) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.17.47)(babel-plugin-macros@3.1.0) + create-jest: 29.7.0(@types/node@20.17.50)(babel-plugin-macros@3.1.0) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@20.17.47)(babel-plugin-macros@3.1.0) + jest-config: 29.7.0(@types/node@20.17.50)(babel-plugin-macros@3.1.0) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -14732,7 +14764,7 @@ snapshots: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.17.47)(babel-plugin-macros@3.1.0): + jest-config@29.7.0(@types/node@20.17.50)(babel-plugin-macros@3.1.0): dependencies: '@babel/core': 7.27.1 '@jest/test-sequencer': 29.7.0 @@ -14757,7 +14789,7 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 20.17.47 + '@types/node': 20.17.50 transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -15052,12 +15084,12 @@ snapshots: - supports-color - ts-node - jest@29.7.0(@types/node@20.17.47)(babel-plugin-macros@3.1.0): + jest@29.7.0(@types/node@20.17.50)(babel-plugin-macros@3.1.0): dependencies: '@jest/core': 29.7.0(babel-plugin-macros@3.1.0) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@20.17.47)(babel-plugin-macros@3.1.0) + jest-cli: 29.7.0(@types/node@20.17.50)(babel-plugin-macros@3.1.0) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -16124,7 +16156,7 @@ snapshots: npm-normalize-package-bin@4.0.0: {} - npm-run-all2@8.0.1: + npm-run-all2@8.0.3: dependencies: ansi-styles: 6.2.1 cross-spawn: 7.0.6 @@ -16239,7 +16271,7 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 - openai@4.98.0(ws@8.18.2)(zod@3.24.4): + openai@4.103.0(ws@8.18.2)(zod@3.24.4): dependencies: '@types/node': 18.19.100 '@types/node-fetch': 2.6.12 @@ -16277,10 +16309,10 @@ snapshots: string-width: 7.2.0 strip-ansi: 7.1.0 - os-name@6.0.0: + os-name@6.1.0: dependencies: macos-release: 3.3.0 - windows-release: 6.0.1 + windows-release: 6.1.0 os-tmpdir@1.0.2: {} @@ -16539,7 +16571,7 @@ snapshots: preact: 10.26.6 web-vitals: 4.2.4 - posthog-node@4.17.1: + posthog-node@4.17.2: dependencies: axios: 1.9.0 transitivePeerDependencies: @@ -16651,7 +16683,7 @@ snapshots: puppeteer-chromium-resolver@23.0.0: dependencies: - '@puppeteer/browsers': 2.10.4 + '@puppeteer/browsers': 2.10.5 eight-colors: 1.3.1 gauge: 5.0.2 puppeteer-core: 23.11.1 @@ -17630,7 +17662,7 @@ snapshots: tar-stream: 2.2.0 optional: true - tar-fs@3.0.8: + tar-fs@3.0.9: dependencies: pump: 3.0.2 tar-stream: 3.1.7 @@ -17792,12 +17824,12 @@ snapshots: babel-jest: 29.7.0(@babel/core@7.27.1) esbuild: 0.25.4 - ts-jest@29.3.3(@babel/core@7.27.1)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.1))(esbuild@0.25.4)(jest@29.7.0(@types/node@20.17.47)(babel-plugin-macros@3.1.0))(typescript@5.8.3): + ts-jest@29.3.3(@babel/core@7.27.1)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.27.1))(esbuild@0.25.4)(jest@29.7.0(@types/node@20.17.50)(babel-plugin-macros@3.1.0))(typescript@5.8.3): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@20.17.47)(babel-plugin-macros@3.1.0) + jest: 29.7.0(@types/node@20.17.50)(babel-plugin-macros@3.1.0) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -17825,7 +17857,7 @@ snapshots: tslib@2.8.1: {} - tsup@8.4.0(jiti@2.4.2)(postcss@8.5.3)(tsx@4.19.4)(typescript@5.8.3)(yaml@2.8.0): + tsup@8.5.0(jiti@2.4.2)(postcss@8.5.3)(tsx@4.19.4)(typescript@5.8.3)(yaml@2.8.0): dependencies: bundle-require: 5.1.0(esbuild@0.25.4) cac: 6.7.14 @@ -17833,6 +17865,7 @@ snapshots: consola: 3.4.2 debug: 4.4.1(supports-color@8.1.1) esbuild: 0.25.4 + fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 picocolors: 1.1.1 postcss-load-config: 6.0.1(jiti@2.4.2)(postcss@8.5.3)(tsx@4.19.4)(yaml@2.8.0) @@ -18198,13 +18231,13 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 - vite-node@3.1.3(@types/node@20.17.47)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0): + vite-node@3.1.3(@types/node@20.17.50)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0): dependencies: cac: 6.7.14 debug: 4.4.1(supports-color@8.1.1) es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 6.3.5(@types/node@20.17.47)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0) + vite: 6.3.5(@types/node@20.17.50)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0) transitivePeerDependencies: - '@types/node' - jiti @@ -18256,7 +18289,7 @@ snapshots: tsx: 4.19.4 yaml: 2.8.0 - vite@6.3.5(@types/node@20.17.47)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0): + vite@6.3.5(@types/node@20.17.50)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0): dependencies: esbuild: 0.25.4 fdir: 6.4.4(picomatch@4.0.2) @@ -18265,7 +18298,7 @@ snapshots: rollup: 4.40.2 tinyglobby: 0.2.13 optionalDependencies: - '@types/node': 20.17.47 + '@types/node': 20.17.50 fsevents: 2.3.3 jiti: 2.4.2 lightningcss: 1.29.2 @@ -18288,10 +18321,10 @@ snapshots: tsx: 4.19.4 yaml: 2.8.0 - vitest@3.1.3(@types/debug@4.1.12)(@types/node@20.17.47)(jiti@2.4.2)(jsdom@20.0.3)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0): + vitest@3.1.3(@types/debug@4.1.12)(@types/node@20.17.50)(jiti@2.4.2)(jsdom@20.0.3)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0): dependencies: '@vitest/expect': 3.1.3 - '@vitest/mocker': 3.1.3(vite@6.3.5(@types/node@20.17.47)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0)) + '@vitest/mocker': 3.1.3(vite@6.3.5(@types/node@20.17.50)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0)) '@vitest/pretty-format': 3.1.3 '@vitest/runner': 3.1.3 '@vitest/snapshot': 3.1.3 @@ -18308,12 +18341,12 @@ snapshots: tinyglobby: 0.2.13 tinypool: 1.0.2 tinyrainbow: 2.0.0 - vite: 6.3.5(@types/node@20.17.47)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0) - vite-node: 3.1.3(@types/node@20.17.47)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0) + vite: 6.3.5(@types/node@20.17.50)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0) + vite-node: 3.1.3(@types/node@20.17.50)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 - '@types/node': 20.17.47 + '@types/node': 20.17.50 jsdom: 20.0.3 transitivePeerDependencies: - jiti @@ -18514,7 +18547,7 @@ snapshots: dependencies: string-width: 4.2.3 - windows-release@6.0.1: + windows-release@6.1.0: dependencies: execa: 8.0.1 diff --git a/src/activate/CodeActionProvider.ts b/src/activate/CodeActionProvider.ts index 37b1a82712..2646552452 100644 --- a/src/activate/CodeActionProvider.ts +++ b/src/activate/CodeActionProvider.ts @@ -1,6 +1,7 @@ import * as vscode from "vscode" -import { CodeActionName, CodeActionId } from "../schemas" +import { CodeActionName, CodeActionId } from "@roo-code/types" + import { getCodeActionCommand } from "../utils/commands" import { EditorUtils } from "../integrations/editor/EditorUtils" diff --git a/src/activate/handleTask.ts b/src/activate/handleTask.ts index 208b7bf427..bc2aed4beb 100644 --- a/src/activate/handleTask.ts +++ b/src/activate/handleTask.ts @@ -1,6 +1,6 @@ import * as vscode from "vscode" -import { Package } from "../schemas" +import { Package } from "../shared/package" import { ClineProvider } from "../core/webview/ClineProvider" import { t } from "../i18n" diff --git a/src/activate/registerCodeActions.ts b/src/activate/registerCodeActions.ts index ba8be1a471..6c0a65b9e0 100644 --- a/src/activate/registerCodeActions.ts +++ b/src/activate/registerCodeActions.ts @@ -1,6 +1,7 @@ import * as vscode from "vscode" -import { CodeActionId, CodeActionName } from "../schemas" +import { CodeActionId, CodeActionName } from "@roo-code/types" + import { getCodeActionCommand } from "../utils/commands" import { EditorUtils } from "../integrations/editor/EditorUtils" import { ClineProvider } from "../core/webview/ClineProvider" diff --git a/src/activate/registerCommands.ts b/src/activate/registerCommands.ts index fc18e96d54..cd76b11f96 100644 --- a/src/activate/registerCommands.ts +++ b/src/activate/registerCommands.ts @@ -1,7 +1,9 @@ import * as vscode from "vscode" import delay from "delay" -import { CommandId, Package } from "../schemas" +import type { CommandId } from "@roo-code/types" + +import { Package } from "../shared/package" import { getCommand } from "../utils/commands" import { ClineProvider } from "../core/webview/ClineProvider" import { ContextProxy } from "../core/config/ContextProxy" diff --git a/src/activate/registerTerminalActions.ts b/src/activate/registerTerminalActions.ts index f2dc8b4709..eb494d66da 100644 --- a/src/activate/registerTerminalActions.ts +++ b/src/activate/registerTerminalActions.ts @@ -1,6 +1,7 @@ import * as vscode from "vscode" -import { TerminalActionId, TerminalActionPromptType } from "../schemas" +import { TerminalActionId, TerminalActionPromptType } from "@roo-code/types" + import { getTerminalCommand } from "../utils/commands" import { ClineProvider } from "../core/webview/ClineProvider" import { Terminal } from "../integrations/terminal/Terminal" diff --git a/src/api/index.ts b/src/api/index.ts index f831e58e8d..8b09bf4cf9 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -1,29 +1,33 @@ import { Anthropic } from "@anthropic-ai/sdk" -import { ProviderSettings, ModelInfo } from "../shared/api" -import { GlamaHandler } from "./providers/glama" -import { AnthropicHandler } from "./providers/anthropic" -import { AwsBedrockHandler } from "./providers/bedrock" -import { OpenRouterHandler } from "./providers/openrouter" -import { VertexHandler } from "./providers/vertex" -import { AnthropicVertexHandler } from "./providers/anthropic-vertex" -import { OpenAiHandler } from "./providers/openai" -import { OllamaHandler } from "./providers/ollama" -import { LmStudioHandler } from "./providers/lmstudio" -import { GeminiHandler } from "./providers/gemini" -import { OpenAiNativeHandler } from "./providers/openai-native" -import { DeepSeekHandler } from "./providers/deepseek" -import { MistralHandler } from "./providers/mistral" -import { VsCodeLmHandler } from "./providers/vscode-lm" +import type { ProviderSettings, ModelInfo } from "@roo-code/types" + import { ApiStream } from "./transform/stream" -import { UnboundHandler } from "./providers/unbound" -import { RequestyHandler } from "./providers/requesty" -import { HumanRelayHandler } from "./providers/human-relay" -import { FakeAIHandler } from "./providers/fake-ai" -import { XAIHandler } from "./providers/xai" -import { GroqHandler } from "./providers/groq" -import { ChutesHandler } from "./providers/chutes" -import { LiteLLMHandler } from "./providers/litellm" + +import { + GlamaHandler, + AnthropicHandler, + AwsBedrockHandler, + OpenRouterHandler, + VertexHandler, + AnthropicVertexHandler, + OpenAiHandler, + OllamaHandler, + LmStudioHandler, + GeminiHandler, + OpenAiNativeHandler, + DeepSeekHandler, + MistralHandler, + VsCodeLmHandler, + UnboundHandler, + RequestyHandler, + HumanRelayHandler, + FakeAIHandler, + XAIHandler, + GroqHandler, + ChutesHandler, + LiteLLMHandler, +} from "./providers" export interface SingleCompletionHandler { completePrompt(prompt: string): Promise @@ -67,11 +71,9 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler { case "bedrock": return new AwsBedrockHandler(options) case "vertex": - if (options.apiModelId?.startsWith("claude")) { - return new AnthropicVertexHandler(options) - } else { - return new VertexHandler(options) - } + return options.apiModelId?.startsWith("claude") + ? new AnthropicVertexHandler(options) + : new VertexHandler(options) case "openai": return new OpenAiHandler(options) case "ollama": diff --git a/src/api/providers/__tests__/gemini.test.ts b/src/api/providers/__tests__/gemini.test.ts index 97c757f8fb..3016e77364 100644 --- a/src/api/providers/__tests__/gemini.test.ts +++ b/src/api/providers/__tests__/gemini.test.ts @@ -2,8 +2,10 @@ import { Anthropic } from "@anthropic-ai/sdk" +import type { ModelInfo } from "@roo-code/types" + +import { geminiDefaultModelId } from "../../../shared/api" import { GeminiHandler } from "../gemini" -import { geminiDefaultModelId, type ModelInfo } from "../../../shared/api" const GEMINI_20_FLASH_THINKING_NAME = "gemini-2.0-flash-thinking-exp-1219" diff --git a/src/api/providers/anthropic-vertex.ts b/src/api/providers/anthropic-vertex.ts index a4ace61c6e..0ad262593b 100644 --- a/src/api/providers/anthropic-vertex.ts +++ b/src/api/providers/anthropic-vertex.ts @@ -2,7 +2,9 @@ import { Anthropic } from "@anthropic-ai/sdk" import { AnthropicVertex } from "@anthropic-ai/vertex-sdk" import { GoogleAuth, JWTInput } from "google-auth-library" -import { ApiHandlerOptions, ModelInfo, vertexDefaultModelId, VertexModelId, vertexModels } from "../../shared/api" +import type { ModelInfo } from "@roo-code/types" + +import { ApiHandlerOptions, vertexDefaultModelId, VertexModelId, vertexModels } from "../../shared/api" import { safeJsonParse } from "../../shared/safeJsonParse" import { ApiStream } from "../transform/stream" diff --git a/src/api/providers/anthropic.ts b/src/api/providers/anthropic.ts index 9c84f388ef..d0c4c7c9d3 100644 --- a/src/api/providers/anthropic.ts +++ b/src/api/providers/anthropic.ts @@ -2,13 +2,9 @@ import { Anthropic } from "@anthropic-ai/sdk" import { Stream as AnthropicStream } from "@anthropic-ai/sdk/streaming" import { CacheControlEphemeral } from "@anthropic-ai/sdk/resources" -import { - anthropicDefaultModelId, - AnthropicModelId, - anthropicModels, - ApiHandlerOptions, - ModelInfo, -} from "../../shared/api" +import type { ModelInfo } from "@roo-code/types" + +import { anthropicDefaultModelId, AnthropicModelId, anthropicModels, ApiHandlerOptions } from "../../shared/api" import { ApiStream } from "../transform/stream" import { getModelParams } from "../transform/model-params" diff --git a/src/api/providers/base-openai-compatible-provider.ts b/src/api/providers/base-openai-compatible-provider.ts index ba9d67b6e3..bf1f3c35a8 100644 --- a/src/api/providers/base-openai-compatible-provider.ts +++ b/src/api/providers/base-openai-compatible-provider.ts @@ -1,7 +1,9 @@ import { Anthropic } from "@anthropic-ai/sdk" import OpenAI from "openai" -import { ApiHandlerOptions, ModelInfo } from "../../shared/api" +import type { ModelInfo } from "@roo-code/types" + +import type { ApiHandlerOptions } from "../../shared/api" import { ApiStream } from "../transform/stream" import { convertToOpenAiMessages } from "../transform/openai-format" diff --git a/src/api/providers/base-provider.ts b/src/api/providers/base-provider.ts index edb15a3f85..6b77521ea4 100644 --- a/src/api/providers/base-provider.ts +++ b/src/api/providers/base-provider.ts @@ -1,6 +1,6 @@ import { Anthropic } from "@anthropic-ai/sdk" -import { ModelInfo } from "../../shared/api" +import type { ModelInfo } from "@roo-code/types" import type { ApiHandler, ApiHandlerCreateMessageMetadata } from "../index" import { ApiStream } from "../transform/stream" diff --git a/src/api/providers/bedrock.ts b/src/api/providers/bedrock.ts index c378441484..cc5de1b548 100644 --- a/src/api/providers/bedrock.ts +++ b/src/api/providers/bedrock.ts @@ -9,18 +9,18 @@ import { } from "@aws-sdk/client-bedrock-runtime" import { fromIni } from "@aws-sdk/credential-providers" import { Anthropic } from "@anthropic-ai/sdk" + +import type { ModelInfo, ProviderSettings } from "@roo-code/types" + import { BedrockModelId, - ModelInfo as SharedModelInfo, bedrockDefaultModelId, bedrockModels, bedrockDefaultPromptRouterModelId, } from "../../shared/api" -import { ProviderSettings } from "../../schemas" import { ApiStream } from "../transform/stream" import { BaseProvider } from "./base-provider" import { logger } from "../../utils/logging" -// New cache-related imports import { MultiPointStrategy } from "../transform/cache-strategy/multi-point-strategy" import { ModelInfo as CacheModelInfo } from "../transform/cache-strategy/types" import { AMAZON_BEDROCK_REGION_INFO } from "../../shared/aws_regions" @@ -514,7 +514,7 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH * *************************************************************************************/ - private costModelConfig: { id: BedrockModelId | string; info: SharedModelInfo } = { + private costModelConfig: { id: BedrockModelId | string; info: ModelInfo } = { id: "", info: { maxTokens: 0, contextWindow: 0, supportsPromptCache: false, supportsImages: false }, } @@ -621,7 +621,7 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH } //Prompt Router responses come back in a different sequence and the model used is in the response and must be fetched by name - getModelById(modelId: string, modelType?: string): { id: BedrockModelId | string; info: SharedModelInfo } { + getModelById(modelId: string, modelType?: string): { id: BedrockModelId | string; info: ModelInfo } { // Try to find the model in bedrockModels const baseModelId = this.parseBaseModelId(modelId) as BedrockModelId @@ -651,7 +651,7 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH return model } - override getModel(): { id: BedrockModelId | string; info: SharedModelInfo } { + override getModel(): { id: BedrockModelId | string; info: ModelInfo } { if (this.costModelConfig?.id?.trim().length > 0) { return this.costModelConfig } @@ -683,7 +683,7 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH modelConfig.info.maxTokens = modelConfig.info.maxTokens || BEDROCK_MAX_TOKENS - return modelConfig as { id: BedrockModelId | string; info: SharedModelInfo } + return modelConfig as { id: BedrockModelId | string; info: ModelInfo } } /************************************************************************************ @@ -695,10 +695,7 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH // Store previous cache point placements for maintaining consistency across consecutive messages private previousCachePointPlacements: { [conversationId: string]: any[] } = {} - private supportsAwsPromptCache(modelConfig: { - id: BedrockModelId | string - info: SharedModelInfo - }): boolean | undefined { + private supportsAwsPromptCache(modelConfig: { id: BedrockModelId | string; info: ModelInfo }): boolean | undefined { // Check if the model supports prompt cache // The cachableFields property is not part of the ModelInfo type in schemas // but it's used in the bedrockModels object in shared/api.ts diff --git a/src/api/providers/fake-ai.ts b/src/api/providers/fake-ai.ts index 9c4f1ca709..c73752fc66 100644 --- a/src/api/providers/fake-ai.ts +++ b/src/api/providers/fake-ai.ts @@ -1,7 +1,10 @@ import { Anthropic } from "@anthropic-ai/sdk" -import { ApiHandlerOptions, ModelInfo } from "../../shared/api" -import { ApiStream } from "../transform/stream" + +import type { ModelInfo } from "@roo-code/types" + import type { ApiHandler, SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" +import type { ApiHandlerOptions } from "../../shared/api" +import { ApiStream } from "../transform/stream" interface FakeAI { /** diff --git a/src/api/providers/fetchers/glama.ts b/src/api/providers/fetchers/glama.ts index 82ceba5233..9fd57e2c68 100644 --- a/src/api/providers/fetchers/glama.ts +++ b/src/api/providers/fetchers/glama.ts @@ -1,7 +1,8 @@ import axios from "axios" -import { ModelInfo } from "../../../shared/api" -import { parseApiPrice } from "../../../utils/cost" +import type { ModelInfo } from "@roo-code/types" + +import { parseApiPrice } from "../../../shared/cost" export async function getGlamaModels(): Promise> { const models: Record = {} diff --git a/src/api/providers/fetchers/litellm.ts b/src/api/providers/fetchers/litellm.ts index 8fb495c63e..a3591d7466 100644 --- a/src/api/providers/fetchers/litellm.ts +++ b/src/api/providers/fetchers/litellm.ts @@ -1,4 +1,5 @@ import axios from "axios" + import { OPEN_ROUTER_COMPUTER_USE_MODELS, ModelRecord } from "../../../shared/api" /** diff --git a/src/api/providers/fetchers/openrouter.ts b/src/api/providers/fetchers/openrouter.ts index 3841b11246..b410d06fc0 100644 --- a/src/api/providers/fetchers/openrouter.ts +++ b/src/api/providers/fetchers/openrouter.ts @@ -1,16 +1,16 @@ import axios from "axios" import { z } from "zod" -import { isModelParameter } from "../../../schemas" +import { type ModelInfo, isModelParameter } from "@roo-code/types" + import { ApiHandlerOptions, - ModelInfo, OPEN_ROUTER_COMPUTER_USE_MODELS, OPEN_ROUTER_REASONING_BUDGET_MODELS, OPEN_ROUTER_REQUIRED_REASONING_BUDGET_MODELS, anthropicModels, } from "../../../shared/api" -import { parseApiPrice } from "../../../utils/cost" +import { parseApiPrice } from "../../../shared/cost" /** * OpenRouterBaseModel diff --git a/src/api/providers/fetchers/requesty.ts b/src/api/providers/fetchers/requesty.ts index 7fe6e41a2b..41f2bdb9b6 100644 --- a/src/api/providers/fetchers/requesty.ts +++ b/src/api/providers/fetchers/requesty.ts @@ -1,7 +1,8 @@ import axios from "axios" -import { ModelInfo } from "../../../shared/api" -import { parseApiPrice } from "../../../utils/cost" +import type { ModelInfo } from "@roo-code/types" + +import { parseApiPrice } from "../../../shared/cost" export async function getRequestyModels(apiKey?: string): Promise> { const models: Record = {} diff --git a/src/api/providers/fetchers/unbound.ts b/src/api/providers/fetchers/unbound.ts index 7834debf35..98c0c58fa5 100644 --- a/src/api/providers/fetchers/unbound.ts +++ b/src/api/providers/fetchers/unbound.ts @@ -1,6 +1,6 @@ import axios from "axios" -import { ModelInfo } from "../../../shared/api" +import type { ModelInfo } from "@roo-code/types" export async function getUnboundModels(apiKey?: string | null): Promise> { const models: Record = {} diff --git a/src/api/providers/gemini.ts b/src/api/providers/gemini.ts index 31c802d2de..e5ceffbf43 100644 --- a/src/api/providers/gemini.ts +++ b/src/api/providers/gemini.ts @@ -7,12 +7,15 @@ import { } from "@google/genai" import type { JWTInput } from "google-auth-library" -import { ApiHandlerOptions, ModelInfo, GeminiModelId, geminiDefaultModelId, geminiModels } from "../../shared/api" +import type { ModelInfo } from "@roo-code/types" + +import { ApiHandlerOptions, GeminiModelId, geminiDefaultModelId, geminiModels } from "../../shared/api" import { safeJsonParse } from "../../shared/safeJsonParse" -import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" import { convertAnthropicContentToGemini, convertAnthropicMessageToGemini } from "../transform/gemini-format" import type { ApiStream } from "../transform/stream" + +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" import { BaseProvider } from "./base-provider" type GeminiHandlerOptions = ApiHandlerOptions & { diff --git a/src/api/providers/glama.ts b/src/api/providers/glama.ts index e743f82399..db2a3f84b6 100644 --- a/src/api/providers/glama.ts +++ b/src/api/providers/glama.ts @@ -2,7 +2,7 @@ import { Anthropic } from "@anthropic-ai/sdk" import axios from "axios" import OpenAI from "openai" -import { Package } from "../../schemas" +import { Package } from "../../shared/package" import { ApiHandlerOptions, glamaDefaultModelId, glamaDefaultModelInfo } from "../../shared/api" import { ApiStream } from "../transform/stream" diff --git a/src/api/providers/human-relay.ts b/src/api/providers/human-relay.ts index 4abdf7b0c0..c1dc3506e9 100644 --- a/src/api/providers/human-relay.ts +++ b/src/api/providers/human-relay.ts @@ -1,10 +1,13 @@ import { Anthropic } from "@anthropic-ai/sdk" import * as vscode from "vscode" -import { ModelInfo } from "../../shared/api" +import type { ModelInfo } from "@roo-code/types" + import { getCommand } from "../../utils/commands" import { ApiStream } from "../transform/stream" + import type { ApiHandler, SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" + /** * Human Relay API processor * This processor does not directly call the API, but interacts with the model through human operations copy and paste. diff --git a/src/api/providers/index.ts b/src/api/providers/index.ts new file mode 100644 index 0000000000..dd2a65dd75 --- /dev/null +++ b/src/api/providers/index.ts @@ -0,0 +1,22 @@ +export { GlamaHandler } from "./glama" +export { AnthropicHandler } from "./anthropic" +export { AwsBedrockHandler } from "./bedrock" +export { OpenRouterHandler } from "./openrouter" +export { VertexHandler } from "./vertex" +export { AnthropicVertexHandler } from "./anthropic-vertex" +export { OpenAiHandler } from "./openai" +export { OllamaHandler } from "./ollama" +export { LmStudioHandler } from "./lmstudio" +export { GeminiHandler } from "./gemini" +export { OpenAiNativeHandler } from "./openai-native" +export { DeepSeekHandler } from "./deepseek" +export { MistralHandler } from "./mistral" +export { VsCodeLmHandler } from "./vscode-lm" +export { UnboundHandler } from "./unbound" +export { RequestyHandler } from "./requesty" +export { HumanRelayHandler } from "./human-relay" +export { FakeAIHandler } from "./fake-ai" +export { XAIHandler } from "./xai" +export { GroqHandler } from "./groq" +export { ChutesHandler } from "./chutes" +export { LiteLLMHandler } from "./litellm" diff --git a/src/api/providers/lmstudio.ts b/src/api/providers/lmstudio.ts index e1aee5e53e..bac6b05551 100644 --- a/src/api/providers/lmstudio.ts +++ b/src/api/providers/lmstudio.ts @@ -2,11 +2,15 @@ import { Anthropic } from "@anthropic-ai/sdk" import OpenAI from "openai" import axios from "axios" -import { ApiHandlerOptions, ModelInfo, openAiModelInfoSaneDefaults } from "../../shared/api" +import type { ModelInfo } from "@roo-code/types" + +import { ApiHandlerOptions, openAiModelInfoSaneDefaults } from "../../shared/api" +import { XmlMatcher } from "../../utils/xml-matcher" + import { convertToOpenAiMessages } from "../transform/openai-format" import { ApiStream } from "../transform/stream" + import { BaseProvider } from "./base-provider" -import { XmlMatcher } from "../../utils/xml-matcher" import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" const LMSTUDIO_DEFAULT_TEMPERATURE = 0 diff --git a/src/api/providers/mistral.ts b/src/api/providers/mistral.ts index 58cd7c7952..5aafb16012 100644 --- a/src/api/providers/mistral.ts +++ b/src/api/providers/mistral.ts @@ -1,8 +1,12 @@ import { Anthropic } from "@anthropic-ai/sdk" import { Mistral } from "@mistralai/mistralai" -import { ApiHandlerOptions, mistralDefaultModelId, MistralModelId, mistralModels, ModelInfo } from "../../shared/api" + +import type { ModelInfo } from "@roo-code/types" + +import { ApiHandlerOptions, mistralDefaultModelId, MistralModelId, mistralModels } from "../../shared/api" import { convertToMistralMessages } from "../transform/mistral-format" import { ApiStream } from "../transform/stream" + import { BaseProvider } from "./base-provider" import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" diff --git a/src/api/providers/ollama.ts b/src/api/providers/ollama.ts index ba2495c095..4a321895d0 100644 --- a/src/api/providers/ollama.ts +++ b/src/api/providers/ollama.ts @@ -2,14 +2,18 @@ import { Anthropic } from "@anthropic-ai/sdk" import OpenAI from "openai" import axios from "axios" -import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" -import { ApiHandlerOptions, ModelInfo, openAiModelInfoSaneDefaults } from "../../shared/api" +import type { ModelInfo } from "@roo-code/types" + +import { ApiHandlerOptions, openAiModelInfoSaneDefaults } from "../../shared/api" +import { XmlMatcher } from "../../utils/xml-matcher" + import { convertToOpenAiMessages } from "../transform/openai-format" import { convertToR1Format } from "../transform/r1-format" import { ApiStream } from "../transform/stream" + import { DEEP_SEEK_DEFAULT_TEMPERATURE } from "./constants" -import { XmlMatcher } from "../../utils/xml-matcher" import { BaseProvider } from "./base-provider" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" // Alias for the usage object returned in streaming chunks type CompletionUsage = OpenAI.Chat.Completions.ChatCompletionChunk["usage"] diff --git a/src/api/providers/openai-native.ts b/src/api/providers/openai-native.ts index 8ce7eaa5ef..41a3a63ae7 100644 --- a/src/api/providers/openai-native.ts +++ b/src/api/providers/openai-native.ts @@ -1,22 +1,23 @@ import { Anthropic } from "@anthropic-ai/sdk" import OpenAI from "openai" +import type { ModelInfo } from "@roo-code/types" + import { ApiHandlerOptions, - ModelInfo, openAiNativeDefaultModelId, OpenAiNativeModelId, openAiNativeModels, } from "../../shared/api" -import { calculateApiCostOpenAI } from "../../utils/cost" +import { calculateApiCostOpenAI } from "../../shared/cost" import { convertToOpenAiMessages } from "../transform/openai-format" import { ApiStream } from "../transform/stream" import { getModelParams } from "../transform/model-params" -import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" import { BaseProvider } from "./base-provider" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" const OPENAI_NATIVE_DEFAULT_TEMPERATURE = 0 diff --git a/src/api/providers/openai.ts b/src/api/providers/openai.ts index 69d0040d0d..43c5a0e6da 100644 --- a/src/api/providers/openai.ts +++ b/src/api/providers/openai.ts @@ -2,12 +2,9 @@ import { Anthropic } from "@anthropic-ai/sdk" import OpenAI, { AzureOpenAI } from "openai" import axios from "axios" -import { - ApiHandlerOptions, - azureOpenAiDefaultApiVersion, - ModelInfo, - openAiModelInfoSaneDefaults, -} from "../../shared/api" +import type { ModelInfo } from "@roo-code/types" + +import { ApiHandlerOptions, azureOpenAiDefaultApiVersion, openAiModelInfoSaneDefaults } from "../../shared/api" import { XmlMatcher } from "../../utils/xml-matcher" @@ -18,8 +15,8 @@ import { ApiStream, ApiStreamUsageChunk } from "../transform/stream" import { getModelParams } from "../transform/model-params" import { DEFAULT_HEADERS, DEEP_SEEK_DEFAULT_TEMPERATURE } from "./constants" -import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" import { BaseProvider } from "./base-provider" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" export const AZURE_AI_INFERENCE_PATH = "/models/chat/completions" diff --git a/src/api/providers/requesty.ts b/src/api/providers/requesty.ts index 1c21af5241..0fbe224668 100644 --- a/src/api/providers/requesty.ts +++ b/src/api/providers/requesty.ts @@ -1,19 +1,18 @@ import { Anthropic } from "@anthropic-ai/sdk" -import { - ApiHandlerOptions, - ModelInfo, - ModelRecord, - requestyDefaultModelId, - requestyDefaultModelInfo, -} from "../../shared/api" +import OpenAI from "openai" + +import type { ModelInfo } from "@roo-code/types" + +import { ApiHandlerOptions, ModelRecord, requestyDefaultModelId, requestyDefaultModelInfo } from "../../shared/api" +import { calculateApiCostOpenAI } from "../../shared/cost" + import { convertToOpenAiMessages } from "../transform/openai-format" -import { calculateApiCostOpenAI } from "../../utils/cost" import { ApiStream, ApiStreamUsageChunk } from "../transform/stream" -import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../" -import { BaseProvider } from "./base-provider" + import { DEFAULT_HEADERS } from "./constants" import { getModels } from "./fetchers/modelCache" -import OpenAI from "openai" +import { BaseProvider } from "./base-provider" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../" // Requesty usage includes an extra field for Anthropic use cases. // Safely cast the prompt token details section to the appropriate structure. diff --git a/src/api/providers/router-provider.ts b/src/api/providers/router-provider.ts index 30093be9b8..c64b29571a 100644 --- a/src/api/providers/router-provider.ts +++ b/src/api/providers/router-provider.ts @@ -1,6 +1,9 @@ import OpenAI from "openai" -import { ApiHandlerOptions, RouterName, ModelRecord, ModelInfo } from "../../shared/api" +import type { ModelInfo } from "@roo-code/types" + +import { ApiHandlerOptions, RouterName, ModelRecord } from "../../shared/api" + import { BaseProvider } from "./base-provider" import { getModels } from "./fetchers/modelCache" diff --git a/src/api/providers/vertex.ts b/src/api/providers/vertex.ts index 6d24f60e58..2bc940de7a 100644 --- a/src/api/providers/vertex.ts +++ b/src/api/providers/vertex.ts @@ -1,7 +1,9 @@ -import { ApiHandlerOptions, ModelInfo, VertexModelId, vertexDefaultModelId, vertexModels } from "../../shared/api" +import type { ModelInfo } from "@roo-code/types" + +import { ApiHandlerOptions, VertexModelId, vertexDefaultModelId, vertexModels } from "../../shared/api" -import { SingleCompletionHandler } from "../index" import { GeminiHandler } from "./gemini" +import { SingleCompletionHandler } from "../index" export class VertexHandler extends GeminiHandler implements SingleCompletionHandler { constructor(options: ApiHandlerOptions) { diff --git a/src/api/providers/vscode-lm.ts b/src/api/providers/vscode-lm.ts index 61aab91123..5990193ecb 100644 --- a/src/api/providers/vscode-lm.ts +++ b/src/api/providers/vscode-lm.ts @@ -1,12 +1,16 @@ import { Anthropic } from "@anthropic-ai/sdk" import * as vscode from "vscode" +import type { ModelInfo } from "@roo-code/types" + +import { SELECTOR_SEPARATOR, stringifyVsCodeLmModelSelector } from "../../shared/vsCodeSelectorUtils" +import { ApiHandlerOptions, openAiModelInfoSaneDefaults } from "../../shared/api" + import { ApiStream } from "../transform/stream" import { convertToVsCodeLmMessages } from "../transform/vscode-lm-format" -import { SELECTOR_SEPARATOR, stringifyVsCodeLmModelSelector } from "../../shared/vsCodeSelectorUtils" -import { ApiHandlerOptions, ModelInfo, openAiModelInfoSaneDefaults } from "../../shared/api" + import { BaseProvider } from "./base-provider" -import type { SingleCompletionHandler, ApiHandler, ApiHandlerCreateMessageMetadata } from "../index" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" /** * Handles interaction with VS Code's Language Model API for chat-based operations. diff --git a/src/api/transform/__tests__/image-cleaning.test.ts b/src/api/transform/__tests__/image-cleaning.test.ts index cbb318531a..6260954e89 100644 --- a/src/api/transform/__tests__/image-cleaning.test.ts +++ b/src/api/transform/__tests__/image-cleaning.test.ts @@ -1,7 +1,8 @@ -import { ApiHandler } from "../.." +import type { ModelInfo } from "@roo-code/types" + +import { ApiHandler } from "../../index" import { ApiMessage } from "../../../core/task-persistence/apiMessages" import { maybeRemoveImageBlocks } from "../image-cleaning" -import { ModelInfo } from "../../../shared/api" describe("maybeRemoveImageBlocks", () => { // Mock ApiHandler factory function diff --git a/src/api/transform/__tests__/model-params.test.ts b/src/api/transform/__tests__/model-params.test.ts index 344659328f..2eabe1c7fa 100644 --- a/src/api/transform/__tests__/model-params.test.ts +++ b/src/api/transform/__tests__/model-params.test.ts @@ -1,6 +1,7 @@ // npx jest src/api/transform/__tests__/model-params.test.ts -import { ModelInfo } from "../../../schemas" +import type { ModelInfo } from "@roo-code/types" + import { ANTHROPIC_DEFAULT_MAX_TOKENS } from "../../providers/constants" import { getModelParams } from "../model-params" diff --git a/src/api/transform/__tests__/reasoning.test.ts b/src/api/transform/__tests__/reasoning.test.ts index a03728f366..47a0317a50 100644 --- a/src/api/transform/__tests__/reasoning.test.ts +++ b/src/api/transform/__tests__/reasoning.test.ts @@ -1,6 +1,7 @@ // npx jest src/api/transform/__tests__/reasoning.test.ts -import { ModelInfo, ProviderSettings } from "../../../schemas" +import type { ModelInfo, ProviderSettings } from "@roo-code/types" + import { getOpenRouterReasoning, getAnthropicReasoning, diff --git a/src/api/transform/image-cleaning.ts b/src/api/transform/image-cleaning.ts index e5987bb59e..04ac3a9f65 100644 --- a/src/api/transform/image-cleaning.ts +++ b/src/api/transform/image-cleaning.ts @@ -1,6 +1,7 @@ -import { ApiHandler } from ".." import { ApiMessage } from "../../core/task-persistence/apiMessages" +import { ApiHandler } from "../index" + /* Removes image blocks from messages if they are not supported by the Api Handler */ export function maybeRemoveImageBlocks(messages: ApiMessage[], apiHandler: ApiHandler): ApiMessage[] { return messages.map((message) => { diff --git a/src/api/transform/model-params.ts b/src/api/transform/model-params.ts index 9abe613714..2fb5012655 100644 --- a/src/api/transform/model-params.ts +++ b/src/api/transform/model-params.ts @@ -1,10 +1,7 @@ +import type { ModelInfo, ProviderSettings } from "@roo-code/types" + import { ANTHROPIC_DEFAULT_MAX_TOKENS } from "../providers/constants" -import { - shouldUseReasoningBudget, - shouldUseReasoningEffort, - type ModelInfo, - type ProviderSettings, -} from "../../shared/api" +import { shouldUseReasoningBudget, shouldUseReasoningEffort } from "../../shared/api" import { type AnthropicReasoningParams, diff --git a/src/api/transform/reasoning.ts b/src/api/transform/reasoning.ts index 7c9fcddb4e..9887f1137a 100644 --- a/src/api/transform/reasoning.ts +++ b/src/api/transform/reasoning.ts @@ -1,7 +1,8 @@ import { BetaThinkingConfigParam } from "@anthropic-ai/sdk/resources/beta" import OpenAI from "openai" -import { ModelInfo, ProviderSettings } from "../../schemas" +import type { ModelInfo, ProviderSettings } from "@roo-code/types" + import { shouldUseReasoningBudget, shouldUseReasoningEffort } from "../../shared/api" type ReasoningEffort = "low" | "medium" | "high" diff --git a/src/core/assistant-message/parseAssistantMessage.ts b/src/core/assistant-message/parseAssistantMessage.ts index f641cabdaa..2fe747a6df 100644 --- a/src/core/assistant-message/parseAssistantMessage.ts +++ b/src/core/assistant-message/parseAssistantMessage.ts @@ -1,5 +1,6 @@ +import { type ToolName, toolNames } from "@roo-code/types" + import { TextContent, ToolUse, ToolParamName, toolParamNames } from "../../shared/tools" -import { toolNames, ToolName } from "../../schemas" export type AssistantMessageContent = TextContent | ToolUse diff --git a/src/core/assistant-message/parseAssistantMessageV2.ts b/src/core/assistant-message/parseAssistantMessageV2.ts index d24a67f83d..6d3594cf60 100644 --- a/src/core/assistant-message/parseAssistantMessageV2.ts +++ b/src/core/assistant-message/parseAssistantMessageV2.ts @@ -1,5 +1,6 @@ +import { type ToolName, toolNames } from "@roo-code/types" + import { TextContent, ToolUse, ToolParamName, toolParamNames } from "../../shared/tools" -import { toolNames, ToolName } from "../../schemas" export type AssistantMessageContent = TextContent | ToolUse diff --git a/src/core/assistant-message/presentAssistantMessage.ts b/src/core/assistant-message/presentAssistantMessage.ts index 6d37063457..77c510889b 100644 --- a/src/core/assistant-message/presentAssistantMessage.ts +++ b/src/core/assistant-message/presentAssistantMessage.ts @@ -1,11 +1,10 @@ import cloneDeep from "clone-deep" import { serializeError } from "serialize-error" -import type { ToolName } from "../../schemas" +import type { ToolName, ClineAsk, ToolProgressStatus } from "@roo-code/types" import { defaultModeSlug, getModeBySlug } from "../../shared/modes" import type { ToolParamName, ToolResponse } from "../../shared/tools" -import type { ClineAsk, ToolProgressStatus } from "../../shared/ExtensionMessage" import { telemetryService } from "../../services/telemetry/TelemetryService" diff --git a/src/core/config/ContextProxy.ts b/src/core/config/ContextProxy.ts index c2373ccad2..874ed719f2 100644 --- a/src/core/config/ContextProxy.ts +++ b/src/core/config/ContextProxy.ts @@ -6,15 +6,16 @@ import { GLOBAL_SETTINGS_KEYS, SECRET_STATE_KEYS, GLOBAL_STATE_KEYS, - ProviderSettings, - GlobalSettings, - SecretState, - GlobalState, - RooCodeSettings, + type ProviderSettings, + type GlobalSettings, + type SecretState, + type GlobalState, + type RooCodeSettings, providerSettingsSchema, globalSettingsSchema, isSecretStateKey, -} from "../../schemas" +} from "@roo-code/types" + import { logger } from "../../utils/logging" import { telemetryService } from "../../services/telemetry/TelemetryService" diff --git a/src/core/config/CustomModesManager.ts b/src/core/config/CustomModesManager.ts index 743f96c00e..dc830688e1 100644 --- a/src/core/config/CustomModesManager.ts +++ b/src/core/config/CustomModesManager.ts @@ -1,13 +1,15 @@ import * as vscode from "vscode" import * as path from "path" import * as fs from "fs/promises" -import { customModesSettingsSchema } from "../../schemas" -import { ModeConfig } from "../../shared/modes" + +import * as yaml from "yaml" + +import { type ModeConfig, customModesSettingsSchema } from "@roo-code/types" + import { fileExistsAtPath } from "../../utils/fs" import { arePathsEqual, getWorkspacePath } from "../../utils/path" import { logger } from "../../utils/logging" import { GlobalFileNames } from "../../shared/globalFileNames" -import * as yaml from "yaml" const ROOMODES_FILENAME = ".roomodes" diff --git a/src/core/config/ProviderSettingsManager.ts b/src/core/config/ProviderSettingsManager.ts index d22a87e097..d4f2715318 100644 --- a/src/core/config/ProviderSettingsManager.ts +++ b/src/core/config/ProviderSettingsManager.ts @@ -1,7 +1,12 @@ import { ExtensionContext } from "vscode" import { z, ZodError } from "zod" -import { providerSettingsSchema, ProviderSettingsEntry, providerSettingsSchemaDiscriminated } from "../../schemas" +import { + type ProviderSettingsEntry, + providerSettingsSchema, + providerSettingsSchemaDiscriminated, +} from "@roo-code/types" + import { Mode, modes } from "../../shared/modes" import { telemetryService } from "../../services/telemetry/TelemetryService" diff --git a/src/core/config/__tests__/ContextProxy.test.ts b/src/core/config/__tests__/ContextProxy.test.ts index bdd3d5ddc5..498c1e2199 100644 --- a/src/core/config/__tests__/ContextProxy.test.ts +++ b/src/core/config/__tests__/ContextProxy.test.ts @@ -1,9 +1,10 @@ // npx jest src/core/config/__tests__/ContextProxy.test.ts import * as vscode from "vscode" -import { ContextProxy } from "../ContextProxy" -import { GLOBAL_STATE_KEYS, SECRET_STATE_KEYS } from "../../../schemas" +import { GLOBAL_STATE_KEYS, SECRET_STATE_KEYS } from "@roo-code/types" + +import { ContextProxy } from "../ContextProxy" jest.mock("vscode", () => ({ Uri: { diff --git a/src/core/config/__tests__/CustomModesManager.test.ts b/src/core/config/__tests__/CustomModesManager.test.ts index 15bf244726..cb49c68a05 100644 --- a/src/core/config/__tests__/CustomModesManager.test.ts +++ b/src/core/config/__tests__/CustomModesManager.test.ts @@ -3,12 +3,16 @@ import * as vscode from "vscode" import * as path from "path" import * as fs from "fs/promises" -import { CustomModesManager } from "../CustomModesManager" -import { ModeConfig } from "../../../shared/modes" + +import * as yaml from "yaml" + +import type { ModeConfig } from "@roo-code/types" + import { fileExistsAtPath } from "../../../utils/fs" import { getWorkspacePath, arePathsEqual } from "../../../utils/path" import { GlobalFileNames } from "../../../shared/globalFileNames" -import * as yaml from "yaml" + +import { CustomModesManager } from "../CustomModesManager" jest.mock("vscode") jest.mock("fs/promises") diff --git a/src/core/config/__tests__/CustomModesSettings.test.ts b/src/core/config/__tests__/CustomModesSettings.test.ts index 247bced8b3..117bdbe571 100644 --- a/src/core/config/__tests__/CustomModesSettings.test.ts +++ b/src/core/config/__tests__/CustomModesSettings.test.ts @@ -1,9 +1,9 @@ // npx jest src/core/config/__tests__/CustomModesSettings.test.ts -import { customModesSettingsSchema } from "../../../schemas" -import { ModeConfig } from "../../../shared/modes" import { ZodError } from "zod" +import { type ModeConfig, customModesSettingsSchema } from "@roo-code/types" + describe("CustomModesSettings", () => { const validMode = { slug: "123e4567-e89b-12d3-a456-426614174000", diff --git a/src/core/config/__tests__/ModeConfig.test.ts b/src/core/config/__tests__/ModeConfig.test.ts index e246a7ec4b..099910b241 100644 --- a/src/core/config/__tests__/ModeConfig.test.ts +++ b/src/core/config/__tests__/ModeConfig.test.ts @@ -2,8 +2,7 @@ import { ZodError } from "zod" -import { modeConfigSchema } from "../../../schemas" -import { ModeConfig } from "../../../shared/modes" +import { type ModeConfig, modeConfigSchema } from "@roo-code/types" function validateCustomMode(mode: unknown): asserts mode is ModeConfig { modeConfigSchema.parse(mode) diff --git a/src/core/config/__tests__/ProviderSettingsManager.test.ts b/src/core/config/__tests__/ProviderSettingsManager.test.ts index 3eb436a079..ff2061be13 100644 --- a/src/core/config/__tests__/ProviderSettingsManager.test.ts +++ b/src/core/config/__tests__/ProviderSettingsManager.test.ts @@ -2,7 +2,8 @@ import { ExtensionContext } from "vscode" -import { ProviderSettings } from "../../../schemas" +import type { ProviderSettings } from "@roo-code/types" + import { ProviderSettingsManager, ProviderProfiles } from "../ProviderSettingsManager" // Mock VSCode ExtensionContext diff --git a/src/core/config/__tests__/importExport.test.ts b/src/core/config/__tests__/importExport.test.ts index 3fe5e97595..40def4ebcd 100644 --- a/src/core/config/__tests__/importExport.test.ts +++ b/src/core/config/__tests__/importExport.test.ts @@ -5,7 +5,8 @@ import * as path from "path" import * as vscode from "vscode" -import { ProviderName } from "../../../schemas" +import type { ProviderName } from "@roo-code/types" + import { importSettings, exportSettings } from "../importExport" import { ProviderSettingsManager } from "../ProviderSettingsManager" import { ContextProxy } from "../ContextProxy" diff --git a/src/core/config/importExport.ts b/src/core/config/importExport.ts index 457e91fa37..b9caef727e 100644 --- a/src/core/config/importExport.ts +++ b/src/core/config/importExport.ts @@ -5,12 +5,13 @@ import fs from "fs/promises" import * as vscode from "vscode" import { z, ZodError } from "zod" -import { globalSettingsSchema } from "../../schemas" +import { globalSettingsSchema } from "@roo-code/types" + +import { telemetryService } from "../../services/telemetry/TelemetryService" import { ProviderSettingsManager, providerProfilesSchema } from "./ProviderSettingsManager" import { ContextProxy } from "./ContextProxy" import { CustomModesManager } from "./CustomModesManager" -import { telemetryService } from "../../services/telemetry/TelemetryService" type ImportOptions = { providerSettingsManager: ProviderSettingsManager diff --git a/src/core/diff/strategies/multi-search-replace.ts b/src/core/diff/strategies/multi-search-replace.ts index fdaba9ecbf..af5a03e468 100644 --- a/src/core/diff/strategies/multi-search-replace.ts +++ b/src/core/diff/strategies/multi-search-replace.ts @@ -2,8 +2,9 @@ import { distance } from "fastest-levenshtein" +import { ToolProgressStatus } from "@roo-code/types" + import { addLineNumbers, everyLineHasLineNumbers, stripLineNumbers } from "../../../integrations/misc/extract-text" -import { ToolProgressStatus } from "../../../shared/ExtensionMessage" import { ToolUse, DiffStrategy, DiffResult } from "../../../shared/tools" import { normalizeString } from "../../../utils/text-normalization" diff --git a/src/core/environment/getEnvironmentDetails.ts b/src/core/environment/getEnvironmentDetails.ts index 3d8a9cdbc3..1f8c82b1a4 100644 --- a/src/core/environment/getEnvironmentDetails.ts +++ b/src/core/environment/getEnvironmentDetails.ts @@ -5,7 +5,9 @@ import * as vscode from "vscode" import pWaitFor from "p-wait-for" import delay from "delay" -import { EXPERIMENT_IDS, experiments as Experiments, ExperimentId } from "../../shared/experiments" +import type { ExperimentId } from "@roo-code/types" + +import { EXPERIMENT_IDS, experiments as Experiments } from "../../shared/experiments" import { formatLanguage } from "../../shared/language" import { defaultModeSlug, getFullModeDetails, getModeBySlug, isToolAllowedForMode } from "../../shared/modes" import { getApiMetrics } from "../../shared/getApiMetrics" diff --git a/src/core/prompts/__tests__/system.test.ts b/src/core/prompts/__tests__/system.test.ts index 015ef43c01..2e5b25b65c 100644 --- a/src/core/prompts/__tests__/system.test.ts +++ b/src/core/prompts/__tests__/system.test.ts @@ -1,9 +1,13 @@ +// npx jest src/core/prompts/__tests__/system.test.ts + import * as vscode from "vscode" +import { ModeConfig } from "@roo-code/types" + import { SYSTEM_PROMPT } from "../system" import { McpHub } from "../../../services/mcp/McpHub" -import { defaultModeSlug, modes, Mode, ModeConfig } from "../../../shared/modes" -import "../../../utils/path" // Import path utils to get access to toPosix string extension. +import { defaultModeSlug, modes, Mode } from "../../../shared/modes" +import "../../../utils/path" import { addCustomInstructions } from "../sections/custom-instructions" import { MultiSearchReplaceDiffStrategy } from "../../diff/strategies/multi-search-replace" diff --git a/src/core/prompts/sections/custom-instructions.ts b/src/core/prompts/sections/custom-instructions.ts index cf1aea24ff..f9f4b7dea0 100644 --- a/src/core/prompts/sections/custom-instructions.ts +++ b/src/core/prompts/sections/custom-instructions.ts @@ -1,9 +1,11 @@ import fs from "fs/promises" import path from "path" - -import { LANGUAGES, isLanguage } from "../../../shared/language" import { Dirent } from "fs" +import { isLanguage } from "@roo-code/types" + +import { LANGUAGES } from "../../../shared/language" + /** * Safely read a file and return its trimmed content */ diff --git a/src/core/prompts/sections/modes.ts b/src/core/prompts/sections/modes.ts index ff12098d5e..9b863840c0 100644 --- a/src/core/prompts/sections/modes.ts +++ b/src/core/prompts/sections/modes.ts @@ -2,7 +2,9 @@ import * as path from "path" import * as vscode from "vscode" import { promises as fs } from "fs" -import { ModeConfig, getAllModesWithPrompts } from "../../../shared/modes" +import type { ModeConfig } from "@roo-code/types" + +import { getAllModesWithPrompts } from "../../../shared/modes" export async function getModesSection(context: vscode.ExtensionContext): Promise { const settingsDir = path.join(context.globalStorageUri.fsPath, "settings") diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index 7a4d152ef9..b5471cea9a 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -1,19 +1,18 @@ -import { - Mode, - modes, - CustomModePrompts, - PromptComponent, - defaultModeSlug, - ModeConfig, - getModeBySlug, - getGroupName, -} from "../../shared/modes" -import { PromptVariables, loadSystemPromptFile } from "./sections/custom-system-prompt" -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 type { ModeConfig, PromptComponent, CustomModePrompts } from "@roo-code/types" + +import { Mode, modes, defaultModeSlug, getModeBySlug, getGroupName } from "../../shared/modes" +import { DiffStrategy } from "../../shared/tools" +import { formatLanguage } from "../../shared/language" + +import { McpHub } from "../../services/mcp/McpHub" +import { CodeIndexManager } from "../../services/code-index/manager" + +import { PromptVariables, loadSystemPromptFile } from "./sections/custom-system-prompt" + +import { getToolDescriptionsForMode } from "./tools" import { getRulesSection, getSystemInfoSection, @@ -26,8 +25,6 @@ import { addCustomInstructions, markdownFormattingSection, } from "./sections" -import { formatLanguage } from "../../shared/language" -import { CodeIndexManager } from "../../services/code-index/manager" async function generatePrompt( context: vscode.ExtensionContext, diff --git a/src/core/prompts/tools/index.ts b/src/core/prompts/tools/index.ts index 675fc8f524..d610b83274 100644 --- a/src/core/prompts/tools/index.ts +++ b/src/core/prompts/tools/index.ts @@ -1,7 +1,8 @@ -import { ToolName } from "../../../schemas" +import type { ToolName, ModeConfig } from "@roo-code/types" + import { TOOL_GROUPS, ALWAYS_AVAILABLE_TOOLS, DiffStrategy } from "../../../shared/tools" import { McpHub } from "../../../services/mcp/McpHub" -import { Mode, ModeConfig, getModeConfig, isToolAllowedForMode, getGroupName } from "../../../shared/modes" +import { Mode, getModeConfig, isToolAllowedForMode, getGroupName } from "../../../shared/modes" import { ToolArgs } from "./types" import { getExecuteCommandDescription } from "./execute-command" diff --git a/src/core/sliding-window/__tests__/sliding-window.test.ts b/src/core/sliding-window/__tests__/sliding-window.test.ts index d48abff449..74bbdf0caa 100644 --- a/src/core/sliding-window/__tests__/sliding-window.test.ts +++ b/src/core/sliding-window/__tests__/sliding-window.test.ts @@ -2,16 +2,18 @@ import { Anthropic } from "@anthropic-ai/sdk" -import { ModelInfo } from "../../../shared/api" +import type { ModelInfo } from "@roo-code/types" + import { BaseProvider } from "../../../api/providers/base-provider" +import { ApiMessage } from "../../task-persistence/apiMessages" +import * as condenseModule from "../../condense" + import { TOKEN_BUFFER_PERCENTAGE, estimateTokenCount, truncateConversation, truncateConversationIfNeeded, } from "../index" -import { ApiMessage } from "../../task-persistence/apiMessages" -import * as condenseModule from "../../condense" // Create a mock ApiHandler for testing class MockApiHandler extends BaseProvider { diff --git a/src/core/task-persistence/taskMessages.ts b/src/core/task-persistence/taskMessages.ts index 54d33b1a51..3ed5c5099e 100644 --- a/src/core/task-persistence/taskMessages.ts +++ b/src/core/task-persistence/taskMessages.ts @@ -1,10 +1,11 @@ import * as path from "path" import * as fs from "fs/promises" +import type { ClineMessage } from "@roo-code/types" + import { fileExistsAtPath } from "../../utils/fs" import { GlobalFileNames } from "../../shared/globalFileNames" -import { ClineMessage } from "../../shared/ExtensionMessage" import { getTaskDirectoryPath } from "../../utils/storage" export type ReadTaskMessagesOptions = { diff --git a/src/core/task-persistence/taskMetadata.ts b/src/core/task-persistence/taskMetadata.ts index 0a028e5ba8..8044acd8ba 100644 --- a/src/core/task-persistence/taskMetadata.ts +++ b/src/core/task-persistence/taskMetadata.ts @@ -1,12 +1,12 @@ import NodeCache from "node-cache" import getFolderSize from "get-folder-size" -import { ClineMessage } from "../../shared/ExtensionMessage" +import type { ClineMessage, HistoryItem } from "@roo-code/types" + import { combineApiRequests } from "../../shared/combineApiRequests" import { combineCommandSequences } from "../../shared/combineCommandSequences" import { getApiMetrics } from "../../shared/getApiMetrics" import { findLastIndex } from "../../shared/array" -import { HistoryItem } from "../../shared/HistoryItem" import { getTaskDirectoryPath } from "../../utils/storage" const taskSizeCache = new NodeCache({ stdTTL: 30, checkperiod: 5 * 60 }) diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index adbf871d73..c53385d2e3 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -8,29 +8,30 @@ import delay from "delay" import pWaitFor from "p-wait-for" import { serializeError } from "serialize-error" -// schemas -import { TokenUsage, ToolUsage, ToolName, ContextCondense } from "../../schemas" +import type { + ProviderSettings, + TokenUsage, + ToolUsage, + ToolName, + ContextCondense, + ClineAsk, + ClineMessage, + ClineSay, + ToolProgressStatus, + HistoryItem, +} from "@roo-code/types" // api import { ApiHandler, ApiHandlerCreateMessageMetadata, buildApiHandler } from "../../api" import { ApiStream } from "../../api/transform/stream" // shared -import { ProviderSettings } from "../../shared/api" import { findLastIndex } from "../../shared/array" import { combineApiRequests } from "../../shared/combineApiRequests" import { combineCommandSequences } from "../../shared/combineCommandSequences" import { t } from "../../i18n" -import { - ClineApiReqCancelReason, - ClineApiReqInfo, - ClineAsk, - ClineMessage, - ClineSay, - ToolProgressStatus, -} from "../../shared/ExtensionMessage" +import { ClineApiReqCancelReason, ClineApiReqInfo } from "../../shared/ExtensionMessage" import { getApiMetrics } from "../../shared/getApiMetrics" -import { HistoryItem } from "../../shared/HistoryItem" import { ClineAskResponse } from "../../shared/WebviewMessage" import { defaultModeSlug } from "../../shared/modes" import { DiffStrategy } from "../../shared/tools" @@ -50,7 +51,7 @@ import { RooTerminalProcess } from "../../integrations/terminal/types" import { TerminalRegistry } from "../../integrations/terminal/TerminalRegistry" // utils -import { calculateApiCostAnthropic } from "../../utils/cost" +import { calculateApiCostAnthropic } from "../../shared/cost" import { getWorkspacePath } from "../../utils/path" // prompts diff --git a/src/core/task/__tests__/Task.test.ts b/src/core/task/__tests__/Task.test.ts index c472355744..79641b56f1 100644 --- a/src/core/task/__tests__/Task.test.ts +++ b/src/core/task/__tests__/Task.test.ts @@ -6,10 +6,10 @@ import * as path from "path" import * as vscode from "vscode" import { Anthropic } from "@anthropic-ai/sdk" -import { GlobalState } from "../../../schemas" +import type { GlobalState, ProviderSettings, ModelInfo } from "@roo-code/types" + import { Task } from "../Task" import { ClineProvider } from "../../webview/ClineProvider" -import { ProviderSettings, ModelInfo } from "../../../shared/api" import { ApiStreamChunk } from "../../../api/transform/stream" import { ContextProxy } from "../../config/ContextProxy" import { processUserContentMentions } from "../../mentions/processUserContentMentions" diff --git a/src/core/tools/__tests__/ToolRepetitionDetector.test.ts b/src/core/tools/__tests__/ToolRepetitionDetector.test.ts index 846011b5d8..286a9559b0 100644 --- a/src/core/tools/__tests__/ToolRepetitionDetector.test.ts +++ b/src/core/tools/__tests__/ToolRepetitionDetector.test.ts @@ -1,6 +1,7 @@ // npx jest src/core/tools/__tests__/ToolRepetitionDetector.test.ts -import type { ToolName } from "../../../schemas" +import type { ToolName } from "@roo-code/types" + import type { ToolUse } from "../../../shared/tools" import { ToolRepetitionDetector } from "../ToolRepetitionDetector" diff --git a/src/core/tools/__tests__/executeCommandTool.test.ts b/src/core/tools/__tests__/executeCommandTool.test.ts index 615d72042d..d0b9a872c8 100644 --- a/src/core/tools/__tests__/executeCommandTool.test.ts +++ b/src/core/tools/__tests__/executeCommandTool.test.ts @@ -2,10 +2,11 @@ import { describe, expect, it, jest, beforeEach } from "@jest/globals" +import type { ToolUsage } from "@roo-code/types" + import { Task } from "../../task/Task" import { formatResponse } from "../../prompts/responses" import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../../shared/tools" -import { ToolUsage } from "../../../schemas" import { unescapeHtmlEntities } from "../../../utils/text-normalization" // Mock dependencies diff --git a/src/core/tools/__tests__/validateToolUse.test.ts b/src/core/tools/__tests__/validateToolUse.test.ts index da2550b4d6..4b673b85f3 100644 --- a/src/core/tools/__tests__/validateToolUse.test.ts +++ b/src/core/tools/__tests__/validateToolUse.test.ts @@ -1,7 +1,10 @@ // npx jest src/core/tools/__tests__/validateToolUse.test.ts -import { isToolAllowedForMode, modes, ModeConfig } from "../../../shared/modes" +import type { ModeConfig } from "@roo-code/types" + +import { isToolAllowedForMode, modes } from "../../../shared/modes" import { TOOL_GROUPS } from "../../../shared/tools" + import { validateToolUse } from "../validateToolUse" const [codeMode, architectMode, askMode] = modes.map((mode) => mode.slug) diff --git a/src/core/tools/executeCommandTool.ts b/src/core/tools/executeCommandTool.ts index 6f5fc714a8..f20b283082 100644 --- a/src/core/tools/executeCommandTool.ts +++ b/src/core/tools/executeCommandTool.ts @@ -3,8 +3,10 @@ import * as path from "path" import delay from "delay" +import { CommandExecutionStatus } from "@roo-code/types" + import { Task } from "../task/Task" -import { CommandExecutionStatus } from "../../schemas" + import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag, ToolResponse } from "../../shared/tools" import { formatResponse } from "../prompts/responses" import { unescapeHtmlEntities } from "../../utils/text-normalization" diff --git a/src/core/tools/validateToolUse.ts b/src/core/tools/validateToolUse.ts index 0b1623f057..f0ce9e16e6 100644 --- a/src/core/tools/validateToolUse.ts +++ b/src/core/tools/validateToolUse.ts @@ -1,5 +1,6 @@ -import { ToolName } from "../../schemas" -import { Mode, isToolAllowedForMode, ModeConfig } from "../../shared/modes" +import type { ToolName, ModeConfig } from "@roo-code/types" + +import { Mode, isToolAllowedForMode } from "../../shared/modes" export function validateToolUse( toolName: ToolName, diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 43f56b8fa5..be7908fda8 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -9,25 +9,26 @@ import axios from "axios" import pWaitFor from "p-wait-for" import * as vscode from "vscode" -import { +import type { GlobalState, ProviderName, ProviderSettings, RooCodeSettings, ProviderSettingsEntry, - Package, CodeActionId, CodeActionName, TerminalActionId, TerminalActionPromptType, -} from "../../schemas" + HistoryItem, +} from "@roo-code/types" + import { t } from "../../i18n" import { setPanel } from "../../activate/registerCommands" +import { Package } from "../../shared/package" import { requestyDefaultModelId, openRouterDefaultModelId, glamaDefaultModelId } from "../../shared/api" import { findLast } from "../../shared/array" import { supportPrompt } from "../../shared/support-prompt" import { GlobalFileNames } from "../../shared/globalFileNames" -import { HistoryItem } from "../../shared/HistoryItem" import { ExtensionMessage } from "../../shared/ExtensionMessage" import { Mode, defaultModeSlug } from "../../shared/modes" import { experimentDefault } from "../../shared/experiments" diff --git a/src/core/webview/__tests__/ClineProvider.test.ts b/src/core/webview/__tests__/ClineProvider.test.ts index 72a40e7044..f141dace36 100644 --- a/src/core/webview/__tests__/ClineProvider.test.ts +++ b/src/core/webview/__tests__/ClineProvider.test.ts @@ -4,14 +4,17 @@ import Anthropic from "@anthropic-ai/sdk" import * as vscode from "vscode" import axios from "axios" -import { ClineProvider } from "../ClineProvider" -import { ProviderSettingsEntry, ClineMessage, ExtensionMessage, ExtensionState } from "../../../shared/ExtensionMessage" -import { setTtsEnabled } from "../../../utils/tts" +import type { ProviderSettingsEntry, ClineMessage } from "@roo-code/types" + +import { ExtensionMessage, ExtensionState } from "../../../shared/ExtensionMessage" import { defaultModeSlug } from "../../../shared/modes" import { experimentDefault } from "../../../shared/experiments" +import { setTtsEnabled } from "../../../utils/tts" import { ContextProxy } from "../../config/ContextProxy" import { Task, TaskOptions } from "../../task/Task" +import { ClineProvider } from "../ClineProvider" + // Mock setup must come before imports jest.mock("../../prompts/sections/custom-instructions") diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index c8fd3608e4..0acae75884 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -3,9 +3,11 @@ import fs from "fs/promises" import pWaitFor from "p-wait-for" import * as vscode from "vscode" +import type { Language, ProviderSettings, GlobalState } from "@roo-code/types" + import { ClineProvider } from "./ClineProvider" -import { Language, ProviderSettings, GlobalState, Package } from "../../schemas" import { changeLanguage, t } from "../../i18n" +import { Package } from "../../shared/package" import { RouterName, toRouterName, ModelRecord } from "../../shared/api" import { supportPrompt } from "../../shared/support-prompt" import { checkoutDiffPayloadSchema, checkoutRestorePayloadSchema, WebviewMessage } from "../../shared/WebviewMessage" diff --git a/src/esbuild.mjs b/src/esbuild.mjs index 67753c5b8a..d8c96b4ede 100644 --- a/src/esbuild.mjs +++ b/src/esbuild.mjs @@ -1,4 +1,5 @@ import * as esbuild from "esbuild" +import * as fs from "fs" import * as path from "path" import { fileURLToPath } from "url" import process from "node:process" @@ -10,6 +11,7 @@ const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) async function main() { + const name = "extension" const production = process.argv.includes("--production") const watch = process.argv.includes("--watch") const minify = production @@ -32,12 +34,17 @@ async function main() { const buildDir = __dirname const distDir = path.join(buildDir, "dist") + if (fs.existsSync(distDir)) { + console.log(`[${name}] Cleaning dist directory: ${distDir}`) + fs.rmSync(distDir, { recursive: true, force: true }) + } + /** * @type {import('esbuild').Plugin[]} */ const plugins = [ { - name: "copy-files", + name: "copyFiles", setup(build) { build.onEnd(() => { copyPaths( @@ -55,13 +62,13 @@ async function main() { }, }, { - name: "copy-wasms", + name: "copyWasms", setup(build) { build.onEnd(() => copyWasms(srcDir, distDir)) }, }, { - name: "copy-locales", + name: "copyLocales", setup(build) { build.onEnd(() => copyLocales(srcDir, distDir)) }, @@ -91,6 +98,9 @@ async function main() { entryPoints: ["extension.ts"], outfile: "dist/extension.js", external: ["vscode"], + alias: { + "@roo-code/types": path.resolve(__dirname, "../packages/types/dist/index.js"), + }, } /** @@ -100,6 +110,9 @@ async function main() { ...buildOptions, entryPoints: ["workers/countTokens.ts"], outdir: "dist/workers", + alias: { + "@roo-code/types": path.resolve(__dirname, "../packages/types/dist/index.js"), + }, } const [extensionCtx, workerCtx] = await Promise.all([ diff --git a/src/exports/log.ts b/src/exports/log.ts deleted file mode 100644 index 1d77172fd1..0000000000 --- a/src/exports/log.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as vscode from "vscode" - -export function outputChannelLog(outputChannel: vscode.OutputChannel, ...args: unknown[]) { - for (const arg of args) { - if (arg === null) { - outputChannel.appendLine("null") - } else if (arg === undefined) { - outputChannel.appendLine("undefined") - } else if (typeof arg === "string") { - outputChannel.appendLine(arg) - } else if (arg instanceof Error) { - outputChannel.appendLine(`Error: ${arg.message}\n${arg.stack || ""}`) - } else { - try { - outputChannel.appendLine( - JSON.stringify( - arg, - (key, value) => { - if (typeof value === "bigint") return `BigInt(${value})` - if (typeof value === "function") return `Function: ${value.name || "anonymous"}` - if (typeof value === "symbol") return value.toString() - return value - }, - 2, - ), - ) - } catch (error) { - outputChannel.appendLine(`[Non-serializable object: ${Object.prototype.toString.call(arg)}]`) - } - } - } -} diff --git a/src/exports/roo-code.d.ts b/src/exports/roo-code.d.ts deleted file mode 100644 index 904eba8530..0000000000 --- a/src/exports/roo-code.d.ts +++ /dev/null @@ -1,1889 +0,0 @@ -import { EventEmitter } from "events" -import { Socket } from "node:net" - -type GlobalSettings = { - currentApiConfigName?: string | undefined - listApiConfigMeta?: - | { - id: string - name: string - apiProvider?: - | ( - | "anthropic" - | "glama" - | "openrouter" - | "bedrock" - | "vertex" - | "openai" - | "ollama" - | "vscode-lm" - | "lmstudio" - | "gemini" - | "openai-native" - | "mistral" - | "deepseek" - | "unbound" - | "requesty" - | "human-relay" - | "fake-ai" - | "xai" - | "groq" - | "chutes" - | "litellm" - ) - | undefined - }[] - | undefined - pinnedApiConfigs?: - | { - [x: string]: boolean - } - | undefined - lastShownAnnouncementId?: string | undefined - customInstructions?: string | undefined - taskHistory?: - | { - id: string - number: number - ts: number - task: string - tokensIn: number - tokensOut: number - cacheWrites?: number | undefined - cacheReads?: number | undefined - totalCost: number - size?: number | undefined - workspace?: string | undefined - }[] - | undefined - condensingApiConfigId?: string | undefined - customCondensingPrompt?: string | undefined - autoApprovalEnabled?: boolean | undefined - alwaysAllowReadOnly?: boolean | undefined - alwaysAllowReadOnlyOutsideWorkspace?: boolean | undefined - codebaseIndexModels?: - | { - openai?: - | { - [x: string]: { - dimension: number - } - } - | undefined - ollama?: - | { - [x: string]: { - dimension: number - } - } - | undefined - } - | undefined - codebaseIndexConfig?: - | { - codebaseIndexEnabled?: boolean | undefined - codebaseIndexQdrantUrl?: string | undefined - codebaseIndexEmbedderProvider?: ("openai" | "ollama") | undefined - codebaseIndexEmbedderBaseUrl?: string | undefined - codebaseIndexEmbedderModelId?: string | undefined - } - | undefined - alwaysAllowWrite?: boolean | undefined - alwaysAllowWriteOutsideWorkspace?: boolean | undefined - writeDelayMs?: number | undefined - alwaysAllowBrowser?: boolean | undefined - alwaysApproveResubmit?: boolean | undefined - requestDelaySeconds?: number | undefined - alwaysAllowMcp?: boolean | undefined - alwaysAllowModeSwitch?: boolean | undefined - alwaysAllowSubtasks?: boolean | undefined - alwaysAllowExecute?: boolean | undefined - allowedCommands?: string[] | undefined - allowedMaxRequests?: (number | null) | undefined - autoCondenseContextPercent?: number | undefined - browserToolEnabled?: boolean | undefined - browserViewportSize?: string | undefined - screenshotQuality?: number | undefined - remoteBrowserEnabled?: boolean | undefined - remoteBrowserHost?: string | undefined - cachedChromeHostUrl?: string | undefined - enableCheckpoints?: boolean | undefined - ttsEnabled?: boolean | undefined - ttsSpeed?: number | undefined - soundEnabled?: boolean | undefined - soundVolume?: number | undefined - maxOpenTabsContext?: number | undefined - maxWorkspaceFiles?: number | undefined - showRooIgnoredFiles?: boolean | undefined - maxReadFileLine?: number | undefined - terminalOutputLineLimit?: number | undefined - terminalShellIntegrationTimeout?: number | undefined - terminalShellIntegrationDisabled?: boolean | undefined - terminalCommandDelay?: number | undefined - terminalPowershellCounter?: boolean | undefined - terminalZshClearEolMark?: boolean | undefined - terminalZshOhMy?: boolean | undefined - terminalZshP10k?: boolean | undefined - terminalZdotdir?: boolean | undefined - terminalCompressProgressBar?: boolean | undefined - rateLimitSeconds?: number | undefined - diffEnabled?: boolean | undefined - fuzzyMatchThreshold?: number | undefined - experiments?: - | { - autoCondenseContext: boolean - powerSteering: boolean - } - | undefined - language?: - | ( - | "ca" - | "de" - | "en" - | "es" - | "fr" - | "hi" - | "it" - | "ja" - | "ko" - | "nl" - | "pl" - | "pt-BR" - | "ru" - | "tr" - | "vi" - | "zh-CN" - | "zh-TW" - ) - | undefined - telemetrySetting?: ("unset" | "enabled" | "disabled") | undefined - mcpEnabled?: boolean | undefined - enableMcpServerCreation?: boolean | undefined - mode?: string | undefined - modeApiConfigs?: - | { - [x: string]: string - } - | undefined - customModes?: - | { - slug: string - name: string - roleDefinition: string - whenToUse?: string | undefined - customInstructions?: string | undefined - groups: ( - | ("read" | "edit" | "browser" | "command" | "mcp" | "modes") - | [ - "read" | "edit" | "browser" | "command" | "mcp" | "modes", - { - fileRegex?: string | undefined - description?: string | undefined - }, - ] - )[] - source?: ("global" | "project") | undefined - }[] - | undefined - customModePrompts?: - | { - [x: string]: - | { - roleDefinition?: string | undefined - whenToUse?: string | undefined - customInstructions?: string | undefined - } - | undefined - } - | undefined - customSupportPrompts?: - | { - [x: string]: string | undefined - } - | undefined - enhancementApiConfigId?: string | undefined - historyPreviewCollapsed?: boolean | undefined -} - -type ProviderName = - | "anthropic" - | "glama" - | "openrouter" - | "bedrock" - | "vertex" - | "openai" - | "ollama" - | "vscode-lm" - | "lmstudio" - | "gemini" - | "openai-native" - | "mistral" - | "deepseek" - | "unbound" - | "requesty" - | "human-relay" - | "fake-ai" - | "xai" - | "groq" - | "chutes" - | "litellm" - -type ProviderSettings = { - apiProvider?: - | ( - | "anthropic" - | "glama" - | "openrouter" - | "bedrock" - | "vertex" - | "openai" - | "ollama" - | "vscode-lm" - | "lmstudio" - | "gemini" - | "openai-native" - | "mistral" - | "deepseek" - | "unbound" - | "requesty" - | "human-relay" - | "fake-ai" - | "xai" - | "groq" - | "chutes" - | "litellm" - ) - | undefined - includeMaxTokens?: boolean | undefined - diffEnabled?: boolean | undefined - fuzzyMatchThreshold?: number | undefined - modelTemperature?: (number | null) | undefined - rateLimitSeconds?: number | undefined - enableReasoningEffort?: boolean | undefined - reasoningEffort?: ("low" | "medium" | "high") | undefined - modelMaxTokens?: number | undefined - modelMaxThinkingTokens?: number | undefined - apiModelId?: string | undefined - apiKey?: string | undefined - anthropicBaseUrl?: string | undefined - anthropicUseAuthToken?: boolean | undefined - glamaModelId?: string | undefined - glamaApiKey?: string | undefined - openRouterApiKey?: string | undefined - openRouterModelId?: string | undefined - openRouterBaseUrl?: string | undefined - openRouterSpecificProvider?: string | undefined - openRouterUseMiddleOutTransform?: boolean | undefined - awsAccessKey?: string | undefined - awsSecretKey?: string | undefined - awsSessionToken?: string | undefined - awsRegion?: string | undefined - awsUseCrossRegionInference?: boolean | undefined - awsUsePromptCache?: boolean | undefined - awsProfile?: string | undefined - awsUseProfile?: boolean | undefined - awsCustomArn?: string | undefined - vertexKeyFile?: string | undefined - vertexJsonCredentials?: string | undefined - vertexProjectId?: string | undefined - vertexRegion?: string | undefined - openAiBaseUrl?: string | undefined - openAiApiKey?: string | undefined - openAiLegacyFormat?: boolean | undefined - openAiR1FormatEnabled?: boolean | undefined - openAiModelId?: string | undefined - openAiCustomModelInfo?: - | ({ - maxTokens?: (number | null) | undefined - maxThinkingTokens?: (number | null) | undefined - contextWindow: number - supportsImages?: boolean | undefined - supportsComputerUse?: boolean | undefined - supportsPromptCache: boolean - supportsReasoningBudget?: boolean | undefined - requiredReasoningBudget?: boolean | undefined - supportsReasoningEffort?: boolean | undefined - supportedParameters?: ("max_tokens" | "temperature" | "reasoning" | "include_reasoning")[] | undefined - inputPrice?: number | undefined - outputPrice?: number | undefined - cacheWritesPrice?: number | undefined - cacheReadsPrice?: number | undefined - description?: string | undefined - reasoningEffort?: ("low" | "medium" | "high") | undefined - minTokensPerCachePoint?: number | undefined - maxCachePoints?: number | undefined - cachableFields?: string[] | undefined - tiers?: - | { - contextWindow: number - inputPrice?: number | undefined - outputPrice?: number | undefined - cacheWritesPrice?: number | undefined - cacheReadsPrice?: number | undefined - }[] - | undefined - } | null) - | undefined - openAiUseAzure?: boolean | undefined - azureApiVersion?: string | undefined - openAiStreamingEnabled?: boolean | undefined - openAiHostHeader?: string | undefined - openAiHeaders?: - | { - [x: string]: string - } - | undefined - ollamaModelId?: string | undefined - ollamaBaseUrl?: string | undefined - vsCodeLmModelSelector?: - | { - vendor?: string | undefined - family?: string | undefined - version?: string | undefined - id?: string | undefined - } - | undefined - lmStudioModelId?: string | undefined - lmStudioBaseUrl?: string | undefined - lmStudioDraftModelId?: string | undefined - lmStudioSpeculativeDecodingEnabled?: boolean | undefined - geminiApiKey?: string | undefined - googleGeminiBaseUrl?: string | undefined - openAiNativeApiKey?: string | undefined - openAiNativeBaseUrl?: string | undefined - mistralApiKey?: string | undefined - mistralCodestralUrl?: string | undefined - deepSeekBaseUrl?: string | undefined - deepSeekApiKey?: string | undefined - unboundApiKey?: string | undefined - unboundModelId?: string | undefined - requestyApiKey?: string | undefined - requestyModelId?: string | undefined - fakeAi?: unknown | undefined - xaiApiKey?: string | undefined - groqApiKey?: string | undefined - chutesApiKey?: string | undefined - litellmBaseUrl?: string | undefined - litellmApiKey?: string | undefined - litellmModelId?: string | undefined - codeIndexOpenAiKey?: string | undefined - codeIndexQdrantApiKey?: string | undefined -} - -type ProviderSettingsEntry = { - id: string - name: string - apiProvider?: - | ( - | "anthropic" - | "glama" - | "openrouter" - | "bedrock" - | "vertex" - | "openai" - | "ollama" - | "vscode-lm" - | "lmstudio" - | "gemini" - | "openai-native" - | "mistral" - | "deepseek" - | "unbound" - | "requesty" - | "human-relay" - | "fake-ai" - | "xai" - | "groq" - | "chutes" - | "litellm" - ) - | undefined -} - -type ClineMessage = { - ts: number - type: "ask" | "say" - ask?: - | ( - | "followup" - | "command" - | "command_output" - | "completion_result" - | "tool" - | "api_req_failed" - | "resume_task" - | "resume_completed_task" - | "mistake_limit_reached" - | "browser_action_launch" - | "use_mcp_server" - | "auto_approval_max_req_reached" - ) - | undefined - say?: - | ( - | "error" - | "api_req_started" - | "api_req_finished" - | "api_req_retried" - | "api_req_retry_delayed" - | "api_req_deleted" - | "text" - | "reasoning" - | "completion_result" - | "user_feedback" - | "user_feedback_diff" - | "command_output" - | "shell_integration_warning" - | "browser_action" - | "browser_action_result" - | "mcp_server_request_started" - | "mcp_server_response" - | "subtask_result" - | "checkpoint_saved" - | "rooignore_error" - | "diff_error" - | "condense_context" - | "codebase_search_result" - ) - | undefined - text?: string | undefined - images?: string[] | undefined - partial?: boolean | undefined - reasoning?: string | undefined - conversationHistoryIndex?: number | undefined - checkpoint?: - | { - [x: string]: unknown - } - | undefined - progressStatus?: - | { - icon?: string | undefined - text?: string | undefined - } - | undefined - contextCondense?: - | { - cost: number - prevContextTokens: number - newContextTokens: number - summary: string - } - | undefined -} - -type TokenUsage = { - totalTokensIn: number - totalTokensOut: number - totalCacheWrites?: number | undefined - totalCacheReads?: number | undefined - totalCost: number - contextTokens: number -} - -type RooCodeEvents = { - message: [ - { - taskId: string - action: "created" | "updated" - message: { - ts: number - type: "ask" | "say" - ask?: - | ( - | "followup" - | "command" - | "command_output" - | "completion_result" - | "tool" - | "api_req_failed" - | "resume_task" - | "resume_completed_task" - | "mistake_limit_reached" - | "browser_action_launch" - | "use_mcp_server" - | "auto_approval_max_req_reached" - ) - | undefined - say?: - | ( - | "error" - | "api_req_started" - | "api_req_finished" - | "api_req_retried" - | "api_req_retry_delayed" - | "api_req_deleted" - | "text" - | "reasoning" - | "completion_result" - | "user_feedback" - | "user_feedback_diff" - | "command_output" - | "shell_integration_warning" - | "browser_action" - | "browser_action_result" - | "mcp_server_request_started" - | "mcp_server_response" - | "subtask_result" - | "checkpoint_saved" - | "rooignore_error" - | "diff_error" - | "condense_context" - | "codebase_search_result" - ) - | undefined - text?: string | undefined - images?: string[] | undefined - partial?: boolean | undefined - reasoning?: string | undefined - conversationHistoryIndex?: number | undefined - checkpoint?: - | { - [x: string]: unknown - } - | undefined - progressStatus?: - | { - icon?: string | undefined - text?: string | undefined - } - | undefined - contextCondense?: - | { - cost: number - prevContextTokens: number - newContextTokens: number - summary: string - } - | undefined - } - }, - ] - taskCreated: [string] - taskStarted: [string] - taskModeSwitched: [string, string] - taskPaused: [string] - taskUnpaused: [string] - taskAskResponded: [string] - taskAborted: [string] - taskSpawned: [string, string] - taskCompleted: [ - string, - { - totalTokensIn: number - totalTokensOut: number - totalCacheWrites?: number | undefined - totalCacheReads?: number | undefined - totalCost: number - contextTokens: number - }, - { - [x: string]: { - attempts: number - failures: number - } - }, - ] - taskTokenUsageUpdated: [ - string, - { - totalTokensIn: number - totalTokensOut: number - totalCacheWrites?: number | undefined - totalCacheReads?: number | undefined - totalCost: number - contextTokens: number - }, - ] - taskToolFailed: [ - string, - ( - | "execute_command" - | "read_file" - | "write_to_file" - | "apply_diff" - | "insert_content" - | "search_and_replace" - | "search_files" - | "list_files" - | "list_code_definition_names" - | "browser_action" - | "use_mcp_tool" - | "access_mcp_resource" - | "ask_followup_question" - | "attempt_completion" - | "switch_mode" - | "new_task" - | "fetch_instructions" - | "codebase_search" - ), - string, - ] -} - -type IpcMessage = - | { - type: "Ack" - origin: "server" - data: { - clientId: string - pid: number - ppid: number - } - } - | { - type: "TaskCommand" - origin: "client" - clientId: string - data: - | { - commandName: "StartNewTask" - data: { - configuration: { - apiProvider?: - | ( - | "anthropic" - | "glama" - | "openrouter" - | "bedrock" - | "vertex" - | "openai" - | "ollama" - | "vscode-lm" - | "lmstudio" - | "gemini" - | "openai-native" - | "mistral" - | "deepseek" - | "unbound" - | "requesty" - | "human-relay" - | "fake-ai" - | "xai" - | "groq" - | "chutes" - | "litellm" - ) - | undefined - includeMaxTokens?: boolean | undefined - diffEnabled?: boolean | undefined - fuzzyMatchThreshold?: number | undefined - modelTemperature?: (number | null) | undefined - rateLimitSeconds?: number | undefined - enableReasoningEffort?: boolean | undefined - reasoningEffort?: ("low" | "medium" | "high") | undefined - modelMaxTokens?: number | undefined - modelMaxThinkingTokens?: number | undefined - apiModelId?: string | undefined - apiKey?: string | undefined - anthropicBaseUrl?: string | undefined - anthropicUseAuthToken?: boolean | undefined - glamaModelId?: string | undefined - glamaApiKey?: string | undefined - openRouterApiKey?: string | undefined - openRouterModelId?: string | undefined - openRouterBaseUrl?: string | undefined - openRouterSpecificProvider?: string | undefined - openRouterUseMiddleOutTransform?: boolean | undefined - awsAccessKey?: string | undefined - awsSecretKey?: string | undefined - awsSessionToken?: string | undefined - awsRegion?: string | undefined - awsUseCrossRegionInference?: boolean | undefined - awsUsePromptCache?: boolean | undefined - awsProfile?: string | undefined - awsUseProfile?: boolean | undefined - awsCustomArn?: string | undefined - vertexKeyFile?: string | undefined - vertexJsonCredentials?: string | undefined - vertexProjectId?: string | undefined - vertexRegion?: string | undefined - openAiBaseUrl?: string | undefined - openAiApiKey?: string | undefined - openAiLegacyFormat?: boolean | undefined - openAiR1FormatEnabled?: boolean | undefined - openAiModelId?: string | undefined - openAiCustomModelInfo?: - | ({ - maxTokens?: (number | null) | undefined - maxThinkingTokens?: (number | null) | undefined - contextWindow: number - supportsImages?: boolean | undefined - supportsComputerUse?: boolean | undefined - supportsPromptCache: boolean - supportsReasoningBudget?: boolean | undefined - requiredReasoningBudget?: boolean | undefined - supportsReasoningEffort?: boolean | undefined - supportedParameters?: - | ("max_tokens" | "temperature" | "reasoning" | "include_reasoning")[] - | undefined - inputPrice?: number | undefined - outputPrice?: number | undefined - cacheWritesPrice?: number | undefined - cacheReadsPrice?: number | undefined - description?: string | undefined - reasoningEffort?: ("low" | "medium" | "high") | undefined - minTokensPerCachePoint?: number | undefined - maxCachePoints?: number | undefined - cachableFields?: string[] | undefined - tiers?: - | { - contextWindow: number - inputPrice?: number | undefined - outputPrice?: number | undefined - cacheWritesPrice?: number | undefined - cacheReadsPrice?: number | undefined - }[] - | undefined - } | null) - | undefined - openAiUseAzure?: boolean | undefined - azureApiVersion?: string | undefined - openAiStreamingEnabled?: boolean | undefined - openAiHostHeader?: string | undefined - openAiHeaders?: - | { - [x: string]: string - } - | undefined - ollamaModelId?: string | undefined - ollamaBaseUrl?: string | undefined - vsCodeLmModelSelector?: - | { - vendor?: string | undefined - family?: string | undefined - version?: string | undefined - id?: string | undefined - } - | undefined - lmStudioModelId?: string | undefined - lmStudioBaseUrl?: string | undefined - lmStudioDraftModelId?: string | undefined - lmStudioSpeculativeDecodingEnabled?: boolean | undefined - geminiApiKey?: string | undefined - googleGeminiBaseUrl?: string | undefined - openAiNativeApiKey?: string | undefined - openAiNativeBaseUrl?: string | undefined - mistralApiKey?: string | undefined - mistralCodestralUrl?: string | undefined - deepSeekBaseUrl?: string | undefined - deepSeekApiKey?: string | undefined - unboundApiKey?: string | undefined - unboundModelId?: string | undefined - requestyApiKey?: string | undefined - requestyModelId?: string | undefined - fakeAi?: unknown | undefined - xaiApiKey?: string | undefined - groqApiKey?: string | undefined - chutesApiKey?: string | undefined - litellmBaseUrl?: string | undefined - litellmApiKey?: string | undefined - litellmModelId?: string | undefined - codeIndexOpenAiKey?: string | undefined - codeIndexQdrantApiKey?: string | undefined - currentApiConfigName?: string | undefined - listApiConfigMeta?: - | { - id: string - name: string - apiProvider?: - | ( - | "anthropic" - | "glama" - | "openrouter" - | "bedrock" - | "vertex" - | "openai" - | "ollama" - | "vscode-lm" - | "lmstudio" - | "gemini" - | "openai-native" - | "mistral" - | "deepseek" - | "unbound" - | "requesty" - | "human-relay" - | "fake-ai" - | "xai" - | "groq" - | "chutes" - | "litellm" - ) - | undefined - }[] - | undefined - pinnedApiConfigs?: - | { - [x: string]: boolean - } - | undefined - lastShownAnnouncementId?: string | undefined - customInstructions?: string | undefined - taskHistory?: - | { - id: string - number: number - ts: number - task: string - tokensIn: number - tokensOut: number - cacheWrites?: number | undefined - cacheReads?: number | undefined - totalCost: number - size?: number | undefined - workspace?: string | undefined - }[] - | undefined - condensingApiConfigId?: string | undefined - customCondensingPrompt?: string | undefined - autoApprovalEnabled?: boolean | undefined - alwaysAllowReadOnly?: boolean | undefined - alwaysAllowReadOnlyOutsideWorkspace?: boolean | undefined - codebaseIndexModels?: - | { - openai?: - | { - [x: string]: { - dimension: number - } - } - | undefined - ollama?: - | { - [x: string]: { - dimension: number - } - } - | undefined - } - | undefined - codebaseIndexConfig?: - | { - codebaseIndexEnabled?: boolean | undefined - codebaseIndexQdrantUrl?: string | undefined - codebaseIndexEmbedderProvider?: ("openai" | "ollama") | undefined - codebaseIndexEmbedderBaseUrl?: string | undefined - codebaseIndexEmbedderModelId?: string | undefined - } - | undefined - alwaysAllowWrite?: boolean | undefined - alwaysAllowWriteOutsideWorkspace?: boolean | undefined - writeDelayMs?: number | undefined - alwaysAllowBrowser?: boolean | undefined - alwaysApproveResubmit?: boolean | undefined - requestDelaySeconds?: number | undefined - alwaysAllowMcp?: boolean | undefined - alwaysAllowModeSwitch?: boolean | undefined - alwaysAllowSubtasks?: boolean | undefined - alwaysAllowExecute?: boolean | undefined - allowedCommands?: string[] | undefined - allowedMaxRequests?: (number | null) | undefined - autoCondenseContextPercent?: number | undefined - browserToolEnabled?: boolean | undefined - browserViewportSize?: string | undefined - screenshotQuality?: number | undefined - remoteBrowserEnabled?: boolean | undefined - remoteBrowserHost?: string | undefined - cachedChromeHostUrl?: string | undefined - enableCheckpoints?: boolean | undefined - ttsEnabled?: boolean | undefined - ttsSpeed?: number | undefined - soundEnabled?: boolean | undefined - soundVolume?: number | undefined - maxOpenTabsContext?: number | undefined - maxWorkspaceFiles?: number | undefined - showRooIgnoredFiles?: boolean | undefined - maxReadFileLine?: number | undefined - terminalOutputLineLimit?: number | undefined - terminalShellIntegrationTimeout?: number | undefined - terminalShellIntegrationDisabled?: boolean | undefined - terminalCommandDelay?: number | undefined - terminalPowershellCounter?: boolean | undefined - terminalZshClearEolMark?: boolean | undefined - terminalZshOhMy?: boolean | undefined - terminalZshP10k?: boolean | undefined - terminalZdotdir?: boolean | undefined - terminalCompressProgressBar?: boolean | undefined - experiments?: - | { - autoCondenseContext: boolean - powerSteering: boolean - } - | undefined - language?: - | ( - | "ca" - | "de" - | "en" - | "es" - | "fr" - | "hi" - | "it" - | "ja" - | "ko" - | "nl" - | "pl" - | "pt-BR" - | "ru" - | "tr" - | "vi" - | "zh-CN" - | "zh-TW" - ) - | undefined - telemetrySetting?: ("unset" | "enabled" | "disabled") | undefined - mcpEnabled?: boolean | undefined - enableMcpServerCreation?: boolean | undefined - mode?: string | undefined - modeApiConfigs?: - | { - [x: string]: string - } - | undefined - customModes?: - | { - slug: string - name: string - roleDefinition: string - whenToUse?: string | undefined - customInstructions?: string | undefined - groups: ( - | ("read" | "edit" | "browser" | "command" | "mcp" | "modes") - | [ - "read" | "edit" | "browser" | "command" | "mcp" | "modes", - { - fileRegex?: string | undefined - description?: string | undefined - }, - ] - )[] - source?: ("global" | "project") | undefined - }[] - | undefined - customModePrompts?: - | { - [x: string]: - | { - roleDefinition?: string | undefined - whenToUse?: string | undefined - customInstructions?: string | undefined - } - | undefined - } - | undefined - customSupportPrompts?: - | { - [x: string]: string | undefined - } - | undefined - enhancementApiConfigId?: string | undefined - historyPreviewCollapsed?: boolean | undefined - } - text: string - images?: string[] | undefined - newTab?: boolean | undefined - } - } - | { - commandName: "CancelTask" - data: string - } - | { - commandName: "CloseTask" - data: string - } - } - | { - type: "TaskEvent" - origin: "server" - relayClientId?: string | undefined - data: - | { - eventName: "message" - payload: [ - { - taskId: string - action: "created" | "updated" - message: { - ts: number - type: "ask" | "say" - ask?: - | ( - | "followup" - | "command" - | "command_output" - | "completion_result" - | "tool" - | "api_req_failed" - | "resume_task" - | "resume_completed_task" - | "mistake_limit_reached" - | "browser_action_launch" - | "use_mcp_server" - | "auto_approval_max_req_reached" - ) - | undefined - say?: - | ( - | "error" - | "api_req_started" - | "api_req_finished" - | "api_req_retried" - | "api_req_retry_delayed" - | "api_req_deleted" - | "text" - | "reasoning" - | "completion_result" - | "user_feedback" - | "user_feedback_diff" - | "command_output" - | "shell_integration_warning" - | "browser_action" - | "browser_action_result" - | "mcp_server_request_started" - | "mcp_server_response" - | "subtask_result" - | "checkpoint_saved" - | "rooignore_error" - | "diff_error" - | "condense_context" - | "codebase_search_result" - ) - | undefined - text?: string | undefined - images?: string[] | undefined - partial?: boolean | undefined - reasoning?: string | undefined - conversationHistoryIndex?: number | undefined - checkpoint?: - | { - [x: string]: unknown - } - | undefined - progressStatus?: - | { - icon?: string | undefined - text?: string | undefined - } - | undefined - contextCondense?: - | { - cost: number - prevContextTokens: number - newContextTokens: number - summary: string - } - | undefined - } - }, - ] - } - | { - eventName: "taskCreated" - payload: [string] - } - | { - eventName: "taskStarted" - payload: [string] - } - | { - eventName: "taskModeSwitched" - payload: [string, string] - } - | { - eventName: "taskPaused" - payload: [string] - } - | { - eventName: "taskUnpaused" - payload: [string] - } - | { - eventName: "taskAskResponded" - payload: [string] - } - | { - eventName: "taskAborted" - payload: [string] - } - | { - eventName: "taskSpawned" - payload: [string, string] - } - | { - eventName: "taskCompleted" - payload: [ - string, - { - totalTokensIn: number - totalTokensOut: number - totalCacheWrites?: number | undefined - totalCacheReads?: number | undefined - totalCost: number - contextTokens: number - }, - { - [x: string]: { - attempts: number - failures: number - } - }, - ] - } - | { - eventName: "taskTokenUsageUpdated" - payload: [ - string, - { - totalTokensIn: number - totalTokensOut: number - totalCacheWrites?: number | undefined - totalCacheReads?: number | undefined - totalCost: number - contextTokens: number - }, - ] - } - } - -type TaskCommand = - | { - commandName: "StartNewTask" - data: { - configuration: { - apiProvider?: - | ( - | "anthropic" - | "glama" - | "openrouter" - | "bedrock" - | "vertex" - | "openai" - | "ollama" - | "vscode-lm" - | "lmstudio" - | "gemini" - | "openai-native" - | "mistral" - | "deepseek" - | "unbound" - | "requesty" - | "human-relay" - | "fake-ai" - | "xai" - | "groq" - | "chutes" - | "litellm" - ) - | undefined - includeMaxTokens?: boolean | undefined - diffEnabled?: boolean | undefined - fuzzyMatchThreshold?: number | undefined - modelTemperature?: (number | null) | undefined - rateLimitSeconds?: number | undefined - enableReasoningEffort?: boolean | undefined - reasoningEffort?: ("low" | "medium" | "high") | undefined - modelMaxTokens?: number | undefined - modelMaxThinkingTokens?: number | undefined - apiModelId?: string | undefined - apiKey?: string | undefined - anthropicBaseUrl?: string | undefined - anthropicUseAuthToken?: boolean | undefined - glamaModelId?: string | undefined - glamaApiKey?: string | undefined - openRouterApiKey?: string | undefined - openRouterModelId?: string | undefined - openRouterBaseUrl?: string | undefined - openRouterSpecificProvider?: string | undefined - openRouterUseMiddleOutTransform?: boolean | undefined - awsAccessKey?: string | undefined - awsSecretKey?: string | undefined - awsSessionToken?: string | undefined - awsRegion?: string | undefined - awsUseCrossRegionInference?: boolean | undefined - awsUsePromptCache?: boolean | undefined - awsProfile?: string | undefined - awsUseProfile?: boolean | undefined - awsCustomArn?: string | undefined - vertexKeyFile?: string | undefined - vertexJsonCredentials?: string | undefined - vertexProjectId?: string | undefined - vertexRegion?: string | undefined - openAiBaseUrl?: string | undefined - openAiApiKey?: string | undefined - openAiLegacyFormat?: boolean | undefined - openAiR1FormatEnabled?: boolean | undefined - openAiModelId?: string | undefined - openAiCustomModelInfo?: - | ({ - maxTokens?: (number | null) | undefined - maxThinkingTokens?: (number | null) | undefined - contextWindow: number - supportsImages?: boolean | undefined - supportsComputerUse?: boolean | undefined - supportsPromptCache: boolean - supportsReasoningBudget?: boolean | undefined - requiredReasoningBudget?: boolean | undefined - supportsReasoningEffort?: boolean | undefined - supportedParameters?: - | ("max_tokens" | "temperature" | "reasoning" | "include_reasoning")[] - | undefined - inputPrice?: number | undefined - outputPrice?: number | undefined - cacheWritesPrice?: number | undefined - cacheReadsPrice?: number | undefined - description?: string | undefined - reasoningEffort?: ("low" | "medium" | "high") | undefined - minTokensPerCachePoint?: number | undefined - maxCachePoints?: number | undefined - cachableFields?: string[] | undefined - tiers?: - | { - contextWindow: number - inputPrice?: number | undefined - outputPrice?: number | undefined - cacheWritesPrice?: number | undefined - cacheReadsPrice?: number | undefined - }[] - | undefined - } | null) - | undefined - openAiUseAzure?: boolean | undefined - azureApiVersion?: string | undefined - openAiStreamingEnabled?: boolean | undefined - openAiHostHeader?: string | undefined - openAiHeaders?: - | { - [x: string]: string - } - | undefined - ollamaModelId?: string | undefined - ollamaBaseUrl?: string | undefined - vsCodeLmModelSelector?: - | { - vendor?: string | undefined - family?: string | undefined - version?: string | undefined - id?: string | undefined - } - | undefined - lmStudioModelId?: string | undefined - lmStudioBaseUrl?: string | undefined - lmStudioDraftModelId?: string | undefined - lmStudioSpeculativeDecodingEnabled?: boolean | undefined - geminiApiKey?: string | undefined - googleGeminiBaseUrl?: string | undefined - openAiNativeApiKey?: string | undefined - openAiNativeBaseUrl?: string | undefined - mistralApiKey?: string | undefined - mistralCodestralUrl?: string | undefined - deepSeekBaseUrl?: string | undefined - deepSeekApiKey?: string | undefined - unboundApiKey?: string | undefined - unboundModelId?: string | undefined - requestyApiKey?: string | undefined - requestyModelId?: string | undefined - fakeAi?: unknown | undefined - xaiApiKey?: string | undefined - groqApiKey?: string | undefined - chutesApiKey?: string | undefined - litellmBaseUrl?: string | undefined - litellmApiKey?: string | undefined - litellmModelId?: string | undefined - codeIndexOpenAiKey?: string | undefined - codeIndexQdrantApiKey?: string | undefined - currentApiConfigName?: string | undefined - listApiConfigMeta?: - | { - id: string - name: string - apiProvider?: - | ( - | "anthropic" - | "glama" - | "openrouter" - | "bedrock" - | "vertex" - | "openai" - | "ollama" - | "vscode-lm" - | "lmstudio" - | "gemini" - | "openai-native" - | "mistral" - | "deepseek" - | "unbound" - | "requesty" - | "human-relay" - | "fake-ai" - | "xai" - | "groq" - | "chutes" - | "litellm" - ) - | undefined - }[] - | undefined - pinnedApiConfigs?: - | { - [x: string]: boolean - } - | undefined - lastShownAnnouncementId?: string | undefined - customInstructions?: string | undefined - taskHistory?: - | { - id: string - number: number - ts: number - task: string - tokensIn: number - tokensOut: number - cacheWrites?: number | undefined - cacheReads?: number | undefined - totalCost: number - size?: number | undefined - workspace?: string | undefined - }[] - | undefined - condensingApiConfigId?: string | undefined - customCondensingPrompt?: string | undefined - autoApprovalEnabled?: boolean | undefined - alwaysAllowReadOnly?: boolean | undefined - alwaysAllowReadOnlyOutsideWorkspace?: boolean | undefined - codebaseIndexModels?: - | { - openai?: - | { - [x: string]: { - dimension: number - } - } - | undefined - ollama?: - | { - [x: string]: { - dimension: number - } - } - | undefined - } - | undefined - codebaseIndexConfig?: - | { - codebaseIndexEnabled?: boolean | undefined - codebaseIndexQdrantUrl?: string | undefined - codebaseIndexEmbedderProvider?: ("openai" | "ollama") | undefined - codebaseIndexEmbedderBaseUrl?: string | undefined - codebaseIndexEmbedderModelId?: string | undefined - } - | undefined - alwaysAllowWrite?: boolean | undefined - alwaysAllowWriteOutsideWorkspace?: boolean | undefined - writeDelayMs?: number | undefined - alwaysAllowBrowser?: boolean | undefined - alwaysApproveResubmit?: boolean | undefined - requestDelaySeconds?: number | undefined - alwaysAllowMcp?: boolean | undefined - alwaysAllowModeSwitch?: boolean | undefined - alwaysAllowSubtasks?: boolean | undefined - alwaysAllowExecute?: boolean | undefined - allowedCommands?: string[] | undefined - allowedMaxRequests?: (number | null) | undefined - autoCondenseContextPercent?: number | undefined - browserToolEnabled?: boolean | undefined - browserViewportSize?: string | undefined - screenshotQuality?: number | undefined - remoteBrowserEnabled?: boolean | undefined - remoteBrowserHost?: string | undefined - cachedChromeHostUrl?: string | undefined - enableCheckpoints?: boolean | undefined - ttsEnabled?: boolean | undefined - ttsSpeed?: number | undefined - soundEnabled?: boolean | undefined - soundVolume?: number | undefined - maxOpenTabsContext?: number | undefined - maxWorkspaceFiles?: number | undefined - showRooIgnoredFiles?: boolean | undefined - maxReadFileLine?: number | undefined - terminalOutputLineLimit?: number | undefined - terminalShellIntegrationTimeout?: number | undefined - terminalShellIntegrationDisabled?: boolean | undefined - terminalCommandDelay?: number | undefined - terminalPowershellCounter?: boolean | undefined - terminalZshClearEolMark?: boolean | undefined - terminalZshOhMy?: boolean | undefined - terminalZshP10k?: boolean | undefined - terminalZdotdir?: boolean | undefined - terminalCompressProgressBar?: boolean | undefined - experiments?: - | { - autoCondenseContext: boolean - powerSteering: boolean - } - | undefined - language?: - | ( - | "ca" - | "de" - | "en" - | "es" - | "fr" - | "hi" - | "it" - | "ja" - | "ko" - | "nl" - | "pl" - | "pt-BR" - | "ru" - | "tr" - | "vi" - | "zh-CN" - | "zh-TW" - ) - | undefined - telemetrySetting?: ("unset" | "enabled" | "disabled") | undefined - mcpEnabled?: boolean | undefined - enableMcpServerCreation?: boolean | undefined - mode?: string | undefined - modeApiConfigs?: - | { - [x: string]: string - } - | undefined - customModes?: - | { - slug: string - name: string - roleDefinition: string - whenToUse?: string | undefined - customInstructions?: string | undefined - groups: ( - | ("read" | "edit" | "browser" | "command" | "mcp" | "modes") - | [ - "read" | "edit" | "browser" | "command" | "mcp" | "modes", - { - fileRegex?: string | undefined - description?: string | undefined - }, - ] - )[] - source?: ("global" | "project") | undefined - }[] - | undefined - customModePrompts?: - | { - [x: string]: - | { - roleDefinition?: string | undefined - whenToUse?: string | undefined - customInstructions?: string | undefined - } - | undefined - } - | undefined - customSupportPrompts?: - | { - [x: string]: string | undefined - } - | undefined - enhancementApiConfigId?: string | undefined - historyPreviewCollapsed?: boolean | undefined - } - text: string - images?: string[] | undefined - newTab?: boolean | undefined - } - } - | { - commandName: "CancelTask" - data: string - } - | { - commandName: "CloseTask" - data: string - } - -type TaskEvent = - | { - eventName: "message" - payload: [ - { - taskId: string - action: "created" | "updated" - message: { - ts: number - type: "ask" | "say" - ask?: - | ( - | "followup" - | "command" - | "command_output" - | "completion_result" - | "tool" - | "api_req_failed" - | "resume_task" - | "resume_completed_task" - | "mistake_limit_reached" - | "browser_action_launch" - | "use_mcp_server" - | "auto_approval_max_req_reached" - ) - | undefined - say?: - | ( - | "error" - | "api_req_started" - | "api_req_finished" - | "api_req_retried" - | "api_req_retry_delayed" - | "api_req_deleted" - | "text" - | "reasoning" - | "completion_result" - | "user_feedback" - | "user_feedback_diff" - | "command_output" - | "shell_integration_warning" - | "browser_action" - | "browser_action_result" - | "mcp_server_request_started" - | "mcp_server_response" - | "subtask_result" - | "checkpoint_saved" - | "rooignore_error" - | "diff_error" - | "condense_context" - | "codebase_search_result" - ) - | undefined - text?: string | undefined - images?: string[] | undefined - partial?: boolean | undefined - reasoning?: string | undefined - conversationHistoryIndex?: number | undefined - checkpoint?: - | { - [x: string]: unknown - } - | undefined - progressStatus?: - | { - icon?: string | undefined - text?: string | undefined - } - | undefined - contextCondense?: - | { - cost: number - prevContextTokens: number - newContextTokens: number - summary: string - } - | undefined - } - }, - ] - } - | { - eventName: "taskCreated" - payload: [string] - } - | { - eventName: "taskStarted" - payload: [string] - } - | { - eventName: "taskModeSwitched" - payload: [string, string] - } - | { - eventName: "taskPaused" - payload: [string] - } - | { - eventName: "taskUnpaused" - payload: [string] - } - | { - eventName: "taskAskResponded" - payload: [string] - } - | { - eventName: "taskAborted" - payload: [string] - } - | { - eventName: "taskSpawned" - payload: [string, string] - } - | { - eventName: "taskCompleted" - payload: [ - string, - { - totalTokensIn: number - totalTokensOut: number - totalCacheWrites?: number | undefined - totalCacheReads?: number | undefined - totalCost: number - contextTokens: number - }, - { - [x: string]: { - attempts: number - failures: number - } - }, - ] - } - | { - eventName: "taskTokenUsageUpdated" - payload: [ - string, - { - totalTokensIn: number - totalTokensOut: number - totalCacheWrites?: number | undefined - totalCacheReads?: number | undefined - totalCost: number - contextTokens: number - }, - ] - } - -declare const Package: { - readonly publisher: string - readonly name: string - readonly version: string - readonly outputChannel: string - readonly sha: string | undefined -} -/** - * ProviderName - */ -declare const providerNames: readonly [ - "anthropic", - "glama", - "openrouter", - "bedrock", - "vertex", - "openai", - "ollama", - "vscode-lm", - "lmstudio", - "gemini", - "openai-native", - "mistral", - "deepseek", - "unbound", - "requesty", - "human-relay", - "fake-ai", - "xai", - "groq", - "chutes", - "litellm", -] -/** - * RooCodeEvent - */ -declare enum RooCodeEventName { - Message = "message", - TaskCreated = "taskCreated", - TaskStarted = "taskStarted", - TaskModeSwitched = "taskModeSwitched", - TaskPaused = "taskPaused", - TaskUnpaused = "taskUnpaused", - TaskAskResponded = "taskAskResponded", - TaskAborted = "taskAborted", - TaskSpawned = "taskSpawned", - TaskCompleted = "taskCompleted", - TaskTokenUsageUpdated = "taskTokenUsageUpdated", - TaskToolFailed = "taskToolFailed", -} -/** - * IpcMessage - */ -declare enum IpcMessageType { - Connect = "Connect", - Disconnect = "Disconnect", - Ack = "Ack", - TaskCommand = "TaskCommand", - TaskEvent = "TaskEvent", -} -declare enum IpcOrigin { - Client = "client", - Server = "server", -} - -/** - * RooCodeAPI - */ -type RooCodeSettings = GlobalSettings & ProviderSettings -interface RooCodeAPI extends EventEmitter { - /** - * Starts a new task with an optional initial message and images. - * @param task Optional initial task message. - * @param images Optional array of image data URIs (e.g., "data:image/webp;base64,..."). - * @returns The ID of the new task. - */ - startNewTask({ - configuration, - text, - images, - newTab, - }: { - configuration?: RooCodeSettings - text?: string - images?: string[] - newTab?: boolean - }): Promise - /** - * Resumes a task with the given ID. - * @param taskId The ID of the task to resume. - * @throws Error if the task is not found in the task history. - */ - resumeTask(taskId: string): Promise - /** - * Checks if a task with the given ID is in the task history. - * @param taskId The ID of the task to check. - * @returns True if the task is in the task history, false otherwise. - */ - isTaskInHistory(taskId: string): Promise - /** - * Returns the current task stack. - * @returns An array of task IDs. - */ - getCurrentTaskStack(): string[] - /** - * Clears the current task. - */ - clearCurrentTask(lastMessage?: string): Promise - /** - * Cancels the current task. - */ - cancelCurrentTask(): Promise - /** - * Sends a message to the current task. - * @param message Optional message to send. - * @param images Optional array of image data URIs (e.g., "data:image/webp;base64,..."). - */ - sendMessage(message?: string, images?: string[]): Promise - /** - * Simulates pressing the primary button in the chat interface. - */ - pressPrimaryButton(): Promise - /** - * Simulates pressing the secondary button in the chat interface. - */ - pressSecondaryButton(): Promise - /** - * Returns true if the API is ready to use. - */ - isReady(): boolean - /** - * Returns the current configuration. - * @returns The current configuration. - */ - getConfiguration(): RooCodeSettings - /** - * Sets the configuration for the current task. - * @param values An object containing key-value pairs to set. - */ - setConfiguration(values: RooCodeSettings): Promise - /** - * Returns a list of all configured profile names - * @returns Array of profile names - */ - getProfiles(): string[] - /** - * Returns the profile entry for a given name - * @param name The name of the profile - * @returns The profile entry, or undefined if the profile does not exist - */ - getProfileEntry(name: string): ProviderSettingsEntry | undefined - /** - * Creates a new API configuration profile - * @param name The name of the profile - * @param profile The profile to create; defaults to an empty object - * @param activate Whether to activate the profile after creation; defaults to true - * @returns The ID of the created profile - * @throws Error if the profile already exists - */ - createProfile(name: string, profile?: ProviderSettings, activate?: boolean): Promise - /** - * Updates an existing API configuration profile - * @param name The name of the profile - * @param profile The profile to update - * @param activate Whether to activate the profile after update; defaults to true - * @returns The ID of the updated profile - * @throws Error if the profile does not exist - */ - updateProfile(name: string, profile: ProviderSettings, activate?: boolean): Promise - /** - * Creates a new API configuration profile or updates an existing one - * @param name The name of the profile - * @param profile The profile to create or update; defaults to an empty object - * @param activate Whether to activate the profile after upsert; defaults to true - * @returns The ID of the upserted profile - */ - upsertProfile(name: string, profile: ProviderSettings, activate?: boolean): Promise - /** - * Deletes a profile by name - * @param name The name of the profile to delete - * @throws Error if the profile does not exist - */ - deleteProfile(name: string): Promise - /** - * Returns the name of the currently active profile - * @returns The profile name, or undefined if no profile is active - */ - getActiveProfile(): string | undefined - /** - * Changes the active API configuration profile - * @param name The name of the profile to activate - * @throws Error if the profile does not exist - */ - setActiveProfile(name: string): Promise -} -/** - * RooCodeIpcServer - */ -type IpcServerEvents = { - [IpcMessageType.Connect]: [clientId: string] - [IpcMessageType.Disconnect]: [clientId: string] - [IpcMessageType.TaskCommand]: [clientId: string, data: TaskCommand] - [IpcMessageType.TaskEvent]: [relayClientId: string | undefined, data: TaskEvent] -} -interface RooCodeIpcServer extends EventEmitter { - listen(): void - broadcast(message: IpcMessage): void - send(client: string | Socket, message: IpcMessage): void - get socketPath(): string - get isListening(): boolean -} - -export { - type ClineMessage, - type GlobalSettings, - type IpcMessage, - IpcMessageType, - IpcOrigin, - type IpcServerEvents, - Package, - type ProviderName, - type ProviderSettings, - type ProviderSettingsEntry, - type RooCodeAPI, - RooCodeEventName, - type RooCodeEvents, - type RooCodeIpcServer, - type RooCodeSettings, - type TaskCommand, - type TaskEvent, - type TokenUsage, - providerNames, -} diff --git a/src/exports/types.ts b/src/exports/types.ts deleted file mode 100644 index 6f4989df62..0000000000 --- a/src/exports/types.ts +++ /dev/null @@ -1,1675 +0,0 @@ -// This file is automatically generated by running `pnpm --filter roo-cline generate-types` -// Do not edit it directly. - -type GlobalSettings = { - currentApiConfigName?: string | undefined - listApiConfigMeta?: - | { - id: string - name: string - apiProvider?: - | ( - | "anthropic" - | "glama" - | "openrouter" - | "bedrock" - | "vertex" - | "openai" - | "ollama" - | "vscode-lm" - | "lmstudio" - | "gemini" - | "openai-native" - | "mistral" - | "deepseek" - | "unbound" - | "requesty" - | "human-relay" - | "fake-ai" - | "xai" - | "groq" - | "chutes" - | "litellm" - ) - | undefined - }[] - | undefined - pinnedApiConfigs?: - | { - [x: string]: boolean - } - | undefined - lastShownAnnouncementId?: string | undefined - customInstructions?: string | undefined - taskHistory?: - | { - id: string - number: number - ts: number - task: string - tokensIn: number - tokensOut: number - cacheWrites?: number | undefined - cacheReads?: number | undefined - totalCost: number - size?: number | undefined - workspace?: string | undefined - }[] - | undefined - condensingApiConfigId?: string | undefined - customCondensingPrompt?: string | undefined - autoApprovalEnabled?: boolean | undefined - alwaysAllowReadOnly?: boolean | undefined - alwaysAllowReadOnlyOutsideWorkspace?: boolean | undefined - codebaseIndexModels?: - | { - openai?: - | { - [x: string]: { - dimension: number - } - } - | undefined - ollama?: - | { - [x: string]: { - dimension: number - } - } - | undefined - } - | undefined - codebaseIndexConfig?: - | { - codebaseIndexEnabled?: boolean | undefined - codebaseIndexQdrantUrl?: string | undefined - codebaseIndexEmbedderProvider?: ("openai" | "ollama") | undefined - codebaseIndexEmbedderBaseUrl?: string | undefined - codebaseIndexEmbedderModelId?: string | undefined - } - | undefined - alwaysAllowWrite?: boolean | undefined - alwaysAllowWriteOutsideWorkspace?: boolean | undefined - writeDelayMs?: number | undefined - alwaysAllowBrowser?: boolean | undefined - alwaysApproveResubmit?: boolean | undefined - requestDelaySeconds?: number | undefined - alwaysAllowMcp?: boolean | undefined - alwaysAllowModeSwitch?: boolean | undefined - alwaysAllowSubtasks?: boolean | undefined - alwaysAllowExecute?: boolean | undefined - allowedCommands?: string[] | undefined - allowedMaxRequests?: (number | null) | undefined - autoCondenseContextPercent?: number | undefined - browserToolEnabled?: boolean | undefined - browserViewportSize?: string | undefined - screenshotQuality?: number | undefined - remoteBrowserEnabled?: boolean | undefined - remoteBrowserHost?: string | undefined - cachedChromeHostUrl?: string | undefined - enableCheckpoints?: boolean | undefined - ttsEnabled?: boolean | undefined - ttsSpeed?: number | undefined - soundEnabled?: boolean | undefined - soundVolume?: number | undefined - maxOpenTabsContext?: number | undefined - maxWorkspaceFiles?: number | undefined - showRooIgnoredFiles?: boolean | undefined - maxReadFileLine?: number | undefined - terminalOutputLineLimit?: number | undefined - terminalShellIntegrationTimeout?: number | undefined - terminalShellIntegrationDisabled?: boolean | undefined - terminalCommandDelay?: number | undefined - terminalPowershellCounter?: boolean | undefined - terminalZshClearEolMark?: boolean | undefined - terminalZshOhMy?: boolean | undefined - terminalZshP10k?: boolean | undefined - terminalZdotdir?: boolean | undefined - terminalCompressProgressBar?: boolean | undefined - rateLimitSeconds?: number | undefined - diffEnabled?: boolean | undefined - fuzzyMatchThreshold?: number | undefined - experiments?: - | { - autoCondenseContext: boolean - powerSteering: boolean - } - | undefined - language?: - | ( - | "ca" - | "de" - | "en" - | "es" - | "fr" - | "hi" - | "it" - | "ja" - | "ko" - | "nl" - | "pl" - | "pt-BR" - | "ru" - | "tr" - | "vi" - | "zh-CN" - | "zh-TW" - ) - | undefined - telemetrySetting?: ("unset" | "enabled" | "disabled") | undefined - mcpEnabled?: boolean | undefined - enableMcpServerCreation?: boolean | undefined - mode?: string | undefined - modeApiConfigs?: - | { - [x: string]: string - } - | undefined - customModes?: - | { - slug: string - name: string - roleDefinition: string - whenToUse?: string | undefined - customInstructions?: string | undefined - groups: ( - | ("read" | "edit" | "browser" | "command" | "mcp" | "modes") - | [ - "read" | "edit" | "browser" | "command" | "mcp" | "modes", - { - fileRegex?: string | undefined - description?: string | undefined - }, - ] - )[] - source?: ("global" | "project") | undefined - }[] - | undefined - customModePrompts?: - | { - [x: string]: - | { - roleDefinition?: string | undefined - whenToUse?: string | undefined - customInstructions?: string | undefined - } - | undefined - } - | undefined - customSupportPrompts?: - | { - [x: string]: string | undefined - } - | undefined - enhancementApiConfigId?: string | undefined - historyPreviewCollapsed?: boolean | undefined -} - -export type { GlobalSettings } - -type ProviderName = - | "anthropic" - | "glama" - | "openrouter" - | "bedrock" - | "vertex" - | "openai" - | "ollama" - | "vscode-lm" - | "lmstudio" - | "gemini" - | "openai-native" - | "mistral" - | "deepseek" - | "unbound" - | "requesty" - | "human-relay" - | "fake-ai" - | "xai" - | "groq" - | "chutes" - | "litellm" - -export type { ProviderName } - -type ProviderSettings = { - apiProvider?: - | ( - | "anthropic" - | "glama" - | "openrouter" - | "bedrock" - | "vertex" - | "openai" - | "ollama" - | "vscode-lm" - | "lmstudio" - | "gemini" - | "openai-native" - | "mistral" - | "deepseek" - | "unbound" - | "requesty" - | "human-relay" - | "fake-ai" - | "xai" - | "groq" - | "chutes" - | "litellm" - ) - | undefined - includeMaxTokens?: boolean | undefined - diffEnabled?: boolean | undefined - fuzzyMatchThreshold?: number | undefined - modelTemperature?: (number | null) | undefined - rateLimitSeconds?: number | undefined - enableReasoningEffort?: boolean | undefined - reasoningEffort?: ("low" | "medium" | "high") | undefined - modelMaxTokens?: number | undefined - modelMaxThinkingTokens?: number | undefined - apiModelId?: string | undefined - apiKey?: string | undefined - anthropicBaseUrl?: string | undefined - anthropicUseAuthToken?: boolean | undefined - glamaModelId?: string | undefined - glamaApiKey?: string | undefined - openRouterApiKey?: string | undefined - openRouterModelId?: string | undefined - openRouterBaseUrl?: string | undefined - openRouterSpecificProvider?: string | undefined - openRouterUseMiddleOutTransform?: boolean | undefined - awsAccessKey?: string | undefined - awsSecretKey?: string | undefined - awsSessionToken?: string | undefined - awsRegion?: string | undefined - awsUseCrossRegionInference?: boolean | undefined - awsUsePromptCache?: boolean | undefined - awsProfile?: string | undefined - awsUseProfile?: boolean | undefined - awsCustomArn?: string | undefined - vertexKeyFile?: string | undefined - vertexJsonCredentials?: string | undefined - vertexProjectId?: string | undefined - vertexRegion?: string | undefined - openAiBaseUrl?: string | undefined - openAiApiKey?: string | undefined - openAiLegacyFormat?: boolean | undefined - openAiR1FormatEnabled?: boolean | undefined - openAiModelId?: string | undefined - openAiCustomModelInfo?: - | ({ - maxTokens?: (number | null) | undefined - maxThinkingTokens?: (number | null) | undefined - contextWindow: number - supportsImages?: boolean | undefined - supportsComputerUse?: boolean | undefined - supportsPromptCache: boolean - supportsReasoningBudget?: boolean | undefined - requiredReasoningBudget?: boolean | undefined - supportsReasoningEffort?: boolean | undefined - supportedParameters?: ("max_tokens" | "temperature" | "reasoning" | "include_reasoning")[] | undefined - inputPrice?: number | undefined - outputPrice?: number | undefined - cacheWritesPrice?: number | undefined - cacheReadsPrice?: number | undefined - description?: string | undefined - reasoningEffort?: ("low" | "medium" | "high") | undefined - minTokensPerCachePoint?: number | undefined - maxCachePoints?: number | undefined - cachableFields?: string[] | undefined - tiers?: - | { - contextWindow: number - inputPrice?: number | undefined - outputPrice?: number | undefined - cacheWritesPrice?: number | undefined - cacheReadsPrice?: number | undefined - }[] - | undefined - } | null) - | undefined - openAiUseAzure?: boolean | undefined - azureApiVersion?: string | undefined - openAiStreamingEnabled?: boolean | undefined - openAiHostHeader?: string | undefined - openAiHeaders?: - | { - [x: string]: string - } - | undefined - ollamaModelId?: string | undefined - ollamaBaseUrl?: string | undefined - vsCodeLmModelSelector?: - | { - vendor?: string | undefined - family?: string | undefined - version?: string | undefined - id?: string | undefined - } - | undefined - lmStudioModelId?: string | undefined - lmStudioBaseUrl?: string | undefined - lmStudioDraftModelId?: string | undefined - lmStudioSpeculativeDecodingEnabled?: boolean | undefined - geminiApiKey?: string | undefined - googleGeminiBaseUrl?: string | undefined - openAiNativeApiKey?: string | undefined - openAiNativeBaseUrl?: string | undefined - mistralApiKey?: string | undefined - mistralCodestralUrl?: string | undefined - deepSeekBaseUrl?: string | undefined - deepSeekApiKey?: string | undefined - unboundApiKey?: string | undefined - unboundModelId?: string | undefined - requestyApiKey?: string | undefined - requestyModelId?: string | undefined - fakeAi?: unknown | undefined - xaiApiKey?: string | undefined - groqApiKey?: string | undefined - chutesApiKey?: string | undefined - litellmBaseUrl?: string | undefined - litellmApiKey?: string | undefined - litellmModelId?: string | undefined - codeIndexOpenAiKey?: string | undefined - codeIndexQdrantApiKey?: string | undefined -} - -export type { ProviderSettings } - -type ProviderSettingsEntry = { - id: string - name: string - apiProvider?: - | ( - | "anthropic" - | "glama" - | "openrouter" - | "bedrock" - | "vertex" - | "openai" - | "ollama" - | "vscode-lm" - | "lmstudio" - | "gemini" - | "openai-native" - | "mistral" - | "deepseek" - | "unbound" - | "requesty" - | "human-relay" - | "fake-ai" - | "xai" - | "groq" - | "chutes" - | "litellm" - ) - | undefined -} - -export type { ProviderSettingsEntry } - -type ClineMessage = { - ts: number - type: "ask" | "say" - ask?: - | ( - | "followup" - | "command" - | "command_output" - | "completion_result" - | "tool" - | "api_req_failed" - | "resume_task" - | "resume_completed_task" - | "mistake_limit_reached" - | "browser_action_launch" - | "use_mcp_server" - | "auto_approval_max_req_reached" - ) - | undefined - say?: - | ( - | "error" - | "api_req_started" - | "api_req_finished" - | "api_req_retried" - | "api_req_retry_delayed" - | "api_req_deleted" - | "text" - | "reasoning" - | "completion_result" - | "user_feedback" - | "user_feedback_diff" - | "command_output" - | "shell_integration_warning" - | "browser_action" - | "browser_action_result" - | "mcp_server_request_started" - | "mcp_server_response" - | "subtask_result" - | "checkpoint_saved" - | "rooignore_error" - | "diff_error" - | "condense_context" - | "codebase_search_result" - ) - | undefined - text?: string | undefined - images?: string[] | undefined - partial?: boolean | undefined - reasoning?: string | undefined - conversationHistoryIndex?: number | undefined - checkpoint?: - | { - [x: string]: unknown - } - | undefined - progressStatus?: - | { - icon?: string | undefined - text?: string | undefined - } - | undefined - contextCondense?: - | { - cost: number - prevContextTokens: number - newContextTokens: number - summary: string - } - | undefined -} - -export type { ClineMessage } - -type TokenUsage = { - totalTokensIn: number - totalTokensOut: number - totalCacheWrites?: number | undefined - totalCacheReads?: number | undefined - totalCost: number - contextTokens: number -} - -export type { TokenUsage } - -type RooCodeEvents = { - message: [ - { - taskId: string - action: "created" | "updated" - message: { - ts: number - type: "ask" | "say" - ask?: - | ( - | "followup" - | "command" - | "command_output" - | "completion_result" - | "tool" - | "api_req_failed" - | "resume_task" - | "resume_completed_task" - | "mistake_limit_reached" - | "browser_action_launch" - | "use_mcp_server" - | "auto_approval_max_req_reached" - ) - | undefined - say?: - | ( - | "error" - | "api_req_started" - | "api_req_finished" - | "api_req_retried" - | "api_req_retry_delayed" - | "api_req_deleted" - | "text" - | "reasoning" - | "completion_result" - | "user_feedback" - | "user_feedback_diff" - | "command_output" - | "shell_integration_warning" - | "browser_action" - | "browser_action_result" - | "mcp_server_request_started" - | "mcp_server_response" - | "subtask_result" - | "checkpoint_saved" - | "rooignore_error" - | "diff_error" - | "condense_context" - | "codebase_search_result" - ) - | undefined - text?: string | undefined - images?: string[] | undefined - partial?: boolean | undefined - reasoning?: string | undefined - conversationHistoryIndex?: number | undefined - checkpoint?: - | { - [x: string]: unknown - } - | undefined - progressStatus?: - | { - icon?: string | undefined - text?: string | undefined - } - | undefined - contextCondense?: - | { - cost: number - prevContextTokens: number - newContextTokens: number - summary: string - } - | undefined - } - }, - ] - taskCreated: [string] - taskStarted: [string] - taskModeSwitched: [string, string] - taskPaused: [string] - taskUnpaused: [string] - taskAskResponded: [string] - taskAborted: [string] - taskSpawned: [string, string] - taskCompleted: [ - string, - { - totalTokensIn: number - totalTokensOut: number - totalCacheWrites?: number | undefined - totalCacheReads?: number | undefined - totalCost: number - contextTokens: number - }, - { - [x: string]: { - attempts: number - failures: number - } - }, - ] - taskTokenUsageUpdated: [ - string, - { - totalTokensIn: number - totalTokensOut: number - totalCacheWrites?: number | undefined - totalCacheReads?: number | undefined - totalCost: number - contextTokens: number - }, - ] - taskToolFailed: [ - string, - ( - | "execute_command" - | "read_file" - | "write_to_file" - | "apply_diff" - | "insert_content" - | "search_and_replace" - | "search_files" - | "list_files" - | "list_code_definition_names" - | "browser_action" - | "use_mcp_tool" - | "access_mcp_resource" - | "ask_followup_question" - | "attempt_completion" - | "switch_mode" - | "new_task" - | "fetch_instructions" - | "codebase_search" - ), - string, - ] -} - -export type { RooCodeEvents } - -type IpcMessage = - | { - type: "Ack" - origin: "server" - data: { - clientId: string - pid: number - ppid: number - } - } - | { - type: "TaskCommand" - origin: "client" - clientId: string - data: - | { - commandName: "StartNewTask" - data: { - configuration: { - apiProvider?: - | ( - | "anthropic" - | "glama" - | "openrouter" - | "bedrock" - | "vertex" - | "openai" - | "ollama" - | "vscode-lm" - | "lmstudio" - | "gemini" - | "openai-native" - | "mistral" - | "deepseek" - | "unbound" - | "requesty" - | "human-relay" - | "fake-ai" - | "xai" - | "groq" - | "chutes" - | "litellm" - ) - | undefined - includeMaxTokens?: boolean | undefined - diffEnabled?: boolean | undefined - fuzzyMatchThreshold?: number | undefined - modelTemperature?: (number | null) | undefined - rateLimitSeconds?: number | undefined - enableReasoningEffort?: boolean | undefined - reasoningEffort?: ("low" | "medium" | "high") | undefined - modelMaxTokens?: number | undefined - modelMaxThinkingTokens?: number | undefined - apiModelId?: string | undefined - apiKey?: string | undefined - anthropicBaseUrl?: string | undefined - anthropicUseAuthToken?: boolean | undefined - glamaModelId?: string | undefined - glamaApiKey?: string | undefined - openRouterApiKey?: string | undefined - openRouterModelId?: string | undefined - openRouterBaseUrl?: string | undefined - openRouterSpecificProvider?: string | undefined - openRouterUseMiddleOutTransform?: boolean | undefined - awsAccessKey?: string | undefined - awsSecretKey?: string | undefined - awsSessionToken?: string | undefined - awsRegion?: string | undefined - awsUseCrossRegionInference?: boolean | undefined - awsUsePromptCache?: boolean | undefined - awsProfile?: string | undefined - awsUseProfile?: boolean | undefined - awsCustomArn?: string | undefined - vertexKeyFile?: string | undefined - vertexJsonCredentials?: string | undefined - vertexProjectId?: string | undefined - vertexRegion?: string | undefined - openAiBaseUrl?: string | undefined - openAiApiKey?: string | undefined - openAiLegacyFormat?: boolean | undefined - openAiR1FormatEnabled?: boolean | undefined - openAiModelId?: string | undefined - openAiCustomModelInfo?: - | ({ - maxTokens?: (number | null) | undefined - maxThinkingTokens?: (number | null) | undefined - contextWindow: number - supportsImages?: boolean | undefined - supportsComputerUse?: boolean | undefined - supportsPromptCache: boolean - supportsReasoningBudget?: boolean | undefined - requiredReasoningBudget?: boolean | undefined - supportsReasoningEffort?: boolean | undefined - supportedParameters?: - | ("max_tokens" | "temperature" | "reasoning" | "include_reasoning")[] - | undefined - inputPrice?: number | undefined - outputPrice?: number | undefined - cacheWritesPrice?: number | undefined - cacheReadsPrice?: number | undefined - description?: string | undefined - reasoningEffort?: ("low" | "medium" | "high") | undefined - minTokensPerCachePoint?: number | undefined - maxCachePoints?: number | undefined - cachableFields?: string[] | undefined - tiers?: - | { - contextWindow: number - inputPrice?: number | undefined - outputPrice?: number | undefined - cacheWritesPrice?: number | undefined - cacheReadsPrice?: number | undefined - }[] - | undefined - } | null) - | undefined - openAiUseAzure?: boolean | undefined - azureApiVersion?: string | undefined - openAiStreamingEnabled?: boolean | undefined - openAiHostHeader?: string | undefined - openAiHeaders?: - | { - [x: string]: string - } - | undefined - ollamaModelId?: string | undefined - ollamaBaseUrl?: string | undefined - vsCodeLmModelSelector?: - | { - vendor?: string | undefined - family?: string | undefined - version?: string | undefined - id?: string | undefined - } - | undefined - lmStudioModelId?: string | undefined - lmStudioBaseUrl?: string | undefined - lmStudioDraftModelId?: string | undefined - lmStudioSpeculativeDecodingEnabled?: boolean | undefined - geminiApiKey?: string | undefined - googleGeminiBaseUrl?: string | undefined - openAiNativeApiKey?: string | undefined - openAiNativeBaseUrl?: string | undefined - mistralApiKey?: string | undefined - mistralCodestralUrl?: string | undefined - deepSeekBaseUrl?: string | undefined - deepSeekApiKey?: string | undefined - unboundApiKey?: string | undefined - unboundModelId?: string | undefined - requestyApiKey?: string | undefined - requestyModelId?: string | undefined - fakeAi?: unknown | undefined - xaiApiKey?: string | undefined - groqApiKey?: string | undefined - chutesApiKey?: string | undefined - litellmBaseUrl?: string | undefined - litellmApiKey?: string | undefined - litellmModelId?: string | undefined - codeIndexOpenAiKey?: string | undefined - codeIndexQdrantApiKey?: string | undefined - currentApiConfigName?: string | undefined - listApiConfigMeta?: - | { - id: string - name: string - apiProvider?: - | ( - | "anthropic" - | "glama" - | "openrouter" - | "bedrock" - | "vertex" - | "openai" - | "ollama" - | "vscode-lm" - | "lmstudio" - | "gemini" - | "openai-native" - | "mistral" - | "deepseek" - | "unbound" - | "requesty" - | "human-relay" - | "fake-ai" - | "xai" - | "groq" - | "chutes" - | "litellm" - ) - | undefined - }[] - | undefined - pinnedApiConfigs?: - | { - [x: string]: boolean - } - | undefined - lastShownAnnouncementId?: string | undefined - customInstructions?: string | undefined - taskHistory?: - | { - id: string - number: number - ts: number - task: string - tokensIn: number - tokensOut: number - cacheWrites?: number | undefined - cacheReads?: number | undefined - totalCost: number - size?: number | undefined - workspace?: string | undefined - }[] - | undefined - condensingApiConfigId?: string | undefined - customCondensingPrompt?: string | undefined - autoApprovalEnabled?: boolean | undefined - alwaysAllowReadOnly?: boolean | undefined - alwaysAllowReadOnlyOutsideWorkspace?: boolean | undefined - codebaseIndexModels?: - | { - openai?: - | { - [x: string]: { - dimension: number - } - } - | undefined - ollama?: - | { - [x: string]: { - dimension: number - } - } - | undefined - } - | undefined - codebaseIndexConfig?: - | { - codebaseIndexEnabled?: boolean | undefined - codebaseIndexQdrantUrl?: string | undefined - codebaseIndexEmbedderProvider?: ("openai" | "ollama") | undefined - codebaseIndexEmbedderBaseUrl?: string | undefined - codebaseIndexEmbedderModelId?: string | undefined - } - | undefined - alwaysAllowWrite?: boolean | undefined - alwaysAllowWriteOutsideWorkspace?: boolean | undefined - writeDelayMs?: number | undefined - alwaysAllowBrowser?: boolean | undefined - alwaysApproveResubmit?: boolean | undefined - requestDelaySeconds?: number | undefined - alwaysAllowMcp?: boolean | undefined - alwaysAllowModeSwitch?: boolean | undefined - alwaysAllowSubtasks?: boolean | undefined - alwaysAllowExecute?: boolean | undefined - allowedCommands?: string[] | undefined - allowedMaxRequests?: (number | null) | undefined - autoCondenseContextPercent?: number | undefined - browserToolEnabled?: boolean | undefined - browserViewportSize?: string | undefined - screenshotQuality?: number | undefined - remoteBrowserEnabled?: boolean | undefined - remoteBrowserHost?: string | undefined - cachedChromeHostUrl?: string | undefined - enableCheckpoints?: boolean | undefined - ttsEnabled?: boolean | undefined - ttsSpeed?: number | undefined - soundEnabled?: boolean | undefined - soundVolume?: number | undefined - maxOpenTabsContext?: number | undefined - maxWorkspaceFiles?: number | undefined - showRooIgnoredFiles?: boolean | undefined - maxReadFileLine?: number | undefined - terminalOutputLineLimit?: number | undefined - terminalShellIntegrationTimeout?: number | undefined - terminalShellIntegrationDisabled?: boolean | undefined - terminalCommandDelay?: number | undefined - terminalPowershellCounter?: boolean | undefined - terminalZshClearEolMark?: boolean | undefined - terminalZshOhMy?: boolean | undefined - terminalZshP10k?: boolean | undefined - terminalZdotdir?: boolean | undefined - terminalCompressProgressBar?: boolean | undefined - experiments?: - | { - autoCondenseContext: boolean - powerSteering: boolean - } - | undefined - language?: - | ( - | "ca" - | "de" - | "en" - | "es" - | "fr" - | "hi" - | "it" - | "ja" - | "ko" - | "nl" - | "pl" - | "pt-BR" - | "ru" - | "tr" - | "vi" - | "zh-CN" - | "zh-TW" - ) - | undefined - telemetrySetting?: ("unset" | "enabled" | "disabled") | undefined - mcpEnabled?: boolean | undefined - enableMcpServerCreation?: boolean | undefined - mode?: string | undefined - modeApiConfigs?: - | { - [x: string]: string - } - | undefined - customModes?: - | { - slug: string - name: string - roleDefinition: string - whenToUse?: string | undefined - customInstructions?: string | undefined - groups: ( - | ("read" | "edit" | "browser" | "command" | "mcp" | "modes") - | [ - "read" | "edit" | "browser" | "command" | "mcp" | "modes", - { - fileRegex?: string | undefined - description?: string | undefined - }, - ] - )[] - source?: ("global" | "project") | undefined - }[] - | undefined - customModePrompts?: - | { - [x: string]: - | { - roleDefinition?: string | undefined - whenToUse?: string | undefined - customInstructions?: string | undefined - } - | undefined - } - | undefined - customSupportPrompts?: - | { - [x: string]: string | undefined - } - | undefined - enhancementApiConfigId?: string | undefined - historyPreviewCollapsed?: boolean | undefined - } - text: string - images?: string[] | undefined - newTab?: boolean | undefined - } - } - | { - commandName: "CancelTask" - data: string - } - | { - commandName: "CloseTask" - data: string - } - } - | { - type: "TaskEvent" - origin: "server" - relayClientId?: string | undefined - data: - | { - eventName: "message" - payload: [ - { - taskId: string - action: "created" | "updated" - message: { - ts: number - type: "ask" | "say" - ask?: - | ( - | "followup" - | "command" - | "command_output" - | "completion_result" - | "tool" - | "api_req_failed" - | "resume_task" - | "resume_completed_task" - | "mistake_limit_reached" - | "browser_action_launch" - | "use_mcp_server" - | "auto_approval_max_req_reached" - ) - | undefined - say?: - | ( - | "error" - | "api_req_started" - | "api_req_finished" - | "api_req_retried" - | "api_req_retry_delayed" - | "api_req_deleted" - | "text" - | "reasoning" - | "completion_result" - | "user_feedback" - | "user_feedback_diff" - | "command_output" - | "shell_integration_warning" - | "browser_action" - | "browser_action_result" - | "mcp_server_request_started" - | "mcp_server_response" - | "subtask_result" - | "checkpoint_saved" - | "rooignore_error" - | "diff_error" - | "condense_context" - | "codebase_search_result" - ) - | undefined - text?: string | undefined - images?: string[] | undefined - partial?: boolean | undefined - reasoning?: string | undefined - conversationHistoryIndex?: number | undefined - checkpoint?: - | { - [x: string]: unknown - } - | undefined - progressStatus?: - | { - icon?: string | undefined - text?: string | undefined - } - | undefined - contextCondense?: - | { - cost: number - prevContextTokens: number - newContextTokens: number - summary: string - } - | undefined - } - }, - ] - } - | { - eventName: "taskCreated" - payload: [string] - } - | { - eventName: "taskStarted" - payload: [string] - } - | { - eventName: "taskModeSwitched" - payload: [string, string] - } - | { - eventName: "taskPaused" - payload: [string] - } - | { - eventName: "taskUnpaused" - payload: [string] - } - | { - eventName: "taskAskResponded" - payload: [string] - } - | { - eventName: "taskAborted" - payload: [string] - } - | { - eventName: "taskSpawned" - payload: [string, string] - } - | { - eventName: "taskCompleted" - payload: [ - string, - { - totalTokensIn: number - totalTokensOut: number - totalCacheWrites?: number | undefined - totalCacheReads?: number | undefined - totalCost: number - contextTokens: number - }, - { - [x: string]: { - attempts: number - failures: number - } - }, - ] - } - | { - eventName: "taskTokenUsageUpdated" - payload: [ - string, - { - totalTokensIn: number - totalTokensOut: number - totalCacheWrites?: number | undefined - totalCacheReads?: number | undefined - totalCost: number - contextTokens: number - }, - ] - } - } - -export type { IpcMessage } - -type TaskCommand = - | { - commandName: "StartNewTask" - data: { - configuration: { - apiProvider?: - | ( - | "anthropic" - | "glama" - | "openrouter" - | "bedrock" - | "vertex" - | "openai" - | "ollama" - | "vscode-lm" - | "lmstudio" - | "gemini" - | "openai-native" - | "mistral" - | "deepseek" - | "unbound" - | "requesty" - | "human-relay" - | "fake-ai" - | "xai" - | "groq" - | "chutes" - | "litellm" - ) - | undefined - includeMaxTokens?: boolean | undefined - diffEnabled?: boolean | undefined - fuzzyMatchThreshold?: number | undefined - modelTemperature?: (number | null) | undefined - rateLimitSeconds?: number | undefined - enableReasoningEffort?: boolean | undefined - reasoningEffort?: ("low" | "medium" | "high") | undefined - modelMaxTokens?: number | undefined - modelMaxThinkingTokens?: number | undefined - apiModelId?: string | undefined - apiKey?: string | undefined - anthropicBaseUrl?: string | undefined - anthropicUseAuthToken?: boolean | undefined - glamaModelId?: string | undefined - glamaApiKey?: string | undefined - openRouterApiKey?: string | undefined - openRouterModelId?: string | undefined - openRouterBaseUrl?: string | undefined - openRouterSpecificProvider?: string | undefined - openRouterUseMiddleOutTransform?: boolean | undefined - awsAccessKey?: string | undefined - awsSecretKey?: string | undefined - awsSessionToken?: string | undefined - awsRegion?: string | undefined - awsUseCrossRegionInference?: boolean | undefined - awsUsePromptCache?: boolean | undefined - awsProfile?: string | undefined - awsUseProfile?: boolean | undefined - awsCustomArn?: string | undefined - vertexKeyFile?: string | undefined - vertexJsonCredentials?: string | undefined - vertexProjectId?: string | undefined - vertexRegion?: string | undefined - openAiBaseUrl?: string | undefined - openAiApiKey?: string | undefined - openAiLegacyFormat?: boolean | undefined - openAiR1FormatEnabled?: boolean | undefined - openAiModelId?: string | undefined - openAiCustomModelInfo?: - | ({ - maxTokens?: (number | null) | undefined - maxThinkingTokens?: (number | null) | undefined - contextWindow: number - supportsImages?: boolean | undefined - supportsComputerUse?: boolean | undefined - supportsPromptCache: boolean - supportsReasoningBudget?: boolean | undefined - requiredReasoningBudget?: boolean | undefined - supportsReasoningEffort?: boolean | undefined - supportedParameters?: - | ("max_tokens" | "temperature" | "reasoning" | "include_reasoning")[] - | undefined - inputPrice?: number | undefined - outputPrice?: number | undefined - cacheWritesPrice?: number | undefined - cacheReadsPrice?: number | undefined - description?: string | undefined - reasoningEffort?: ("low" | "medium" | "high") | undefined - minTokensPerCachePoint?: number | undefined - maxCachePoints?: number | undefined - cachableFields?: string[] | undefined - tiers?: - | { - contextWindow: number - inputPrice?: number | undefined - outputPrice?: number | undefined - cacheWritesPrice?: number | undefined - cacheReadsPrice?: number | undefined - }[] - | undefined - } | null) - | undefined - openAiUseAzure?: boolean | undefined - azureApiVersion?: string | undefined - openAiStreamingEnabled?: boolean | undefined - openAiHostHeader?: string | undefined - openAiHeaders?: - | { - [x: string]: string - } - | undefined - ollamaModelId?: string | undefined - ollamaBaseUrl?: string | undefined - vsCodeLmModelSelector?: - | { - vendor?: string | undefined - family?: string | undefined - version?: string | undefined - id?: string | undefined - } - | undefined - lmStudioModelId?: string | undefined - lmStudioBaseUrl?: string | undefined - lmStudioDraftModelId?: string | undefined - lmStudioSpeculativeDecodingEnabled?: boolean | undefined - geminiApiKey?: string | undefined - googleGeminiBaseUrl?: string | undefined - openAiNativeApiKey?: string | undefined - openAiNativeBaseUrl?: string | undefined - mistralApiKey?: string | undefined - mistralCodestralUrl?: string | undefined - deepSeekBaseUrl?: string | undefined - deepSeekApiKey?: string | undefined - unboundApiKey?: string | undefined - unboundModelId?: string | undefined - requestyApiKey?: string | undefined - requestyModelId?: string | undefined - fakeAi?: unknown | undefined - xaiApiKey?: string | undefined - groqApiKey?: string | undefined - chutesApiKey?: string | undefined - litellmBaseUrl?: string | undefined - litellmApiKey?: string | undefined - litellmModelId?: string | undefined - codeIndexOpenAiKey?: string | undefined - codeIndexQdrantApiKey?: string | undefined - currentApiConfigName?: string | undefined - listApiConfigMeta?: - | { - id: string - name: string - apiProvider?: - | ( - | "anthropic" - | "glama" - | "openrouter" - | "bedrock" - | "vertex" - | "openai" - | "ollama" - | "vscode-lm" - | "lmstudio" - | "gemini" - | "openai-native" - | "mistral" - | "deepseek" - | "unbound" - | "requesty" - | "human-relay" - | "fake-ai" - | "xai" - | "groq" - | "chutes" - | "litellm" - ) - | undefined - }[] - | undefined - pinnedApiConfigs?: - | { - [x: string]: boolean - } - | undefined - lastShownAnnouncementId?: string | undefined - customInstructions?: string | undefined - taskHistory?: - | { - id: string - number: number - ts: number - task: string - tokensIn: number - tokensOut: number - cacheWrites?: number | undefined - cacheReads?: number | undefined - totalCost: number - size?: number | undefined - workspace?: string | undefined - }[] - | undefined - condensingApiConfigId?: string | undefined - customCondensingPrompt?: string | undefined - autoApprovalEnabled?: boolean | undefined - alwaysAllowReadOnly?: boolean | undefined - alwaysAllowReadOnlyOutsideWorkspace?: boolean | undefined - codebaseIndexModels?: - | { - openai?: - | { - [x: string]: { - dimension: number - } - } - | undefined - ollama?: - | { - [x: string]: { - dimension: number - } - } - | undefined - } - | undefined - codebaseIndexConfig?: - | { - codebaseIndexEnabled?: boolean | undefined - codebaseIndexQdrantUrl?: string | undefined - codebaseIndexEmbedderProvider?: ("openai" | "ollama") | undefined - codebaseIndexEmbedderBaseUrl?: string | undefined - codebaseIndexEmbedderModelId?: string | undefined - } - | undefined - alwaysAllowWrite?: boolean | undefined - alwaysAllowWriteOutsideWorkspace?: boolean | undefined - writeDelayMs?: number | undefined - alwaysAllowBrowser?: boolean | undefined - alwaysApproveResubmit?: boolean | undefined - requestDelaySeconds?: number | undefined - alwaysAllowMcp?: boolean | undefined - alwaysAllowModeSwitch?: boolean | undefined - alwaysAllowSubtasks?: boolean | undefined - alwaysAllowExecute?: boolean | undefined - allowedCommands?: string[] | undefined - allowedMaxRequests?: (number | null) | undefined - autoCondenseContextPercent?: number | undefined - browserToolEnabled?: boolean | undefined - browserViewportSize?: string | undefined - screenshotQuality?: number | undefined - remoteBrowserEnabled?: boolean | undefined - remoteBrowserHost?: string | undefined - cachedChromeHostUrl?: string | undefined - enableCheckpoints?: boolean | undefined - ttsEnabled?: boolean | undefined - ttsSpeed?: number | undefined - soundEnabled?: boolean | undefined - soundVolume?: number | undefined - maxOpenTabsContext?: number | undefined - maxWorkspaceFiles?: number | undefined - showRooIgnoredFiles?: boolean | undefined - maxReadFileLine?: number | undefined - terminalOutputLineLimit?: number | undefined - terminalShellIntegrationTimeout?: number | undefined - terminalShellIntegrationDisabled?: boolean | undefined - terminalCommandDelay?: number | undefined - terminalPowershellCounter?: boolean | undefined - terminalZshClearEolMark?: boolean | undefined - terminalZshOhMy?: boolean | undefined - terminalZshP10k?: boolean | undefined - terminalZdotdir?: boolean | undefined - terminalCompressProgressBar?: boolean | undefined - experiments?: - | { - autoCondenseContext: boolean - powerSteering: boolean - } - | undefined - language?: - | ( - | "ca" - | "de" - | "en" - | "es" - | "fr" - | "hi" - | "it" - | "ja" - | "ko" - | "nl" - | "pl" - | "pt-BR" - | "ru" - | "tr" - | "vi" - | "zh-CN" - | "zh-TW" - ) - | undefined - telemetrySetting?: ("unset" | "enabled" | "disabled") | undefined - mcpEnabled?: boolean | undefined - enableMcpServerCreation?: boolean | undefined - mode?: string | undefined - modeApiConfigs?: - | { - [x: string]: string - } - | undefined - customModes?: - | { - slug: string - name: string - roleDefinition: string - whenToUse?: string | undefined - customInstructions?: string | undefined - groups: ( - | ("read" | "edit" | "browser" | "command" | "mcp" | "modes") - | [ - "read" | "edit" | "browser" | "command" | "mcp" | "modes", - { - fileRegex?: string | undefined - description?: string | undefined - }, - ] - )[] - source?: ("global" | "project") | undefined - }[] - | undefined - customModePrompts?: - | { - [x: string]: - | { - roleDefinition?: string | undefined - whenToUse?: string | undefined - customInstructions?: string | undefined - } - | undefined - } - | undefined - customSupportPrompts?: - | { - [x: string]: string | undefined - } - | undefined - enhancementApiConfigId?: string | undefined - historyPreviewCollapsed?: boolean | undefined - } - text: string - images?: string[] | undefined - newTab?: boolean | undefined - } - } - | { - commandName: "CancelTask" - data: string - } - | { - commandName: "CloseTask" - data: string - } - -export type { TaskCommand } - -type TaskEvent = - | { - eventName: "message" - payload: [ - { - taskId: string - action: "created" | "updated" - message: { - ts: number - type: "ask" | "say" - ask?: - | ( - | "followup" - | "command" - | "command_output" - | "completion_result" - | "tool" - | "api_req_failed" - | "resume_task" - | "resume_completed_task" - | "mistake_limit_reached" - | "browser_action_launch" - | "use_mcp_server" - | "auto_approval_max_req_reached" - ) - | undefined - say?: - | ( - | "error" - | "api_req_started" - | "api_req_finished" - | "api_req_retried" - | "api_req_retry_delayed" - | "api_req_deleted" - | "text" - | "reasoning" - | "completion_result" - | "user_feedback" - | "user_feedback_diff" - | "command_output" - | "shell_integration_warning" - | "browser_action" - | "browser_action_result" - | "mcp_server_request_started" - | "mcp_server_response" - | "subtask_result" - | "checkpoint_saved" - | "rooignore_error" - | "diff_error" - | "condense_context" - | "codebase_search_result" - ) - | undefined - text?: string | undefined - images?: string[] | undefined - partial?: boolean | undefined - reasoning?: string | undefined - conversationHistoryIndex?: number | undefined - checkpoint?: - | { - [x: string]: unknown - } - | undefined - progressStatus?: - | { - icon?: string | undefined - text?: string | undefined - } - | undefined - contextCondense?: - | { - cost: number - prevContextTokens: number - newContextTokens: number - summary: string - } - | undefined - } - }, - ] - } - | { - eventName: "taskCreated" - payload: [string] - } - | { - eventName: "taskStarted" - payload: [string] - } - | { - eventName: "taskModeSwitched" - payload: [string, string] - } - | { - eventName: "taskPaused" - payload: [string] - } - | { - eventName: "taskUnpaused" - payload: [string] - } - | { - eventName: "taskAskResponded" - payload: [string] - } - | { - eventName: "taskAborted" - payload: [string] - } - | { - eventName: "taskSpawned" - payload: [string, string] - } - | { - eventName: "taskCompleted" - payload: [ - string, - { - totalTokensIn: number - totalTokensOut: number - totalCacheWrites?: number | undefined - totalCacheReads?: number | undefined - totalCost: number - contextTokens: number - }, - { - [x: string]: { - attempts: number - failures: number - } - }, - ] - } - | { - eventName: "taskTokenUsageUpdated" - payload: [ - string, - { - totalTokensIn: number - totalTokensOut: number - totalCacheWrites?: number | undefined - totalCacheReads?: number | undefined - totalCost: number - contextTokens: number - }, - ] - } - -export type { TaskEvent } diff --git a/src/extension.ts b/src/extension.ts index 0244e425f8..db4edd7b26 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -14,17 +14,17 @@ try { import "./utils/path" // Necessary to have access to String.prototype.toPosix. -import { Package } from "./schemas" +import { Package } from "./shared/package" +import { formatLanguage } from "./shared/language" import { ContextProxy } from "./core/config/ContextProxy" import { ClineProvider } from "./core/webview/ClineProvider" import { DIFF_VIEW_URI_SCHEME } from "./integrations/editor/DiffViewProvider" import { TerminalRegistry } from "./integrations/terminal/TerminalRegistry" import { McpServerManager } from "./services/mcp/McpServerManager" import { telemetryService } from "./services/telemetry/TelemetryService" -import { API } from "./exports/api" -import { migrateSettings } from "./utils/migrateSettings" -import { formatLanguage } from "./shared/language" import { CodeIndexManager } from "./services/code-index/manager" +import { migrateSettings } from "./utils/migrateSettings" +import { API } from "./extension/api" import { handleUri, diff --git a/src/exports/api.ts b/src/extension/api.ts similarity index 90% rename from src/exports/api.ts rename to src/extension/api.ts index 17728f3019..f9b8e7a69a 100644 --- a/src/exports/api.ts +++ b/src/extension/api.ts @@ -3,11 +3,8 @@ import * as vscode from "vscode" import fs from "fs/promises" import * as path from "path" -import { Package } from "../schemas" -import { getWorkspacePath } from "../utils/path" -import { ClineProvider } from "../core/webview/ClineProvider" -import { openClineInNewTab } from "../activate/registerCommands" import { + RooCodeAPI, RooCodeSettings, RooCodeEvents, RooCodeEventName, @@ -18,11 +15,14 @@ import { IpcMessageType, TaskCommandName, TaskEvent, -} from "../schemas" +} from "@roo-code/types" -import { RooCodeAPI } from "./interface" -import { IpcServer } from "./ipc" -import { outputChannelLog } from "./log" +import { Package } from "../shared/package" +import { getWorkspacePath } from "../utils/path" +import { ClineProvider } from "../core/webview/ClineProvider" +import { openClineInNewTab } from "../activate/registerCommands" + +import { IpcServer } from "./ipc-server" export class API extends EventEmitter implements RooCodeAPI { private readonly outputChannel: vscode.OutputChannel @@ -47,7 +47,7 @@ export class API extends EventEmitter implements RooCodeAPI { if (enableLogging) { this.log = (...args: unknown[]) => { - outputChannelLog(this.outputChannel, ...args) + this.outputChannelLog(...args) console.log(args) } @@ -243,6 +243,39 @@ export class API extends EventEmitter implements RooCodeAPI { }) } + // Logging + + private outputChannelLog(...args: unknown[]) { + for (const arg of args) { + if (arg === null) { + this.outputChannel.appendLine("null") + } else if (arg === undefined) { + this.outputChannel.appendLine("undefined") + } else if (typeof arg === "string") { + this.outputChannel.appendLine(arg) + } else if (arg instanceof Error) { + this.outputChannel.appendLine(`Error: ${arg.message}\n${arg.stack || ""}`) + } else { + try { + this.outputChannel.appendLine( + JSON.stringify( + arg, + (key, value) => { + if (typeof value === "bigint") return `BigInt(${value})` + if (typeof value === "function") return `Function: ${value.name || "anonymous"}` + if (typeof value === "symbol") return value.toString() + return value + }, + 2, + ), + ) + } catch (error) { + this.outputChannel.appendLine(`[Non-serializable object: ${Object.prototype.toString.call(arg)}]`) + } + } + } + } + private async fileLog(message: string) { if (!this.logfile) { return diff --git a/src/exports/ipc.ts b/src/extension/ipc-server.ts similarity index 95% rename from src/exports/ipc.ts rename to src/extension/ipc-server.ts index 85950c5ee6..3903846243 100644 --- a/src/exports/ipc.ts +++ b/src/extension/ipc-server.ts @@ -4,8 +4,14 @@ import * as crypto from "node:crypto" import ipc from "node-ipc" -import { IpcOrigin, IpcMessageType, type IpcMessage, ipcMessageSchema } from "../schemas" -import type { IpcServerEvents, RooCodeIpcServer } from "./interface" +import { + type IpcServerEvents, + type RooCodeIpcServer, + IpcOrigin, + IpcMessageType, + type IpcMessage, + ipcMessageSchema, +} from "@roo-code/types" /** * IpcServer diff --git a/src/integrations/diagnostics/__tests__/diagnostics.test.ts b/src/integrations/diagnostics/__tests__/diagnostics.test.ts index 3cf9ede4f6..874ce40b09 100644 --- a/src/integrations/diagnostics/__tests__/diagnostics.test.ts +++ b/src/integrations/diagnostics/__tests__/diagnostics.test.ts @@ -1,5 +1,6 @@ import * as vscode from "vscode" -import { diagnosticsToProblemsString } from ".." + +import { diagnosticsToProblemsString } from "../index" // Mock path module jest.mock("path", () => ({ diff --git a/src/integrations/theme/getTheme.ts b/src/integrations/theme/getTheme.ts index 7951b9e2c0..20171cc304 100644 --- a/src/integrations/theme/getTheme.ts +++ b/src/integrations/theme/getTheme.ts @@ -3,7 +3,7 @@ import * as path from "path" import * as fs from "fs/promises" import { convertTheme } from "monaco-vscode-textmate-theme-converter/lib/cjs" -import { Package } from "../../schemas" +import { Package } from "../../shared/package" const defaultThemes: Record = { "Default Dark Modern": "dark_modern", diff --git a/src/package.json b/src/package.json index c76298e59a..dba6499244 100644 --- a/src/package.json +++ b/src/package.json @@ -320,19 +320,16 @@ "scripts": { "lint": "eslint . --ext=ts --max-warnings=0", "check-types": "tsc --noEmit", - "pretest": "pnpm bundle", + "pretest": "turbo run bundle --cwd ..", "test": "jest -w=40% && vitest run --globals", "format": "prettier --write .", - "bundle": "pnpm clean && pnpm --filter @roo-code/build build && node esbuild.mjs", - "build": "pnpm bundle --production && pnpm --filter @roo-code/vscode-webview build", - "build:development": "pnpm bundle && pnpm --filter @roo-code/vscode-webview build", - "publish:marketplace": "vsce publish --no-dependencies && ovsx publish --no-dependencies", - "vscode:prepublish": "pnpm build", + "bundle": "node esbuild.mjs", + "vscode:prepublish": "pnpm bundle --production", "vsix": "mkdirp ../bin && npx vsce package --no-dependencies --out ../bin", + "publish:marketplace": "vsce publish --no-dependencies && ovsx publish --no-dependencies", "watch:esbuild": "pnpm bundle --watch", "watch:tsc": "tsc --noEmit --watch --project tsconfig.json", - "generate-types": "tsx scripts/generate-types.mts", - "clean": "rimraf README.md CHANGELOG.md LICENSE dist webview-ui out .turbo" + "clean": "rimraf README.md CHANGELOG.md LICENSE dist webview-ui out mock .turbo" }, "dependencies": { "@anthropic-ai/bedrock-sdk": "^0.10.2", @@ -343,6 +340,7 @@ "@google/genai": "^0.13.0", "@mistralai/mistralai": "^1.3.6", "@modelcontextprotocol/sdk": "^1.9.0", + "@roo-code/types": "workspace:^", "@qdrant/js-client-rest": "^1.14.0", "@types/lodash.debounce": "^4.0.9", "@vscode/codicons": "^0.0.36", diff --git a/src/schemas/__tests__/index.test.ts b/src/schemas/__tests__/index.test.ts deleted file mode 100644 index 1780ce04a8..0000000000 --- a/src/schemas/__tests__/index.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -// npx jest src/schemas/__tests__/index.test.ts - -import { contributes } from "../../package.json" - -import { GLOBAL_STATE_KEYS, Package, codeActionIds, terminalActionIds, commandIds } from "../index" - -describe("GLOBAL_STATE_KEYS", () => { - it("should contain provider settings keys", () => { - expect(GLOBAL_STATE_KEYS).toContain("autoApprovalEnabled") - }) - - it("should contain provider settings keys", () => { - expect(GLOBAL_STATE_KEYS).toContain("anthropicBaseUrl") - }) - - it("should not contain secret state keys", () => { - expect(GLOBAL_STATE_KEYS).not.toContain("openRouterApiKey") - }) -}) - -describe("package.json#contributes", () => { - it("is in sync with the schema's commands", () => { - // These aren't explicitly referenced in package.json despite - // being registered by the extension. - const absent = new Set([ - "activationCompleted", - "showHumanRelayDialog", - "registerHumanRelayCallback", - "unregisterHumanRelayCallback", - "handleHumanRelayResponse", - ]) - - // This test will notify us if package.json drifts from the schema. - expect(contributes.commands.map((command) => command.command).sort()).toEqual( - [...new Set([...commandIds, ...terminalActionIds, ...codeActionIds])] - .filter((id) => !absent.has(id)) - .map((id) => `${Package.name}.${id}`) - .sort(), - ) - }) -}) diff --git a/src/scripts/generate-types.mts b/src/scripts/generate-types.mts deleted file mode 100644 index fc67c005b2..0000000000 --- a/src/scripts/generate-types.mts +++ /dev/null @@ -1,34 +0,0 @@ -import path from "path" -import fs from "fs" - -import { zodToTs, createTypeAlias, printNode } from "zod-to-ts" -import { $ } from "execa" - -import schemas from "../schemas" - -const { typeDefinitions } = schemas - -async function main() { - const types: string[] = [ - "// This file is automatically generated by running `pnpm --filter roo-cline generate-types`\n// Do not edit it directly.", - ] - - for (const { schema, identifier } of typeDefinitions) { - types.push(printNode(createTypeAlias(zodToTs(schema, identifier).node, identifier))) - types.push(`export type { ${identifier} }`) - } - - fs.writeFileSync("exports/types.ts", types.join("\n\n")) - - await $`npx tsup exports/interface.ts --dts -d out` - fs.copyFileSync("out/interface.d.ts", "exports/roo-code.d.ts") - - await $`npx prettier --write exports/types.ts exports/roo-code.d.ts` - - if (fs.existsSync(path.join("..", "..", "Roo-Code-Types", "src"))) { - fs.copyFileSync("out/interface.js", path.join("..", "..", "Roo-Code-Types", "src", "index.js")) - fs.copyFileSync("out/interface.d.ts", path.join("..", "..", "Roo-Code-Types", "src", "index.d.ts")) - } -} - -main() diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index cd1efbe983..5586e1327b 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -1,6 +1,4 @@ -import { GitCommit } from "../utils/git" - -import { +import type { GlobalSettings, ProviderSettingsEntry, ProviderSettings, @@ -8,17 +6,15 @@ import { ModeConfig, TelemetrySetting, ExperimentId, - ClineAsk, - ClineSay, - ToolProgressStatus, ClineMessage, -} from "../schemas" +} from "@roo-code/types" + +import { GitCommit } from "../utils/git" + import { McpServer } from "./mcp" import { Mode } from "./modes" import { RouterModels } from "./api" -export type { ProviderSettingsEntry, ToolProgressStatus } - export interface LanguageModelChatSelector { vendor?: string family?: string @@ -217,8 +213,6 @@ export type ExtensionState = Pick< autoCondenseContextPercent: number } -export type { ClineMessage, ClineAsk, ClineSay } - export interface ClineSayTool { tool: | "editedExistingFile" diff --git a/src/shared/HistoryItem.ts b/src/shared/HistoryItem.ts deleted file mode 100644 index 0bc5528302..0000000000 --- a/src/shared/HistoryItem.ts +++ /dev/null @@ -1,3 +0,0 @@ -import type { HistoryItem } from "../schemas" - -export type { HistoryItem } diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 85a12aa238..9ce596deb7 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -1,7 +1,8 @@ import { z } from "zod" -import { ProviderSettings } from "./api" -import { Mode, PromptComponent, ModeConfig } from "./modes" +import type { ProviderSettings, PromptComponent, ModeConfig } from "@roo-code/types" + +import { Mode } from "./modes" export type ClineAskResponse = "yesButtonClicked" | "noButtonClicked" | "messageResponse" diff --git a/src/shared/__tests__/api.test.ts b/src/shared/__tests__/api.test.ts index 19dbe1dbcc..b71b68095f 100644 --- a/src/shared/__tests__/api.test.ts +++ b/src/shared/__tests__/api.test.ts @@ -1,14 +1,11 @@ // npx jest src/shared/__tests__/api.test.ts -import { - type ModelInfo, - ProviderSettings, - getModelMaxOutputTokens, - shouldUseReasoningBudget, - shouldUseReasoningEffort, -} from "../api" +import type { ModelInfo, ProviderSettings } from "@roo-code/types" + import { ANTHROPIC_DEFAULT_MAX_TOKENS } from "../../api/providers/constants" +import { getModelMaxOutputTokens, shouldUseReasoningBudget, shouldUseReasoningEffort } from "../api" + describe("getMaxTokensForModel", () => { const modelId = "test" diff --git a/src/shared/__tests__/checkExistApiConfig.test.ts b/src/shared/__tests__/checkExistApiConfig.test.ts index a8e603db8a..218313e7ef 100644 --- a/src/shared/__tests__/checkExistApiConfig.test.ts +++ b/src/shared/__tests__/checkExistApiConfig.test.ts @@ -1,5 +1,8 @@ +// npx jest src/shared/__tests__/checkExistApiConfig.test.ts + +import type { ProviderSettings } from "@roo-code/types" + import { checkExistKey } from "../checkExistApiConfig" -import { ProviderSettings } from "../api" describe("checkExistKey", () => { it("should return false for undefined config", () => { diff --git a/src/shared/__tests__/combineApiRequests.test.ts b/src/shared/__tests__/combineApiRequests.test.ts index 4a939e5f53..04a942eda5 100644 --- a/src/shared/__tests__/combineApiRequests.test.ts +++ b/src/shared/__tests__/combineApiRequests.test.ts @@ -1,7 +1,8 @@ // npx jest src/shared/__tests__/combineApiRequests.test.ts +import type { ClineMessage, ClineSay } from "@roo-code/types" + import { combineApiRequests } from "../combineApiRequests" -import { ClineMessage, ClineSay } from "../ExtensionMessage" describe("combineApiRequests", () => { // Helper function to create a basic api_req_started message diff --git a/src/shared/__tests__/combineCommandSequences.test.ts b/src/shared/__tests__/combineCommandSequences.test.ts index 2bed68ba73..93305b20ce 100644 --- a/src/shared/__tests__/combineCommandSequences.test.ts +++ b/src/shared/__tests__/combineCommandSequences.test.ts @@ -1,6 +1,6 @@ // npx jest src/shared/__tests__/combineCommandSequences.test.ts -import { ClineMessage } from "../ExtensionMessage" +import type { ClineMessage } from "@roo-code/types" import { combineCommandSequences } from "../combineCommandSequences" diff --git a/src/shared/__tests__/experiments.test.ts b/src/shared/__tests__/experiments.test.ts index 9d0e6dab9c..1e7ce0993a 100644 --- a/src/shared/__tests__/experiments.test.ts +++ b/src/shared/__tests__/experiments.test.ts @@ -1,4 +1,8 @@ -import { EXPERIMENT_IDS, experimentConfigsMap, experiments as Experiments, ExperimentId } from "../experiments" +// npx jest src/shared/__tests__/experiments.test.ts + +import type { ExperimentId } from "@roo-code/types" + +import { EXPERIMENT_IDS, experimentConfigsMap, experiments as Experiments } from "../experiments" describe("experiments", () => { describe("POWER_STEERING", () => { diff --git a/src/shared/__tests__/getApiMetrics.test.ts b/src/shared/__tests__/getApiMetrics.test.ts index 4a884f5ea5..52cdc10283 100644 --- a/src/shared/__tests__/getApiMetrics.test.ts +++ b/src/shared/__tests__/getApiMetrics.test.ts @@ -1,7 +1,8 @@ // npx jest src/shared/__tests__/getApiMetrics.test.ts +import type { ClineMessage } from "@roo-code/types" + import { getApiMetrics } from "../getApiMetrics" -import { ClineMessage } from "../ExtensionMessage" describe("getApiMetrics", () => { // Helper function to create a basic api_req_started message diff --git a/src/shared/__tests__/modes.test.ts b/src/shared/__tests__/modes.test.ts index 0e93137e9c..e45417c741 100644 --- a/src/shared/__tests__/modes.test.ts +++ b/src/shared/__tests__/modes.test.ts @@ -1,11 +1,17 @@ +// npx jest src/shared/__tests__/modes.test.ts + +import type { ModeConfig } from "@roo-code/types" + // Mock setup must come before imports jest.mock("vscode") + const mockAddCustomInstructions = jest.fn().mockResolvedValue("Combined instructions") + jest.mock("../../core/prompts/sections/custom-instructions", () => ({ addCustomInstructions: mockAddCustomInstructions, })) -import { isToolAllowedForMode, FileRestrictionError, ModeConfig, getFullModeDetails, modes } from "../modes" +import { isToolAllowedForMode, FileRestrictionError, getFullModeDetails, modes } from "../modes" import { addCustomInstructions } from "../../core/prompts/sections/custom-instructions" describe("isToolAllowedForMode", () => { diff --git a/src/shared/api.ts b/src/shared/api.ts index d66aca6721..48c397d1b4 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -1,7 +1,6 @@ -import { ANTHROPIC_DEFAULT_MAX_TOKENS } from "../api/providers/constants" -import { ModelInfo, ProviderName, ProviderSettings } from "../schemas" +import type { ModelInfo, ProviderSettings } from "@roo-code/types" -export type { ModelInfo, ProviderName, ProviderSettings } +import { ANTHROPIC_DEFAULT_MAX_TOKENS } from "../api/providers/constants" export type ApiHandlerOptions = Omit diff --git a/src/shared/checkExistApiConfig.ts b/src/shared/checkExistApiConfig.ts index 05dcb6e678..4093e8541d 100644 --- a/src/shared/checkExistApiConfig.ts +++ b/src/shared/checkExistApiConfig.ts @@ -1,4 +1,4 @@ -import { SECRET_STATE_KEYS, ProviderSettings } from "../schemas" +import { SECRET_STATE_KEYS, ProviderSettings } from "@roo-code/types" export function checkExistKey(config: ProviderSettings | undefined) { if (!config) { diff --git a/src/shared/combineApiRequests.ts b/src/shared/combineApiRequests.ts index 363ff66051..20ba6bb6aa 100644 --- a/src/shared/combineApiRequests.ts +++ b/src/shared/combineApiRequests.ts @@ -1,4 +1,4 @@ -import { ClineMessage } from "./ExtensionMessage" +import type { ClineMessage } from "@roo-code/types" /** * Combines API request start and finish messages in an array of ClineMessages. diff --git a/src/shared/combineCommandSequences.ts b/src/shared/combineCommandSequences.ts index dd171a77ec..7b37b72c63 100644 --- a/src/shared/combineCommandSequences.ts +++ b/src/shared/combineCommandSequences.ts @@ -1,4 +1,4 @@ -import { ClineMessage } from "./ExtensionMessage" +import type { ClineMessage } from "@roo-code/types" export const COMMAND_OUTPUT_STRING = "Output:" diff --git a/src/utils/cost.ts b/src/shared/cost.ts similarity index 97% rename from src/utils/cost.ts rename to src/shared/cost.ts index 48108b6348..3257cab16c 100644 --- a/src/utils/cost.ts +++ b/src/shared/cost.ts @@ -1,4 +1,4 @@ -import { ModelInfo } from "../shared/api" +import type { ModelInfo } from "@roo-code/types" function calculateApiCostInternal( modelInfo: ModelInfo, diff --git a/src/shared/experiments.ts b/src/shared/experiments.ts index aa2c246bb6..fbcea728ac 100644 --- a/src/shared/experiments.ts +++ b/src/shared/experiments.ts @@ -1,7 +1,4 @@ -import { ExperimentId } from "../schemas" -import { AssertEqual, Equals, Keys, Values } from "../utils/type-fu" - -export type { ExperimentId } +import type { AssertEqual, Equals, Keys, Values, ExperimentId } from "@roo-code/types" export const EXPERIMENT_IDS = { POWER_STEERING: "powerSteering", diff --git a/src/shared/getApiMetrics.ts b/src/shared/getApiMetrics.ts index c728aa563b..49476fdbb6 100644 --- a/src/shared/getApiMetrics.ts +++ b/src/shared/getApiMetrics.ts @@ -1,6 +1,4 @@ -import { TokenUsage } from "../schemas" - -import { ClineMessage } from "./ExtensionMessage" +import type { TokenUsage, ClineMessage } from "@roo-code/types" export type ParsedApiReqStartedTextType = { tokensIn: number diff --git a/src/shared/language.ts b/src/shared/language.ts index 82947ccbcd..4513aa89c9 100644 --- a/src/shared/language.ts +++ b/src/shared/language.ts @@ -1,9 +1,7 @@ -import { type Language, isLanguage } from "../schemas" - -export { type Language, isLanguage } +import { type Language, isLanguage } from "@roo-code/types" /** - * Language name mapping from ISO codes to full language names + * Language name mapping from ISO codes to full language names. */ export const LANGUAGES: Record = { diff --git a/src/shared/modes.ts b/src/shared/modes.ts index 18028791a9..686c0437d3 100644 --- a/src/shared/modes.ts +++ b/src/shared/modes.ts @@ -1,12 +1,13 @@ import * as vscode from "vscode" -import { GroupOptions, GroupEntry, ModeConfig, PromptComponent, CustomModePrompts, ExperimentId } from "../schemas" -import { TOOL_GROUPS, ToolGroup, ALWAYS_AVAILABLE_TOOLS } from "./tools" -import { addCustomInstructions } from "../core/prompts/sections/custom-instructions" -import { EXPERIMENT_IDS } from "./experiments" -export type Mode = string +import type { GroupOptions, GroupEntry, ModeConfig, CustomModePrompts, ExperimentId, ToolGroup } from "@roo-code/types" -export type { GroupOptions, GroupEntry, ModeConfig, PromptComponent, CustomModePrompts } +import { addCustomInstructions } from "../core/prompts/sections/custom-instructions" + +import { EXPERIMENT_IDS } from "./experiments" +import { TOOL_GROUPS, ALWAYS_AVAILABLE_TOOLS } from "./tools" + +export type Mode = string // Helper to extract group name regardless of format export function getGroupName(group: GroupEntry): ToolGroup { diff --git a/src/shared/package.ts b/src/shared/package.ts new file mode 100644 index 0000000000..25588164ec --- /dev/null +++ b/src/shared/package.ts @@ -0,0 +1,19 @@ +/** + * Package + */ + +import { publisher, name, version } from "../package.json" + +// These ENV variables can be defined by ESBuild when building the extension +// in order to override the values in package.json. This allows us to build +// different extension variants with the same package.json file. +// The build process still needs to emit a modified package.json for consumption +// by VSCode, but that build artifact is not used during the transpile step of +// the build, so we still need this override mechanism. +export const Package = { + publisher, + name: process.env.PKG_NAME || name, + version: process.env.PKG_VERSION || version, + outputChannel: process.env.PKG_OUTPUT_CHANNEL || "Roo-Code", + sha: process.env.PKG_SHA, +} as const diff --git a/src/shared/tools.ts b/src/shared/tools.ts index 37ab53516e..6fc32b98c7 100644 --- a/src/shared/tools.ts +++ b/src/shared/tools.ts @@ -1,6 +1,6 @@ import { Anthropic } from "@anthropic-ai/sdk" -import { ClineAsk, ToolProgressStatus, ToolGroup, ToolName } from "../schemas" +import type { ClineAsk, ToolProgressStatus, ToolGroup, ToolName } from "@roo-code/types" export type ToolResponse = string | Array @@ -190,8 +190,6 @@ export const TOOL_DISPLAY_NAMES: Record = { codebase_search: "codebase search", } as const -export type { ToolGroup } - // Define available tool groups. export const TOOL_GROUPS: Record = { read: { diff --git a/src/utils/__tests__/cost.test.ts b/src/utils/__tests__/cost.test.ts index 4501f86b88..3ca22e1801 100644 --- a/src/utils/__tests__/cost.test.ts +++ b/src/utils/__tests__/cost.test.ts @@ -1,5 +1,8 @@ -import { calculateApiCostAnthropic, calculateApiCostOpenAI } from "../cost" -import { ModelInfo } from "../../shared/api" +// npx jest src/utils/__tests__/cost.test.ts + +import type { ModelInfo } from "@roo-code/types" + +import { calculateApiCostAnthropic, calculateApiCostOpenAI } from "../../shared/cost" describe("Cost Utility", () => { describe("calculateApiCostAnthropic", () => { diff --git a/src/utils/__tests__/enhance-prompt.test.ts b/src/utils/__tests__/enhance-prompt.test.ts index 524f99c817..6f4ea2be62 100644 --- a/src/utils/__tests__/enhance-prompt.test.ts +++ b/src/utils/__tests__/enhance-prompt.test.ts @@ -1,5 +1,8 @@ +// npx jest src/utils/__tests__/enhance-prompt.test.ts + +import type { ProviderSettings } from "@roo-code/types" + import { singleCompletionHandler } from "../single-completion-handler" -import { ProviderSettings } from "../../shared/api" import { buildApiHandler, SingleCompletionHandler } from "../../api" import { supportPrompt } from "../../shared/support-prompt" diff --git a/src/utils/commands.ts b/src/utils/commands.ts index cb7d548725..5836c2735f 100644 --- a/src/utils/commands.ts +++ b/src/utils/commands.ts @@ -1,4 +1,6 @@ -import { Package, CommandId, CodeActionId, TerminalActionId } from "../schemas" +import type { CommandId, CodeActionId, TerminalActionId } from "@roo-code/types" + +import { Package } from "../shared/package" export const getCommand = (id: CommandId) => `${Package.name}.${id}` diff --git a/src/utils/single-completion-handler.ts b/src/utils/single-completion-handler.ts index 7434d07e19..4606a17bab 100644 --- a/src/utils/single-completion-handler.ts +++ b/src/utils/single-completion-handler.ts @@ -1,4 +1,5 @@ -import { ProviderSettings } from "../shared/api" +import type { ProviderSettings } from "@roo-code/types" + import { buildApiHandler, SingleCompletionHandler } from "../api" /** diff --git a/src/utils/storage.ts b/src/utils/storage.ts index b4e47c9888..8240588794 100644 --- a/src/utils/storage.ts +++ b/src/utils/storage.ts @@ -2,7 +2,7 @@ import * as vscode from "vscode" import * as path from "path" import * as fs from "fs/promises" -import { Package } from "../schemas" +import { Package } from "../shared/package" import { t } from "../i18n" /** diff --git a/src/utils/type-fu.ts b/src/utils/type-fu.ts deleted file mode 100644 index e7d93b77ac..0000000000 --- a/src/utils/type-fu.ts +++ /dev/null @@ -1,7 +0,0 @@ -export type Keys = keyof T - -export type Values = T[keyof T] - -export type Equals = (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? true : false - -export type AssertEqual = T diff --git a/src/vitest.config.ts b/src/vitest.config.ts index 42d471a0ce..e8e65e5e91 100644 --- a/src/vitest.config.ts +++ b/src/vitest.config.ts @@ -1,8 +1,14 @@ import { defineConfig } from "vitest/config" +import path from "path" export default defineConfig({ test: { include: ["**/__tests__/**/*.spec.ts"], globals: true, }, + resolve: { + alias: { + "@roo-code/types": path.resolve(__dirname, "..", "packages", "types", "src", "index.ts"), + }, + }, }) diff --git a/turbo.json b/turbo.json index b7f7d1fa8d..55ef646fa6 100644 --- a/turbo.json +++ b/turbo.json @@ -3,8 +3,33 @@ "tasks": { "lint": {}, "check-types": {}, - "test": {}, + "test": { + "dependsOn": ["@roo-code/types#build"] + }, "format": {}, - "clean": { "cache": false } + "clean": { + "cache": false + }, + "build": { + "outputs": ["dist/**"], + "inputs": ["src/**", "package.json", "tsconfig.json", "tsup.config.ts"] + }, + "build:nightly": {}, + "bundle": { + "dependsOn": ["^build"], + "cache": false + }, + "bundle:nightly": { + "dependsOn": ["^build"], + "cache": false + }, + "vsix": { + "dependsOn": ["bundle", "@roo-code/vscode-webview#build"], + "cache": false + }, + "vsix:nightly": { + "dependsOn": ["bundle:nightly", "@roo-code/vscode-webview#build:nightly"], + "cache": false + } } } diff --git a/webview-ui/jest.config.cjs b/webview-ui/jest.config.cjs index 8a673da1ce..4a897ef00c 100644 --- a/webview-ui/jest.config.cjs +++ b/webview-ui/jest.config.cjs @@ -12,7 +12,7 @@ module.exports = { "^vscrui$": "/src/__mocks__/vscrui.ts", "^@vscode/webview-ui-toolkit/react$": "/src/__mocks__/@vscode/webview-ui-toolkit/react.ts", "^@/(.*)$": "/src/$1", - "^@roo/(.*)$": "/../src/$1", + "^@roo/(.*)$": "/../src/shared/$1", "^@src/(.*)$": "/src/$1", "^src/i18n/setup$": "/src/__mocks__/i18n/setup.ts", "^\\.\\./setup$": "/src/__mocks__/i18n/setup.ts", diff --git a/webview-ui/package.json b/webview-ui/package.json index cb9e5c91e3..269b8e6e49 100644 --- a/webview-ui/package.json +++ b/webview-ui/package.json @@ -5,10 +5,12 @@ "scripts": { "lint": "eslint src --ext=ts,tsx --max-warnings=0", "check-types": "tsc", + "pretest": "turbo run bundle --cwd ..", "test": "jest -w=40%", "format": "prettier --write src", "dev": "vite", "build": "tsc -b && vite build", + "build:nightly": "tsc -b && vite build --mode nightly", "preview": "vite preview", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", @@ -29,6 +31,7 @@ "@radix-ui/react-slider": "^1.2.3", "@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.8", + "@roo-code/types": "workspace:^", "@tailwindcss/vite": "^4.0.0", "@tanstack/react-query": "^5.68.0", "@vscode/codicons": "^0.0.36", diff --git a/webview-ui/src/App.tsx b/webview-ui/src/App.tsx index 8ca72ecf71..053c9f2456 100644 --- a/webview-ui/src/App.tsx +++ b/webview-ui/src/App.tsx @@ -2,9 +2,9 @@ import { useCallback, useEffect, useRef, useState } from "react" import { useEvent } from "react-use" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" -import { ExtensionMessage } from "@roo/shared/ExtensionMessage" -import TranslationProvider from "./i18n/TranslationContext" +import { ExtensionMessage } from "@roo/ExtensionMessage" +import TranslationProvider from "./i18n/TranslationContext" import { vscode } from "./utils/vscode" import { telemetryClient } from "./utils/TelemetryClient" import { ExtensionStateContextProvider, useExtensionState } from "./context/ExtensionStateContext" diff --git a/webview-ui/src/components/chat/Announcement.tsx b/webview-ui/src/components/chat/Announcement.tsx index 5bce25b786..012829f547 100644 --- a/webview-ui/src/components/chat/Announcement.tsx +++ b/webview-ui/src/components/chat/Announcement.tsx @@ -2,7 +2,7 @@ import { useState, memo } from "react" import { Trans } from "react-i18next" import { VSCodeLink } from "@vscode/webview-ui-toolkit/react" -import { Package } from "@roo/schemas" +import { Package } from "@roo/package" import { useAppTranslation } from "@src/i18n/TranslationContext" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@src/components/ui" diff --git a/webview-ui/src/components/chat/AutoApprovedRequestLimitWarning.tsx b/webview-ui/src/components/chat/AutoApprovedRequestLimitWarning.tsx index 608646b266..1c454e0082 100644 --- a/webview-ui/src/components/chat/AutoApprovedRequestLimitWarning.tsx +++ b/webview-ui/src/components/chat/AutoApprovedRequestLimitWarning.tsx @@ -1,9 +1,11 @@ import React, { memo, useState } from "react" import { VSCodeButton } from "@vscode/webview-ui-toolkit/react" -import { ClineMessage } from "@roo/shared/ExtensionMessage" -import { vscode } from "@src/utils/vscode" import { Trans } from "react-i18next" +import type { ClineMessage } from "@roo-code/types" + +import { vscode } from "@src/utils/vscode" + type AutoApprovedRequestLimitWarningProps = { message: ClineMessage } diff --git a/webview-ui/src/components/chat/BrowserSessionRow.tsx b/webview-ui/src/components/chat/BrowserSessionRow.tsx index e4aa54fdad..cdb15315dd 100644 --- a/webview-ui/src/components/chat/BrowserSessionRow.tsx +++ b/webview-ui/src/components/chat/BrowserSessionRow.tsx @@ -4,10 +4,12 @@ import deepEqual from "fast-deep-equal" import { useTranslation } from "react-i18next" import { VSCodeButton } from "@vscode/webview-ui-toolkit/react" -import { BrowserAction, BrowserActionResult, ClineMessage, ClineSayBrowserAction } from "@roo/shared/ExtensionMessage" +import type { ClineMessage } from "@roo-code/types" + +import { BrowserAction, BrowserActionResult, ClineSayBrowserAction } from "@roo/ExtensionMessage" -import { useExtensionState } from "@src/context/ExtensionStateContext" import { vscode } from "@src/utils/vscode" +import { useExtensionState } from "@src/context/ExtensionStateContext" import CodeBlock, { CODE_BLOCK_BG_COLOR } from "../common/CodeBlock" import { ChatRowContent } from "./ChatRow" diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index f9522901b7..e6b8bb601a 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -4,9 +4,11 @@ import { useTranslation, Trans } from "react-i18next" import deepEqual from "fast-deep-equal" import { VSCodeBadge, VSCodeButton } from "@vscode/webview-ui-toolkit/react" -import { ClineApiReqInfo, ClineAskUseMcpServer, ClineMessage, ClineSayTool } from "@roo/shared/ExtensionMessage" -import { COMMAND_OUTPUT_STRING } from "@roo/shared/combineCommandSequences" -import { safeJsonParse } from "@roo/shared/safeJsonParse" +import type { ClineMessage } from "@roo-code/types" + +import { ClineApiReqInfo, ClineAskUseMcpServer, ClineSayTool } from "@roo/ExtensionMessage" +import { COMMAND_OUTPUT_STRING } from "@roo/combineCommandSequences" +import { safeJsonParse } from "@roo/safeJsonParse" import { useCopyToClipboard } from "@src/utils/clipboard" import { useExtensionState } from "@src/context/ExtensionStateContext" diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index 0df1a90f60..5d8e0a2112 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -2,10 +2,10 @@ import React, { forwardRef, useCallback, useEffect, useLayoutEffect, useMemo, us import { useEvent } from "react-use" import DynamicTextArea from "react-textarea-autosize" -import { mentionRegex, mentionRegexGlobal, unescapeSpaces } from "@roo/shared/context-mentions" -import { WebviewMessage } from "@roo/shared/WebviewMessage" -import { Mode, getAllModes } from "@roo/shared/modes" -import { ExtensionMessage } from "@roo/shared/ExtensionMessage" +import { mentionRegex, mentionRegexGlobal, unescapeSpaces } from "@roo/context-mentions" +import { WebviewMessage } from "@roo/WebviewMessage" +import { Mode, getAllModes } from "@roo/modes" +import { ExtensionMessage } from "@roo/ExtensionMessage" import { vscode } from "@/utils/vscode" import { useExtensionState } from "@/context/ExtensionStateContext" diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index 0b39baf39a..6eaceb1374 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -7,32 +7,29 @@ import { Trans } from "react-i18next" import { VSCodeButton } from "@vscode/webview-ui-toolkit/react" import useSound from "use-sound" -import { - ClineAsk, - ClineMessage, - ClineSayBrowserAction, - ClineSayTool, - ExtensionMessage, -} from "@roo/shared/ExtensionMessage" -import { McpServer, McpTool } from "@roo/shared/mcp" -import { findLast } from "@roo/shared/array" -import { combineApiRequests } from "@roo/shared/combineApiRequests" -import { combineCommandSequences } from "@roo/shared/combineCommandSequences" -import { getApiMetrics } from "@roo/shared/getApiMetrics" -import { AudioType } from "@roo/shared/WebviewMessage" -import { getAllModes } from "@roo/shared/modes" +import type { ClineAsk, ClineMessage } from "@roo-code/types" + +import { ClineSayBrowserAction, ClineSayTool, ExtensionMessage } from "@roo/ExtensionMessage" +import { McpServer, McpTool } from "@roo/mcp" +import { findLast } from "@roo/array" +import { combineApiRequests } from "@roo/combineApiRequests" +import { combineCommandSequences } from "@roo/combineCommandSequences" +import { getApiMetrics } from "@roo/getApiMetrics" +import { AudioType } from "@roo/WebviewMessage" +import { getAllModes } from "@roo/modes" -import { useExtensionState } from "@src/context/ExtensionStateContext" import { vscode } from "@src/utils/vscode" -import { useSelectedModel } from "@/components/ui/hooks/useSelectedModel" import { validateCommand } from "@src/utils/command-validation" +import { buildDocLink } from "@src/utils/docLinks" import { useAppTranslation } from "@src/i18n/TranslationContext" +import { useExtensionState } from "@src/context/ExtensionStateContext" +import { useSelectedModel } from "@src/components/ui/hooks/useSelectedModel" +import RooHero from "@src/components/welcome/RooHero" +import RooTips from "@src/components/welcome/RooTips" import TelemetryBanner from "../common/TelemetryBanner" import { useTaskSearch } from "../history/useTaskSearch" import HistoryPreview from "../history/HistoryPreview" -import RooHero from "@src/components/welcome/RooHero" -import RooTips from "@src/components/welcome/RooTips" import Announcement from "./Announcement" import BrowserSessionRow from "./BrowserSessionRow" import ChatRow from "./ChatRow" @@ -41,7 +38,6 @@ import TaskHeader from "./TaskHeader" import AutoApproveMenu from "./AutoApproveMenu" import SystemPromptWarning from "./SystemPromptWarning" import { CheckpointWarning } from "./CheckpointWarning" -import { buildDocLink } from "@src/utils/docLinks" export interface ChatViewProps { isHidden: boolean diff --git a/webview-ui/src/components/chat/CommandExecution.tsx b/webview-ui/src/components/chat/CommandExecution.tsx index d1142addd1..8c92ec7e7b 100644 --- a/webview-ui/src/components/chat/CommandExecution.tsx +++ b/webview-ui/src/components/chat/CommandExecution.tsx @@ -2,10 +2,11 @@ import { useCallback, useState, memo, useMemo } from "react" import { useEvent } from "react-use" import { ChevronDown, Skull } from "lucide-react" -import { CommandExecutionStatus, commandExecutionStatusSchema } from "@roo/schemas" -import { ExtensionMessage } from "@roo/shared/ExtensionMessage" -import { safeJsonParse } from "@roo/shared/safeJsonParse" -import { COMMAND_OUTPUT_STRING } from "@roo/shared/combineCommandSequences" +import { CommandExecutionStatus, commandExecutionStatusSchema } from "@roo-code/types" + +import { ExtensionMessage } from "@roo/ExtensionMessage" +import { safeJsonParse } from "@roo/safeJsonParse" +import { COMMAND_OUTPUT_STRING } from "@roo/combineCommandSequences" import { vscode } from "@src/utils/vscode" import { useExtensionState } from "@src/context/ExtensionStateContext" diff --git a/webview-ui/src/components/chat/ContextCondenseRow.tsx b/webview-ui/src/components/chat/ContextCondenseRow.tsx index fa52d853ad..6a80208770 100644 --- a/webview-ui/src/components/chat/ContextCondenseRow.tsx +++ b/webview-ui/src/components/chat/ContextCondenseRow.tsx @@ -2,7 +2,8 @@ import { useState } from "react" import { useTranslation } from "react-i18next" import { VSCodeBadge } from "@vscode/webview-ui-toolkit/react" -import { ContextCondense } from "@roo/schemas" +import type { ContextCondense } from "@roo-code/types" + import { Markdown } from "./Markdown" import { ProgressIndicator } from "./ProgressIndicator" diff --git a/webview-ui/src/components/chat/ContextMenu.tsx b/webview-ui/src/components/chat/ContextMenu.tsx index 2983c023f2..1672c35ee3 100644 --- a/webview-ui/src/components/chat/ContextMenu.tsx +++ b/webview-ui/src/components/chat/ContextMenu.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useMemo, useRef, useState } from "react" import { getIconForFilePath, getIconUrlByName, getIconForDirectoryPath } from "vscode-material-icons" -import { ModeConfig } from "@roo/shared/modes" +import type { ModeConfig } from "@roo-code/types" import { ContextMenuOptionType, diff --git a/webview-ui/src/components/chat/Mention.tsx b/webview-ui/src/components/chat/Mention.tsx index 188bb36332..764c027203 100644 --- a/webview-ui/src/components/chat/Mention.tsx +++ b/webview-ui/src/components/chat/Mention.tsx @@ -1,4 +1,4 @@ -import { mentionRegexGlobal } from "@roo/shared/context-mentions" +import { mentionRegexGlobal } from "@roo/context-mentions" import { vscode } from "../../utils/vscode" diff --git a/webview-ui/src/components/chat/TaskActions.tsx b/webview-ui/src/components/chat/TaskActions.tsx index d143a9aa1c..2eebc6ccb3 100644 --- a/webview-ui/src/components/chat/TaskActions.tsx +++ b/webview-ui/src/components/chat/TaskActions.tsx @@ -2,8 +2,9 @@ import { useState } from "react" import prettyBytes from "pretty-bytes" import { useTranslation } from "react-i18next" +import type { HistoryItem } from "@roo-code/types" + import { vscode } from "@/utils/vscode" -import { HistoryItem } from "@roo/shared/HistoryItem" import { DeleteTaskDialog } from "../history/DeleteTaskDialog" import { IconButton } from "./IconButton" diff --git a/webview-ui/src/components/chat/TaskHeader.tsx b/webview-ui/src/components/chat/TaskHeader.tsx index 038a903d04..a46035c9b4 100644 --- a/webview-ui/src/components/chat/TaskHeader.tsx +++ b/webview-ui/src/components/chat/TaskHeader.tsx @@ -4,8 +4,9 @@ import { useTranslation } from "react-i18next" import { VSCodeBadge } from "@vscode/webview-ui-toolkit/react" import { CloudUpload, CloudDownload } from "lucide-react" -import { ClineMessage } from "@roo/shared/ExtensionMessage" -import { getModelMaxOutputTokens } from "@roo/shared/api" +import type { ClineMessage } from "@roo-code/types" + +import { getModelMaxOutputTokens } from "@roo/api" import { formatLargeNumber } from "@src/utils/format" import { cn } from "@src/lib/utils" diff --git a/webview-ui/src/components/chat/__tests__/Announcement.test.tsx b/webview-ui/src/components/chat/__tests__/Announcement.test.tsx index 70cc8ac506..d2f109f365 100644 --- a/webview-ui/src/components/chat/__tests__/Announcement.test.tsx +++ b/webview-ui/src/components/chat/__tests__/Announcement.test.tsx @@ -1,7 +1,7 @@ import { render, screen } from "@testing-library/react" import { jest } from "@jest/globals" // Or 'jest' if using Jest -import { Package } from "@roo/schemas" +import { Package } from "@roo/package" import Announcement from "../Announcement" diff --git a/webview-ui/src/components/chat/__tests__/ChatTextArea.test.tsx b/webview-ui/src/components/chat/__tests__/ChatTextArea.test.tsx index 204821c902..8b09a5eb87 100644 --- a/webview-ui/src/components/chat/__tests__/ChatTextArea.test.tsx +++ b/webview-ui/src/components/chat/__tests__/ChatTextArea.test.tsx @@ -2,7 +2,7 @@ import { render, fireEvent, screen } from "@testing-library/react" import ChatTextArea from "../ChatTextArea" import { useExtensionState } from "@src/context/ExtensionStateContext" import { vscode } from "@src/utils/vscode" -import { defaultModeSlug } from "@roo/shared/modes" +import { defaultModeSlug } from "@roo/modes" import * as pathMentions from "@src/utils/path-mentions" // Mock modules diff --git a/webview-ui/src/components/chat/__tests__/TaskHeader.test.tsx b/webview-ui/src/components/chat/__tests__/TaskHeader.test.tsx index 27704a2ce5..12f43b8bea 100644 --- a/webview-ui/src/components/chat/__tests__/TaskHeader.test.tsx +++ b/webview-ui/src/components/chat/__tests__/TaskHeader.test.tsx @@ -4,7 +4,7 @@ import React from "react" import { render, screen } from "@testing-library/react" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" -import { ProviderSettings } from "@roo/shared/api" +import type { ProviderSettings } from "@roo-code/types" import TaskHeader, { TaskHeaderProps } from "../TaskHeader" diff --git a/webview-ui/src/components/common/CodeAccordian.tsx b/webview-ui/src/components/common/CodeAccordian.tsx index c3e44e7713..b42d0e2f7e 100644 --- a/webview-ui/src/components/common/CodeAccordian.tsx +++ b/webview-ui/src/components/common/CodeAccordian.tsx @@ -1,7 +1,8 @@ import { memo, useMemo } from "react" import { VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react" -import { type ToolProgressStatus } from "@roo/shared/ExtensionMessage" +import type { ToolProgressStatus } from "@roo-code/types" + import { getLanguageFromPath } from "@src/utils/getLanguageFromPath" import { removeLeadingNonAlphanumeric } from "@src/utils/removeLeadingNonAlphanumeric" diff --git a/webview-ui/src/components/common/TelemetryBanner.tsx b/webview-ui/src/components/common/TelemetryBanner.tsx index dac980489c..6ce2f79994 100644 --- a/webview-ui/src/components/common/TelemetryBanner.tsx +++ b/webview-ui/src/components/common/TelemetryBanner.tsx @@ -1,11 +1,13 @@ -import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react" import { memo, useState } from "react" +import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react" import styled from "styled-components" -import { vscode } from "@src/utils/vscode" -import { TelemetrySetting } from "@roo/shared/TelemetrySetting" -import { useAppTranslation } from "@src/i18n/TranslationContext" import { Trans } from "react-i18next" +import { TelemetrySetting } from "@roo/TelemetrySetting" + +import { vscode } from "@src/utils/vscode" +import { useAppTranslation } from "@src/i18n/TranslationContext" + const BannerContainer = styled.div` background-color: var(--vscode-banner-background); padding: 12px 20px; diff --git a/webview-ui/src/components/mcp/McpErrorRow.tsx b/webview-ui/src/components/mcp/McpErrorRow.tsx index 3b1ea8f7ce..cebd8a4d4b 100644 --- a/webview-ui/src/components/mcp/McpErrorRow.tsx +++ b/webview-ui/src/components/mcp/McpErrorRow.tsx @@ -1,7 +1,7 @@ import { useMemo } from "react" import { formatRelative } from "date-fns" -import type { McpErrorEntry } from "@roo/shared/mcp" +import type { McpErrorEntry } from "@roo/mcp" type McpErrorRowProps = { error: McpErrorEntry diff --git a/webview-ui/src/components/mcp/McpResourceRow.tsx b/webview-ui/src/components/mcp/McpResourceRow.tsx index b31082dafb..651a569a3c 100644 --- a/webview-ui/src/components/mcp/McpResourceRow.tsx +++ b/webview-ui/src/components/mcp/McpResourceRow.tsx @@ -1,4 +1,4 @@ -import { McpResource, McpResourceTemplate } from "@roo/shared/mcp" +import { McpResource, McpResourceTemplate } from "@roo/mcp" type McpResourceRowProps = { item: McpResource | McpResourceTemplate diff --git a/webview-ui/src/components/mcp/McpToolRow.tsx b/webview-ui/src/components/mcp/McpToolRow.tsx index 2cab9b31ee..507933ddf1 100644 --- a/webview-ui/src/components/mcp/McpToolRow.tsx +++ b/webview-ui/src/components/mcp/McpToolRow.tsx @@ -1,5 +1,7 @@ import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react" -import { McpTool } from "@roo/shared/mcp" + +import { McpTool } from "@roo/mcp" + import { useAppTranslation } from "@src/i18n/TranslationContext" import { vscode } from "@src/utils/vscode" diff --git a/webview-ui/src/components/mcp/McpView.tsx b/webview-ui/src/components/mcp/McpView.tsx index 7b6a89799b..d486d8b93c 100644 --- a/webview-ui/src/components/mcp/McpView.tsx +++ b/webview-ui/src/components/mcp/McpView.tsx @@ -9,7 +9,7 @@ import { VSCodePanelView, } from "@vscode/webview-ui-toolkit/react" -import { McpServer } from "@roo/shared/mcp" +import { McpServer } from "@roo/mcp" import { vscode } from "@src/utils/vscode" import { useExtensionState } from "@src/context/ExtensionStateContext" diff --git a/webview-ui/src/components/prompts/PromptsView.tsx b/webview-ui/src/components/prompts/PromptsView.tsx index c47a51e6d7..1f175154e1 100644 --- a/webview-ui/src/components/prompts/PromptsView.tsx +++ b/webview-ui/src/components/prompts/PromptsView.tsx @@ -1,5 +1,4 @@ import React, { useState, useEffect, useMemo, useCallback, useRef } from "react" -import { Button } from "@/components/ui/button" import { VSCodeCheckbox, VSCodeRadioGroup, @@ -7,28 +6,22 @@ import { VSCodeTextArea, VSCodeLink, } from "@vscode/webview-ui-toolkit/react" - -import { useExtensionState } from "@src/context/ExtensionStateContext" -import { - Mode, - PromptComponent, - getRoleDefinition, - getWhenToUse, - getCustomInstructions, - getAllModes, - ModeConfig, - GroupEntry, -} from "@roo/shared/modes" -import { modeConfigSchema } from "@roo/schemas" -import { supportPrompt, SupportPromptType } from "@roo/shared/support-prompt" - -import { TOOL_GROUPS, ToolGroup } from "@roo/shared/tools" -import { vscode } from "@src/utils/vscode" -import { Tab, TabContent, TabHeader } from "../common/Tab" -import { useAppTranslation } from "@src/i18n/TranslationContext" import { Trans } from "react-i18next" +import { ChevronsUpDown, X } from "lucide-react" + +import { ModeConfig, GroupEntry, PromptComponent, ToolGroup, modeConfigSchema } from "@roo-code/types" + +import { Mode, getRoleDefinition, getWhenToUse, getCustomInstructions, getAllModes } from "@roo/modes" +import { supportPrompt, SupportPromptType } from "@roo/support-prompt" +import { TOOL_GROUPS } from "@roo/tools" + +import { vscode } from "@src/utils/vscode" import { buildDocLink } from "@src/utils/docLinks" +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { useExtensionState } from "@src/context/ExtensionStateContext" +import { Tab, TabContent, TabHeader } from "@src/components/common/Tab" import { + Button, Select, SelectContent, SelectItem, @@ -44,8 +37,7 @@ import { CommandItem, CommandGroup, Input, -} from "../ui" -import { ChevronsUpDown, X } from "lucide-react" +} from "@src/components/ui" // Get all available groups that should show in prompts view const availableGroups = (Object.keys(TOOL_GROUPS) as ToolGroup[]).filter((group) => !TOOL_GROUPS[group].alwaysAvailable) diff --git a/webview-ui/src/components/settings/About.tsx b/webview-ui/src/components/settings/About.tsx index fb045932c0..bfffeb1611 100644 --- a/webview-ui/src/components/settings/About.tsx +++ b/webview-ui/src/components/settings/About.tsx @@ -5,8 +5,8 @@ import { Info, Download, Upload, TriangleAlert } from "lucide-react" import { VSCodeCheckbox, VSCodeLink } from "@vscode/webview-ui-toolkit/react" -import { Package } from "@roo/schemas" -import { TelemetrySetting } from "@roo/shared/TelemetrySetting" +import { Package } from "@roo/package" +import { TelemetrySetting } from "@roo/TelemetrySetting" import { vscode } from "@/utils/vscode" import { cn } from "@/lib/utils" diff --git a/webview-ui/src/components/settings/ApiConfigManager.tsx b/webview-ui/src/components/settings/ApiConfigManager.tsx index 05b14bc586..54e3725e44 100644 --- a/webview-ui/src/components/settings/ApiConfigManager.tsx +++ b/webview-ui/src/components/settings/ApiConfigManager.tsx @@ -2,7 +2,7 @@ import { memo, useEffect, useRef, useState } from "react" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" import { ChevronsUpDown, Check, X } from "lucide-react" -import { ProviderSettingsEntry } from "@roo/shared/ExtensionMessage" +import type { ProviderSettingsEntry } from "@roo-code/types" import { useAppTranslation } from "@/i18n/TranslationContext" import { cn } from "@/lib/utils" diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 20d10cf459..bef4e45f20 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -3,15 +3,15 @@ import { convertHeadersToObject } from "./utils/headers" import { useDebounce } from "react-use" import { VSCodeLink } from "@vscode/webview-ui-toolkit/react" +import type { ProviderName, ProviderSettings } from "@roo-code/types" + import { - type ProviderName, - type ProviderSettings, openRouterDefaultModelId, requestyDefaultModelId, glamaDefaultModelId, unboundDefaultModelId, litellmDefaultModelId, -} from "@roo/shared/api" +} from "@roo/api" import { vscode } from "@src/utils/vscode" import { validateApiConfiguration, validateModelId } from "@src/utils/validate" diff --git a/webview-ui/src/components/settings/AutoApproveToggle.tsx b/webview-ui/src/components/settings/AutoApproveToggle.tsx index 17307e8367..ffad47e2ac 100644 --- a/webview-ui/src/components/settings/AutoApproveToggle.tsx +++ b/webview-ui/src/components/settings/AutoApproveToggle.tsx @@ -1,4 +1,4 @@ -import type { GlobalSettings } from "@roo/schemas" +import type { GlobalSettings } from "@roo-code/types" import { useAppTranslation } from "@/i18n/TranslationContext" import { cn } from "@/lib/utils" diff --git a/webview-ui/src/components/settings/CodeIndexSettings.tsx b/webview-ui/src/components/settings/CodeIndexSettings.tsx index 06bac1927d..45ed5a9087 100644 --- a/webview-ui/src/components/settings/CodeIndexSettings.tsx +++ b/webview-ui/src/components/settings/CodeIndexSettings.tsx @@ -1,11 +1,23 @@ import React, { useState, useEffect } from "react" +import { z } from "zod" import * as ProgressPrimitive from "@radix-ui/react-progress" -import { Trans } from "react-i18next" -import { useAppTranslation } from "@/i18n/TranslationContext" - import { VSCodeCheckbox, VSCodeTextField, VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react" -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { Trans } from "react-i18next" + +import { CodebaseIndexConfig, CodebaseIndexModels, ProviderSettings } from "@roo-code/types" + +import { EmbedderProvider } from "@roo/embeddingModels" + +import { vscode } from "@src/utils/vscode" +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { buildDocLink } from "@src/utils/docLinks" + import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, AlertDialog, AlertDialogAction, AlertDialogCancel, @@ -15,13 +27,7 @@ import { AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, -} from "@/components/ui/alert-dialog" - -import { vscode } from "@/utils/vscode" -import { buildDocLink } from "@/utils/docLinks" -import { CodebaseIndexConfig, CodebaseIndexModels, ProviderSettings } from "../../../../src/schemas" -import { EmbedderProvider } from "../../../../src/shared/embeddingModels" -import { z } from "zod" +} from "@src/components/ui" import { SetCachedStateField } from "./types" diff --git a/webview-ui/src/components/settings/ExperimentalSettings.tsx b/webview-ui/src/components/settings/ExperimentalSettings.tsx index 36b5c86a97..ee7cde49fb 100644 --- a/webview-ui/src/components/settings/ExperimentalSettings.tsx +++ b/webview-ui/src/components/settings/ExperimentalSettings.tsx @@ -1,21 +1,22 @@ import { HTMLAttributes } from "react" -import { useAppTranslation } from "@/i18n/TranslationContext" +import { VSCodeTextArea } from "@vscode/webview-ui-toolkit/react" import { FlaskConical } from "lucide-react" -import { EXPERIMENT_IDS, experimentConfigsMap, ExperimentId } from "@roo/shared/experiments" +import type { ExperimentId, CodebaseIndexConfig, CodebaseIndexModels, ProviderSettings } from "@roo-code/types" -import { cn } from "@/lib/utils" -import { vscode } from "@/utils/vscode" +import { EXPERIMENT_IDS, experimentConfigsMap } from "@roo/experiments" + +import { vscode } from "@src/utils/vscode" +import { ExtensionStateContextType } from "@src/context/ExtensionStateContext" +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { cn } from "@src/lib/utils" +import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Slider } from "@src/components/ui" import { SetCachedStateField, SetExperimentEnabled } from "./types" import { SectionHeader } from "./SectionHeader" import { Section } from "./Section" import { ExperimentalFeature } from "./ExperimentalFeature" -import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Slider } from "@/components/ui/" -import { VSCodeTextArea } from "@vscode/webview-ui-toolkit/react" -import { CodebaseIndexConfig, CodebaseIndexModels, ProviderSettings } from "../../../../src/schemas" import { CodeIndexSettings } from "./CodeIndexSettings" -import { ExtensionStateContextType } from "../../context/ExtensionStateContext" const SUMMARY_PROMPT = `\ Your task is to create a detailed summary of the conversation so far, paying close attention to the user's explicit requests and your previous actions. diff --git a/webview-ui/src/components/settings/LanguageSettings.tsx b/webview-ui/src/components/settings/LanguageSettings.tsx index b7dd141fbf..5745d78b9b 100644 --- a/webview-ui/src/components/settings/LanguageSettings.tsx +++ b/webview-ui/src/components/settings/LanguageSettings.tsx @@ -2,9 +2,12 @@ import { HTMLAttributes } from "react" import { useAppTranslation } from "@/i18n/TranslationContext" import { Globe } from "lucide-react" -import { cn } from "@/lib/utils" -import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/ui" -import { Language, LANGUAGES } from "@roo/shared/language" +import type { Language } from "@roo-code/types" + +import { LANGUAGES } from "@roo/language" + +import { cn } from "@src/lib/utils" +import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui" import { SetCachedStateField } from "./types" import { SectionHeader } from "./SectionHeader" diff --git a/webview-ui/src/components/settings/ModelInfoView.tsx b/webview-ui/src/components/settings/ModelInfoView.tsx index 7920469c19..d940e66d42 100644 --- a/webview-ui/src/components/settings/ModelInfoView.tsx +++ b/webview-ui/src/components/settings/ModelInfoView.tsx @@ -1,10 +1,10 @@ import { VSCodeLink } from "@vscode/webview-ui-toolkit/react" -import { useAppTranslation } from "@/i18n/TranslationContext" -import { formatPrice } from "@/utils/formatPrice" -import { cn } from "@/lib/utils" +import type { ModelInfo } from "@roo-code/types" -import { ModelInfo } from "@roo/shared/api" +import { formatPrice } from "@src/utils/formatPrice" +import { cn } from "@src/lib/utils" +import { useAppTranslation } from "@src/i18n/TranslationContext" import { ModelDescriptionMarkdown } from "./ModelDescriptionMarkdown" diff --git a/webview-ui/src/components/settings/ModelPicker.tsx b/webview-ui/src/components/settings/ModelPicker.tsx index 4ac7f530a6..96ac4e0dec 100644 --- a/webview-ui/src/components/settings/ModelPicker.tsx +++ b/webview-ui/src/components/settings/ModelPicker.tsx @@ -3,7 +3,7 @@ import { VSCodeLink } from "@vscode/webview-ui-toolkit/react" import { Trans } from "react-i18next" import { ChevronsUpDown, Check, X } from "lucide-react" -import { ProviderSettings, ModelInfo } from "@roo/schemas" +import type { ProviderSettings, ModelInfo } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { useSelectedModel } from "@/components/ui/hooks/useSelectedModel" diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 16770a15e2..8243632469 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -9,7 +9,6 @@ import React, { useRef, useState, } from "react" -import { useAppTranslation } from "@/i18n/TranslationContext" import { CheckCheck, SquareMousePointer, @@ -25,12 +24,13 @@ import { LucideIcon, } from "lucide-react" -import { ExperimentId } from "@roo/shared/experiments" -import { TelemetrySetting } from "@roo/shared/TelemetrySetting" -import { ProviderSettings } from "@roo/shared/api" +import type { ProviderSettings, ExperimentId } from "@roo-code/types" -import { vscode } from "@/utils/vscode" -import { ExtensionStateContextType, useExtensionState } from "@/context/ExtensionStateContext" +import { TelemetrySetting } from "@roo/TelemetrySetting" + +import { vscode } from "@src/utils/vscode" +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { ExtensionStateContextType, useExtensionState } from "@src/context/ExtensionStateContext" import { AlertDialog, AlertDialogContent, @@ -45,7 +45,7 @@ import { TooltipContent, TooltipProvider, TooltipTrigger, -} from "@/components/ui" +} from "@src/components/ui" import { Tab, TabContent, TabHeader, TabList, TabTrigger } from "../common/Tab" import { SetCachedStateField, SetExperimentEnabled } from "./types" diff --git a/webview-ui/src/components/settings/TerminalSettings.tsx b/webview-ui/src/components/settings/TerminalSettings.tsx index 59c434fbb2..2808fb03be 100644 --- a/webview-ui/src/components/settings/TerminalSettings.tsx +++ b/webview-ui/src/components/settings/TerminalSettings.tsx @@ -7,7 +7,7 @@ import { Trans } from "react-i18next" import { buildDocLink } from "@src/utils/docLinks" import { useEvent, useMount } from "react-use" -import { ExtensionMessage } from "@roo/shared/ExtensionMessage" +import { ExtensionMessage } from "@roo/ExtensionMessage" import { cn } from "@/lib/utils" import { Slider } from "@/components/ui" diff --git a/webview-ui/src/components/settings/ThinkingBudget.tsx b/webview-ui/src/components/settings/ThinkingBudget.tsx index a33ed13d68..456e0be17a 100644 --- a/webview-ui/src/components/settings/ThinkingBudget.tsx +++ b/webview-ui/src/components/settings/ThinkingBudget.tsx @@ -1,13 +1,9 @@ import { useEffect } from "react" import { Checkbox } from "vscrui" -import { reasoningEfforts, ReasoningEffort } from "@roo/schemas" -import { - type ProviderSettings, - type ModelInfo, - DEFAULT_HYBRID_REASONING_MODEL_MAX_TOKENS, - DEFAULT_HYBRID_REASONING_MODEL_THINKING_TOKENS, -} from "@roo/shared/api" +import { type ProviderSettings, type ModelInfo, type ReasoningEffort, reasoningEfforts } from "@roo-code/types" + +import { DEFAULT_HYBRID_REASONING_MODEL_MAX_TOKENS, DEFAULT_HYBRID_REASONING_MODEL_THINKING_TOKENS } from "@roo/api" import { useAppTranslation } from "@src/i18n/TranslationContext" import { Slider, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui" diff --git a/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx b/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx index 19d707a009..ca0eb5f7fe 100644 --- a/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx +++ b/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx @@ -3,9 +3,11 @@ import { render, screen, fireEvent } from "@testing-library/react" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" -import { ModelInfo, ProviderSettings, openAiModelInfoSaneDefaults } from "@roo/shared/api" +import type { ModelInfo, ProviderSettings } from "@roo-code/types" -import { ExtensionStateContextProvider } from "@/context/ExtensionStateContext" +import { openAiModelInfoSaneDefaults } from "@roo/api" + +import { ExtensionStateContextProvider } from "@src/context/ExtensionStateContext" import ApiOptions, { ApiOptionsProps } from "../ApiOptions" @@ -146,6 +148,22 @@ jest.mock("../DiffSettingsControl", () => ({ ), })) +// Mock ThinkingBudget component +jest.mock("../ThinkingBudget", () => ({ + ThinkingBudget: ({ modelInfo }: any) => { + // Only render if model supports reasoning budget (thinking models) + if (modelInfo?.supportsReasoningBudget || modelInfo?.requiredReasoningBudget) { + return ( +

+
Max Thinking Tokens
+ +
+ ) + } + return null + }, +})) + // Mock LiteLLM provider for tests jest.mock("../providers/LiteLLM", () => ({ LiteLLM: ({ apiConfiguration, setApiConfigurationField }: any) => ( diff --git a/webview-ui/src/components/settings/__tests__/ModelPicker.test.tsx b/webview-ui/src/components/settings/__tests__/ModelPicker.test.tsx index 710ca3e5ef..6d64130cf1 100644 --- a/webview-ui/src/components/settings/__tests__/ModelPicker.test.tsx +++ b/webview-ui/src/components/settings/__tests__/ModelPicker.test.tsx @@ -4,7 +4,7 @@ import { screen, fireEvent, render } from "@testing-library/react" import { act } from "react" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" -import { ModelInfo } from "@roo/schemas" +import { ModelInfo } from "@roo-code/types" import { ModelPicker } from "../ModelPicker" diff --git a/webview-ui/src/components/settings/__tests__/ThinkingBudget.test.tsx b/webview-ui/src/components/settings/__tests__/ThinkingBudget.test.tsx index 4e448f7cb1..60f2c29497 100644 --- a/webview-ui/src/components/settings/__tests__/ThinkingBudget.test.tsx +++ b/webview-ui/src/components/settings/__tests__/ThinkingBudget.test.tsx @@ -2,7 +2,7 @@ import { render, screen, fireEvent } from "@testing-library/react" -import { ModelInfo } from "@roo/shared/api" +import type { ModelInfo } from "@roo-code/types" import { ThinkingBudget } from "../ThinkingBudget" diff --git a/webview-ui/src/components/settings/constants.ts b/webview-ui/src/components/settings/constants.ts index 295088f9de..bd1ce69eb6 100644 --- a/webview-ui/src/components/settings/constants.ts +++ b/webview-ui/src/components/settings/constants.ts @@ -1,6 +1,6 @@ +import type { ProviderName, ModelInfo } from "@roo-code/types" + import { - ProviderName, - ModelInfo, anthropicModels, bedrockModels, deepSeekModels, @@ -11,9 +11,9 @@ import { xaiModels, groqModels, chutesModels, -} from "@roo/shared/api" +} from "@roo/api" -export { AWS_REGIONS } from "@roo/shared/aws_regions" +export { AWS_REGIONS } from "@roo/aws_regions" export const MODELS_BY_PROVIDER: Partial>> = { anthropic: anthropicModels, diff --git a/webview-ui/src/components/settings/providers/Anthropic.tsx b/webview-ui/src/components/settings/providers/Anthropic.tsx index 4d5957b562..f340e73f72 100644 --- a/webview-ui/src/components/settings/providers/Anthropic.tsx +++ b/webview-ui/src/components/settings/providers/Anthropic.tsx @@ -2,7 +2,7 @@ import { useCallback, useState } from "react" import { Checkbox } from "vscrui" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { ProviderSettings } from "@roo/shared/api" +import type { ProviderSettings } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" diff --git a/webview-ui/src/components/settings/providers/Bedrock.tsx b/webview-ui/src/components/settings/providers/Bedrock.tsx index 2ddc5797ca..a672ff406e 100644 --- a/webview-ui/src/components/settings/providers/Bedrock.tsx +++ b/webview-ui/src/components/settings/providers/Bedrock.tsx @@ -2,7 +2,7 @@ import { useCallback } from "react" import { Checkbox } from "vscrui" import { VSCodeTextField, VSCodeRadio, VSCodeRadioGroup } from "@vscode/webview-ui-toolkit/react" -import { ProviderSettings, ModelInfo } from "@roo/shared/api" +import type { ProviderSettings, ModelInfo } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui" diff --git a/webview-ui/src/components/settings/providers/BedrockCustomArn.tsx b/webview-ui/src/components/settings/providers/BedrockCustomArn.tsx index 1caf95b8c7..7ace93c843 100644 --- a/webview-ui/src/components/settings/providers/BedrockCustomArn.tsx +++ b/webview-ui/src/components/settings/providers/BedrockCustomArn.tsx @@ -1,7 +1,7 @@ import { useMemo } from "react" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { ProviderSettings } from "@roo/shared/api" +import type { ProviderSettings } from "@roo-code/types" import { validateBedrockArn } from "@src/utils/validate" import { useAppTranslation } from "@src/i18n/TranslationContext" diff --git a/webview-ui/src/components/settings/providers/Chutes.tsx b/webview-ui/src/components/settings/providers/Chutes.tsx index 10ca2c202f..c51479421a 100644 --- a/webview-ui/src/components/settings/providers/Chutes.tsx +++ b/webview-ui/src/components/settings/providers/Chutes.tsx @@ -1,7 +1,7 @@ import { useCallback } from "react" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { ProviderSettings } from "@roo/shared/api" +import type { ProviderSettings } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" diff --git a/webview-ui/src/components/settings/providers/DeepSeek.tsx b/webview-ui/src/components/settings/providers/DeepSeek.tsx index ffb48aec09..6f0ac2d92a 100644 --- a/webview-ui/src/components/settings/providers/DeepSeek.tsx +++ b/webview-ui/src/components/settings/providers/DeepSeek.tsx @@ -1,7 +1,7 @@ import { useCallback } from "react" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { ProviderSettings } from "@roo/shared/api" +import type { ProviderSettings } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" diff --git a/webview-ui/src/components/settings/providers/Gemini.tsx b/webview-ui/src/components/settings/providers/Gemini.tsx index 0a9af31568..21056f12d5 100644 --- a/webview-ui/src/components/settings/providers/Gemini.tsx +++ b/webview-ui/src/components/settings/providers/Gemini.tsx @@ -2,7 +2,7 @@ import { useCallback, useState } from "react" import { Checkbox } from "vscrui" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { ProviderSettings } from "@roo/shared/api" +import type { ProviderSettings } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" diff --git a/webview-ui/src/components/settings/providers/Glama.tsx b/webview-ui/src/components/settings/providers/Glama.tsx index 7a7f5dcc49..990f7804d0 100644 --- a/webview-ui/src/components/settings/providers/Glama.tsx +++ b/webview-ui/src/components/settings/providers/Glama.tsx @@ -1,7 +1,9 @@ import { useCallback } from "react" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { ProviderSettings, RouterModels, glamaDefaultModelId } from "@roo/shared/api" +import type { ProviderSettings } from "@roo-code/types" + +import { RouterModels, glamaDefaultModelId } from "@roo/api" import { useAppTranslation } from "@src/i18n/TranslationContext" import { getGlamaAuthUrl } from "@src/oauth/urls" diff --git a/webview-ui/src/components/settings/providers/Groq.tsx b/webview-ui/src/components/settings/providers/Groq.tsx index eaf29a4572..a8a910d1ac 100644 --- a/webview-ui/src/components/settings/providers/Groq.tsx +++ b/webview-ui/src/components/settings/providers/Groq.tsx @@ -1,7 +1,7 @@ import { useCallback } from "react" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { ProviderSettings } from "@roo/shared/api" +import type { ProviderSettings } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" diff --git a/webview-ui/src/components/settings/providers/LMStudio.tsx b/webview-ui/src/components/settings/providers/LMStudio.tsx index df58f903bb..9177457039 100644 --- a/webview-ui/src/components/settings/providers/LMStudio.tsx +++ b/webview-ui/src/components/settings/providers/LMStudio.tsx @@ -4,10 +4,10 @@ import { Trans } from "react-i18next" import { Checkbox } from "vscrui" import { VSCodeLink, VSCodeRadio, VSCodeRadioGroup, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { ProviderSettings } from "@roo/shared/api" +import type { ProviderSettings } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" -import { ExtensionMessage } from "@roo/shared/ExtensionMessage" +import { ExtensionMessage } from "@roo/ExtensionMessage" import { inputEventTransform } from "../transforms" diff --git a/webview-ui/src/components/settings/providers/LiteLLM.tsx b/webview-ui/src/components/settings/providers/LiteLLM.tsx index 717b10bb8a..b9ea04e87f 100644 --- a/webview-ui/src/components/settings/providers/LiteLLM.tsx +++ b/webview-ui/src/components/settings/providers/LiteLLM.tsx @@ -1,16 +1,18 @@ import { useCallback, useState, useEffect, useRef } from "react" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { ProviderSettings, litellmDefaultModelId, RouterName } from "@roo/shared/api" -import { Button } from "@src/components/ui" -import { vscode } from "@src/utils/vscode" -import { ExtensionMessage } from "@roo/shared/ExtensionMessage" +import type { ProviderSettings } from "@roo-code/types" +import { litellmDefaultModelId, RouterName } from "@roo/api" +import { ExtensionMessage } from "@roo/ExtensionMessage" + +import { vscode } from "@src/utils/vscode" +import { useExtensionState } from "@src/context/ExtensionStateContext" import { useAppTranslation } from "@src/i18n/TranslationContext" +import { Button } from "@src/components/ui" import { inputEventTransform } from "../transforms" import { ModelPicker } from "../ModelPicker" -import { useExtensionState } from "@src/context/ExtensionStateContext" type LiteLLMProps = { apiConfiguration: ProviderSettings diff --git a/webview-ui/src/components/settings/providers/Mistral.tsx b/webview-ui/src/components/settings/providers/Mistral.tsx index 1669166043..115b0b6b80 100644 --- a/webview-ui/src/components/settings/providers/Mistral.tsx +++ b/webview-ui/src/components/settings/providers/Mistral.tsx @@ -1,7 +1,9 @@ import { useCallback } from "react" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { ProviderSettings, RouterModels, mistralDefaultModelId } from "@roo/shared/api" +import type { ProviderSettings } from "@roo-code/types" + +import { RouterModels, mistralDefaultModelId } from "@roo/api" import { useAppTranslation } from "@src/i18n/TranslationContext" import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" diff --git a/webview-ui/src/components/settings/providers/Ollama.tsx b/webview-ui/src/components/settings/providers/Ollama.tsx index bda3c441cc..27fd2a5189 100644 --- a/webview-ui/src/components/settings/providers/Ollama.tsx +++ b/webview-ui/src/components/settings/providers/Ollama.tsx @@ -2,8 +2,9 @@ import { useState, useCallback } from "react" import { useEvent } from "react-use" import { VSCodeTextField, VSCodeRadioGroup, VSCodeRadio } from "@vscode/webview-ui-toolkit/react" -import { ProviderSettings } from "@roo/shared/api" -import { ExtensionMessage } from "@roo/shared/ExtensionMessage" +import type { ProviderSettings } from "@roo-code/types" + +import { ExtensionMessage } from "@roo/ExtensionMessage" import { useAppTranslation } from "@src/i18n/TranslationContext" diff --git a/webview-ui/src/components/settings/providers/OpenAI.tsx b/webview-ui/src/components/settings/providers/OpenAI.tsx index 771d77ef6a..e2f7857fe0 100644 --- a/webview-ui/src/components/settings/providers/OpenAI.tsx +++ b/webview-ui/src/components/settings/providers/OpenAI.tsx @@ -2,7 +2,7 @@ import { useCallback, useState } from "react" import { Checkbox } from "vscrui" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { ProviderSettings } from "@roo/shared/api" +import type { ProviderSettings } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" diff --git a/webview-ui/src/components/settings/providers/OpenAICompatible.tsx b/webview-ui/src/components/settings/providers/OpenAICompatible.tsx index e0de23e792..948bcc4b7f 100644 --- a/webview-ui/src/components/settings/providers/OpenAICompatible.tsx +++ b/webview-ui/src/components/settings/providers/OpenAICompatible.tsx @@ -2,15 +2,16 @@ import { useState, useCallback, useEffect } from "react" import { useEvent } from "react-use" import { Checkbox } from "vscrui" import { VSCodeButton, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { convertHeadersToObject } from "../utils/headers" -import { ModelInfo, ReasoningEffort } from "@roo/schemas" -import { ProviderSettings, azureOpenAiDefaultApiVersion, openAiModelInfoSaneDefaults } from "@roo/shared/api" -import { ExtensionMessage } from "@roo/shared/ExtensionMessage" +import type { ProviderSettings, ModelInfo, ReasoningEffort } from "@roo-code/types" + +import { azureOpenAiDefaultApiVersion, openAiModelInfoSaneDefaults } from "@roo/api" +import { ExtensionMessage } from "@roo/ExtensionMessage" import { useAppTranslation } from "@src/i18n/TranslationContext" import { Button } from "@src/components/ui" +import { convertHeadersToObject } from "../utils/headers" import { inputEventTransform, noTransform } from "../transforms" import { ModelPicker } from "../ModelPicker" import { R1FormatSetting } from "../R1FormatSetting" diff --git a/webview-ui/src/components/settings/providers/OpenRouter.tsx b/webview-ui/src/components/settings/providers/OpenRouter.tsx index 35cf34b6c9..eb89f6bfaf 100644 --- a/webview-ui/src/components/settings/providers/OpenRouter.tsx +++ b/webview-ui/src/components/settings/providers/OpenRouter.tsx @@ -4,7 +4,9 @@ import { Checkbox } from "vscrui" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" import { ExternalLinkIcon } from "@radix-ui/react-icons" -import { ProviderSettings, RouterModels, openRouterDefaultModelId } from "@roo/shared/api" +import type { ProviderSettings } from "@roo-code/types" + +import { RouterModels, openRouterDefaultModelId } from "@roo/api" import { useAppTranslation } from "@src/i18n/TranslationContext" import { getOpenRouterAuthUrl } from "@src/oauth/urls" diff --git a/webview-ui/src/components/settings/providers/Requesty.tsx b/webview-ui/src/components/settings/providers/Requesty.tsx index 9c6ba2844e..6c9e3c97a8 100644 --- a/webview-ui/src/components/settings/providers/Requesty.tsx +++ b/webview-ui/src/components/settings/providers/Requesty.tsx @@ -1,7 +1,9 @@ import { useCallback, useState } from "react" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { ProviderSettings, RouterModels, requestyDefaultModelId } from "@roo/shared/api" +import type { ProviderSettings } from "@roo-code/types" + +import { RouterModels, requestyDefaultModelId } from "@roo/api" import { vscode } from "@src/utils/vscode" import { useAppTranslation } from "@src/i18n/TranslationContext" diff --git a/webview-ui/src/components/settings/providers/Unbound.tsx b/webview-ui/src/components/settings/providers/Unbound.tsx index 3d5aa0c67a..33b124c69c 100644 --- a/webview-ui/src/components/settings/providers/Unbound.tsx +++ b/webview-ui/src/components/settings/providers/Unbound.tsx @@ -2,7 +2,9 @@ import { useCallback, useState, useRef } from "react" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" import { useQueryClient } from "@tanstack/react-query" -import { ProviderSettings, RouterModels, unboundDefaultModelId } from "@roo/shared/api" +import type { ProviderSettings } from "@roo-code/types" + +import { RouterModels, unboundDefaultModelId } from "@roo/api" import { useAppTranslation } from "@src/i18n/TranslationContext" import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" diff --git a/webview-ui/src/components/settings/providers/VSCodeLM.tsx b/webview-ui/src/components/settings/providers/VSCodeLM.tsx index 7f9426e5ce..a2097badf6 100644 --- a/webview-ui/src/components/settings/providers/VSCodeLM.tsx +++ b/webview-ui/src/components/settings/providers/VSCodeLM.tsx @@ -2,8 +2,9 @@ import { useState, useCallback } from "react" import { useEvent } from "react-use" import { LanguageModelChatSelector } from "vscode" -import { ProviderSettings } from "@roo/shared/api" -import { ExtensionMessage } from "@roo/shared/ExtensionMessage" +import type { ProviderSettings } from "@roo-code/types" + +import { ExtensionMessage } from "@roo/ExtensionMessage" import { useAppTranslation } from "@src/i18n/TranslationContext" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui" diff --git a/webview-ui/src/components/settings/providers/Vertex.tsx b/webview-ui/src/components/settings/providers/Vertex.tsx index b43c793816..1bf475eb0a 100644 --- a/webview-ui/src/components/settings/providers/Vertex.tsx +++ b/webview-ui/src/components/settings/providers/Vertex.tsx @@ -1,7 +1,7 @@ import { useCallback } from "react" import { VSCodeLink, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { ProviderSettings } from "@roo/shared/api" +import type { ProviderSettings } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui" diff --git a/webview-ui/src/components/settings/providers/XAI.tsx b/webview-ui/src/components/settings/providers/XAI.tsx index c42bde1b1e..619f223901 100644 --- a/webview-ui/src/components/settings/providers/XAI.tsx +++ b/webview-ui/src/components/settings/providers/XAI.tsx @@ -1,7 +1,7 @@ import { useCallback } from "react" import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import { ProviderSettings } from "@roo/shared/api" +import type { ProviderSettings } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" diff --git a/webview-ui/src/components/settings/types.ts b/webview-ui/src/components/settings/types.ts index 9eb22b7694..f4fa4c7832 100644 --- a/webview-ui/src/components/settings/types.ts +++ b/webview-ui/src/components/settings/types.ts @@ -1,4 +1,4 @@ -import { ExperimentId } from "@roo/shared/experiments" +import type { ExperimentId } from "@roo-code/types" import { ExtensionStateContextType } from "@/context/ExtensionStateContext" diff --git a/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.test.ts b/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.test.ts index 544da42c35..e7806a9f21 100644 --- a/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.test.ts +++ b/webview-ui/src/components/ui/hooks/__tests__/useSelectedModel.test.ts @@ -4,7 +4,7 @@ import React from "react" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" import { renderHook } from "@testing-library/react" -import { ProviderSettings, ModelInfo } from "@roo/shared/api" +import { ProviderSettings, ModelInfo } from "@roo-code/types" import { useSelectedModel } from "../useSelectedModel" import { useRouterModels } from "../useRouterModels" diff --git a/webview-ui/src/components/ui/hooks/useOpenRouterModelProviders.ts b/webview-ui/src/components/ui/hooks/useOpenRouterModelProviders.ts index fb3863ff6a..dc50c0f6a6 100644 --- a/webview-ui/src/components/ui/hooks/useOpenRouterModelProviders.ts +++ b/webview-ui/src/components/ui/hooks/useOpenRouterModelProviders.ts @@ -2,8 +2,9 @@ import axios from "axios" import { z } from "zod" import { useQuery, UseQueryOptions } from "@tanstack/react-query" -import { ModelInfo } from "@roo/shared/api" -import { parseApiPrice } from "@roo/utils/cost" +import type { ModelInfo } from "@roo-code/types" + +import { parseApiPrice } from "@roo/cost" export const OPENROUTER_DEFAULT_PROVIDER_NAME = "[default]" diff --git a/webview-ui/src/components/ui/hooks/useRouterModels.ts b/webview-ui/src/components/ui/hooks/useRouterModels.ts index f9a9c06a66..0ca68cc27a 100644 --- a/webview-ui/src/components/ui/hooks/useRouterModels.ts +++ b/webview-ui/src/components/ui/hooks/useRouterModels.ts @@ -1,8 +1,9 @@ -import { RouterModels } from "@roo/shared/api" +import { useQuery } from "@tanstack/react-query" + +import { RouterModels } from "@roo/api" +import { ExtensionMessage } from "@roo/ExtensionMessage" import { vscode } from "@src/utils/vscode" -import { ExtensionMessage } from "@roo/shared/ExtensionMessage" -import { useQuery } from "@tanstack/react-query" const getRouterModels = async () => new Promise((resolve, reject) => { diff --git a/webview-ui/src/components/ui/hooks/useSelectedModel.ts b/webview-ui/src/components/ui/hooks/useSelectedModel.ts index e28b24824b..f656c702dd 100644 --- a/webview-ui/src/components/ui/hooks/useSelectedModel.ts +++ b/webview-ui/src/components/ui/hooks/useSelectedModel.ts @@ -1,8 +1,7 @@ +import type { ProviderName, ProviderSettings, ModelInfo } from "@roo-code/types" + import { - type ProviderName, - type ProviderSettings, - type RouterModels, - type ModelInfo, + RouterModels, anthropicDefaultModelId, anthropicModels, bedrockDefaultModelId, @@ -31,7 +30,7 @@ import { glamaDefaultModelId, unboundDefaultModelId, litellmDefaultModelId, -} from "@roo/shared/api" +} from "@roo/api" import { useRouterModels } from "./useRouterModels" import { useOpenRouterModelProviders } from "./useOpenRouterModelProviders" diff --git a/webview-ui/src/components/welcome/WelcomeView.tsx b/webview-ui/src/components/welcome/WelcomeView.tsx index 3d860fd7ec..3989151c52 100644 --- a/webview-ui/src/components/welcome/WelcomeView.tsx +++ b/webview-ui/src/components/welcome/WelcomeView.tsx @@ -1,16 +1,20 @@ import { useCallback, useState } from "react" +import knuthShuffle from "knuth-shuffle-seeded" +import { Trans } from "react-i18next" import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react" + +import type { ProviderSettings } from "@roo-code/types" + import { useExtensionState } from "@src/context/ExtensionStateContext" import { validateApiConfiguration } from "@src/utils/validate" import { vscode } from "@src/utils/vscode" -import ApiOptions from "../settings/ApiOptions" -import { Tab, TabContent } from "../common/Tab" -import { Trans } from "react-i18next" import { useAppTranslation } from "@src/i18n/TranslationContext" import { getRequestyAuthUrl, getOpenRouterAuthUrl } from "@src/oauth/urls" + +import ApiOptions from "../settings/ApiOptions" +import { Tab, TabContent } from "../common/Tab" + import RooHero from "./RooHero" -import knuthShuffle from "knuth-shuffle-seeded" -import { ProviderSettings } from "@roo/shared/api" const WelcomeView = () => { const { apiConfiguration, currentApiConfigName, setApiConfiguration, uriScheme, machineId } = useExtensionState() diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index 53629f942e..b7cdf75f25 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -1,16 +1,23 @@ import React, { createContext, useCallback, useContext, useEffect, useState } from "react" import { useEvent } from "react-use" -import { ProviderSettingsEntry, ExtensionMessage, ExtensionState } from "@roo/shared/ExtensionMessage" -import { ProviderSettings } from "@roo/shared/api" -import { findLastIndex } from "@roo/shared/array" -import { McpServer } from "@roo/shared/mcp" -import { checkExistKey } from "@roo/shared/checkExistApiConfig" -import { Mode, CustomModePrompts, defaultModeSlug, defaultPrompts, ModeConfig } from "@roo/shared/modes" -import { CustomSupportPrompts } from "@roo/shared/support-prompt" -import { experimentDefault, ExperimentId } from "@roo/shared/experiments" -import { TelemetrySetting } from "@roo/shared/TelemetrySetting" -import { RouterModels } from "@roo/shared/api" +import type { + ProviderSettings, + ProviderSettingsEntry, + CustomModePrompts, + ModeConfig, + ExperimentId, +} from "@roo-code/types" + +import { ExtensionMessage, ExtensionState } from "@roo/ExtensionMessage" +import { findLastIndex } from "@roo/array" +import { McpServer } from "@roo/mcp" +import { checkExistKey } from "@roo/checkExistApiConfig" +import { Mode, defaultModeSlug, defaultPrompts } from "@roo/modes" +import { CustomSupportPrompts } from "@roo/support-prompt" +import { experimentDefault } from "@roo/experiments" +import { TelemetrySetting } from "@roo/TelemetrySetting" +import { RouterModels } from "@roo/api" import { vscode } from "@src/utils/vscode" import { convertTextMateToHljs } from "@src/utils/textMateToHljs" diff --git a/webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx b/webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx index 9c15b3bb0d..911d516caa 100644 --- a/webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx +++ b/webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx @@ -2,10 +2,11 @@ import { render, screen, act } from "@testing-library/react" -import { ExtensionState } from "@roo/shared/ExtensionMessage" +import { ProviderSettings, ExperimentId } from "@roo-code/types" + +import { ExtensionState } from "@roo/ExtensionMessage" + import { ExtensionStateContextProvider, useExtensionState, mergeExtensionState } from "../ExtensionStateContext" -import { ExperimentId } from "@roo/shared/experiments" -import { ProviderSettings } from "@roo/shared/api" const TestComponent = () => { const { allowedCommands, setAllowedCommands, soundEnabled, showRooIgnoredFiles, setShowRooIgnoredFiles } = diff --git a/webview-ui/src/oauth/urls.ts b/webview-ui/src/oauth/urls.ts index 17e205ae3e..8abf0ca434 100644 --- a/webview-ui/src/oauth/urls.ts +++ b/webview-ui/src/oauth/urls.ts @@ -1,4 +1,4 @@ -import { Package } from "@roo/schemas" +import { Package } from "@roo/package" export function getCallbackUrl(provider: string, uriScheme?: string) { return encodeURIComponent(`${uriScheme || "vscode"}://${Package.publisher}.${Package.name}/${provider}`) diff --git a/webview-ui/src/utils/TelemetryClient.ts b/webview-ui/src/utils/TelemetryClient.ts index 8b502da5a4..4f8759d2c5 100644 --- a/webview-ui/src/utils/TelemetryClient.ts +++ b/webview-ui/src/utils/TelemetryClient.ts @@ -1,5 +1,5 @@ import posthog from "posthog-js" -import { TelemetrySetting } from "@roo/shared/TelemetrySetting" +import { TelemetrySetting } from "@roo/TelemetrySetting" class TelemetryClient { private static instance: TelemetryClient diff --git a/webview-ui/src/utils/context-mentions.ts b/webview-ui/src/utils/context-mentions.ts index e3783d13ae..5df3404b23 100644 --- a/webview-ui/src/utils/context-mentions.ts +++ b/webview-ui/src/utils/context-mentions.ts @@ -1,6 +1,8 @@ -import { mentionRegex } from "@roo/shared/context-mentions" import { Fzf } from "fzf" -import { ModeConfig } from "@roo/shared/modes" + +import type { ModeConfig } from "@roo-code/types" + +import { mentionRegex } from "@roo/context-mentions" import { escapeSpaces } from "./path-mentions" diff --git a/webview-ui/src/utils/mcp.ts b/webview-ui/src/utils/mcp.ts index c3bce68f0a..b2a2ca002f 100644 --- a/webview-ui/src/utils/mcp.ts +++ b/webview-ui/src/utils/mcp.ts @@ -1,4 +1,4 @@ -import { McpResource, McpResourceTemplate } from "@roo/shared/mcp" +import { McpResource, McpResourceTemplate } from "@roo/mcp" /** * Matches a URI against an array of URI templates and returns the matching template diff --git a/webview-ui/src/utils/validate.ts b/webview-ui/src/utils/validate.ts index 0765fffeda..69b7590c0f 100644 --- a/webview-ui/src/utils/validate.ts +++ b/webview-ui/src/utils/validate.ts @@ -1,6 +1,8 @@ import i18next from "i18next" -import { ProviderSettings, isRouterName, RouterModels } from "@roo/shared/api" +import type { ProviderSettings } from "@roo-code/types" + +import { isRouterName, RouterModels } from "@roo/api" export function validateApiConfiguration(apiConfiguration: ProviderSettings): string | undefined { switch (apiConfiguration.apiProvider) { diff --git a/webview-ui/src/utils/vscode.ts b/webview-ui/src/utils/vscode.ts index 238eed22fd..2b2b25593b 100644 --- a/webview-ui/src/utils/vscode.ts +++ b/webview-ui/src/utils/vscode.ts @@ -1,6 +1,7 @@ -import { WebviewMessage } from "@roo/shared/WebviewMessage" import type { WebviewApi } from "vscode-webview" +import { WebviewMessage } from "@roo/WebviewMessage" + /** * A utility wrapper around the acquireVsCodeApi() function, which enables * message passing and state management between the webview and extension diff --git a/webview-ui/tsconfig.json b/webview-ui/tsconfig.json index 94b4a65bf9..530519bd27 100644 --- a/webview-ui/tsconfig.json +++ b/webview-ui/tsconfig.json @@ -21,7 +21,7 @@ "paths": { "@/*": ["./src/*"], "@src/*": ["./src/*"], - "@roo/*": ["../src/*"] + "@roo/*": ["../src/shared/*"] } }, "include": ["src", "../src/shared"] diff --git a/webview-ui/vite.config.ts b/webview-ui/vite.config.ts index 46afaf06dd..c47a838afa 100644 --- a/webview-ui/vite.config.ts +++ b/webview-ui/vite.config.ts @@ -87,7 +87,7 @@ export default defineConfig(({ mode }) => { alias: { "@": resolve(__dirname, "./src"), "@src": resolve(__dirname, "./src"), - "@roo": resolve(__dirname, "../src"), + "@roo": resolve(__dirname, "../src/shared"), }, }, build: { From 1e5bf7413694efa2838214ea85daa0cd29d7904d Mon Sep 17 00:00:00 2001 From: KJ7LNW <93454819+KJ7LNW@users.noreply.github.com> Date: Mon, 26 May 2025 13:25:39 -0700 Subject: [PATCH 013/104] fix: Correct path resolution for .vite-port file in ClineProvider (#4007) The ClineProvider was looking for the .vite-port file in the wrong location. Updated the path resolution to correctly point to the project root where the Vite development server creates the file. Fixes: #4006 Signed-off-by: Eric Wheeler Co-authored-by: Eric Wheeler --- src/core/webview/ClineProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index be7908fda8..25db95ac2a 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -557,7 +557,7 @@ export class ClineProvider extends EventEmitter implements try { const fs = require("fs") const path = require("path") - const portFilePath = path.resolve(__dirname, "../.vite-port") + const portFilePath = path.resolve(__dirname, "../../.vite-port") if (fs.existsSync(portFilePath)) { localPort = fs.readFileSync(portFilePath, "utf8").trim() From 1384077495e75e2be7128e9a42cf2cf6583fee5a Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Mon, 26 May 2025 20:22:54 -0700 Subject: [PATCH 014/104] Telemetry refactor (#4021) Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> --- packages/types/src/codebase-index.ts | 37 + packages/types/src/experiment.ts | 26 + packages/types/src/global-settings.ts | 269 ++++ packages/types/src/history.ts | 21 + packages/types/src/index.ts | 15 +- packages/types/src/ipc.ts | 183 +++ packages/types/src/message.ts | 118 ++ packages/types/src/mode.ts | 128 ++ packages/types/src/model.ts | 63 + packages/types/src/provider-settings.ts | 360 +++++ packages/types/src/telemetry.ts | 134 ++ packages/types/src/terminal.ts | 30 + packages/types/src/tool.ts | 54 + packages/types/src/type-fu.ts | 11 + packages/types/src/types.ts | 1344 ----------------- packages/types/src/vscode.ts | 84 ++ src/core/task/Task.ts | 15 + src/core/webview/ClineProvider.ts | 72 +- src/core/webview/webviewMessageHandler.ts | 2 +- src/extension.ts | 2 +- src/services/telemetry/PostHogClient.ts | 150 -- src/services/telemetry/TelemetryService.ts | 113 +- .../telemetry/clients/BaseTelemetryClient.ts | 58 + .../clients/PostHogTelemetryClient.ts | 88 ++ .../__tests__/PostHogTelemetryClient.test.ts | 270 ++++ src/services/telemetry/index.ts | 2 + src/services/telemetry/types.ts | 19 + src/utils/__tests__/refresh-timer.test.ts | 210 +++ src/utils/refresh-timer.ts | 154 ++ 29 files changed, 2429 insertions(+), 1603 deletions(-) create mode 100644 packages/types/src/codebase-index.ts create mode 100644 packages/types/src/experiment.ts create mode 100644 packages/types/src/global-settings.ts create mode 100644 packages/types/src/history.ts create mode 100644 packages/types/src/ipc.ts create mode 100644 packages/types/src/message.ts create mode 100644 packages/types/src/mode.ts create mode 100644 packages/types/src/model.ts create mode 100644 packages/types/src/provider-settings.ts create mode 100644 packages/types/src/telemetry.ts create mode 100644 packages/types/src/terminal.ts create mode 100644 packages/types/src/tool.ts create mode 100644 packages/types/src/type-fu.ts delete mode 100644 packages/types/src/types.ts create mode 100644 packages/types/src/vscode.ts delete mode 100644 src/services/telemetry/PostHogClient.ts create mode 100644 src/services/telemetry/clients/BaseTelemetryClient.ts create mode 100644 src/services/telemetry/clients/PostHogTelemetryClient.ts create mode 100644 src/services/telemetry/clients/__tests__/PostHogTelemetryClient.test.ts create mode 100644 src/services/telemetry/index.ts create mode 100644 src/services/telemetry/types.ts create mode 100644 src/utils/__tests__/refresh-timer.test.ts create mode 100644 src/utils/refresh-timer.ts diff --git a/packages/types/src/codebase-index.ts b/packages/types/src/codebase-index.ts new file mode 100644 index 0000000000..c9443e2fa7 --- /dev/null +++ b/packages/types/src/codebase-index.ts @@ -0,0 +1,37 @@ +import { z } from "zod" + +/** + * CodebaseIndexConfig + */ + +export const codebaseIndexConfigSchema = z.object({ + codebaseIndexEnabled: z.boolean().optional(), + codebaseIndexQdrantUrl: z.string().optional(), + codebaseIndexEmbedderProvider: z.enum(["openai", "ollama"]).optional(), + codebaseIndexEmbedderBaseUrl: z.string().optional(), + codebaseIndexEmbedderModelId: z.string().optional(), +}) + +export type CodebaseIndexConfig = z.infer + +/** + * CodebaseIndexModels + */ + +export const codebaseIndexModelsSchema = z.object({ + openai: z.record(z.string(), z.object({ dimension: z.number() })).optional(), + ollama: z.record(z.string(), z.object({ dimension: z.number() })).optional(), +}) + +export type CodebaseIndexModels = z.infer + +/** + * CdebaseIndexProvider + */ + +export const codebaseIndexProviderSchema = z.object({ + codeIndexOpenAiKey: z.string().optional(), + codeIndexQdrantApiKey: z.string().optional(), +}) + +export type CodebaseIndexProvider = z.infer diff --git a/packages/types/src/experiment.ts b/packages/types/src/experiment.ts new file mode 100644 index 0000000000..6b43327207 --- /dev/null +++ b/packages/types/src/experiment.ts @@ -0,0 +1,26 @@ +import { z } from "zod" + +import type { Keys, Equals, AssertEqual } from "./type-fu.js" + +/** + * ExperimentId + */ + +export const experimentIds = ["autoCondenseContext", "powerSteering"] as const + +export const experimentIdsSchema = z.enum(experimentIds) + +export type ExperimentId = z.infer + +/** + * Experiments + */ + +export const experimentsSchema = z.object({ + autoCondenseContext: z.boolean(), + powerSteering: z.boolean(), +}) + +export type Experiments = z.infer + +type _AssertExperiments = AssertEqual>> diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts new file mode 100644 index 0000000000..d69a77fa53 --- /dev/null +++ b/packages/types/src/global-settings.ts @@ -0,0 +1,269 @@ +import { z } from "zod" + +import type { Keys } from "./type-fu.js" +import { + type ProviderSettings, + PROVIDER_SETTINGS_KEYS, + providerSettingsEntrySchema, + providerSettingsSchema, +} from "./provider-settings.js" +import { historyItemSchema } from "./history.js" +import { codebaseIndexModelsSchema, codebaseIndexConfigSchema } from "./codebase-index.js" +import { experimentsSchema } from "./experiment.js" +import { telemetrySettingsSchema } from "./telemetry.js" +import { modeConfigSchema } from "./mode.js" +import { customModePromptsSchema, customSupportPromptsSchema } from "./mode.js" +import { languagesSchema } from "./vscode.js" + +/** + * GlobalSettings + */ + +export const globalSettingsSchema = z.object({ + currentApiConfigName: z.string().optional(), + listApiConfigMeta: z.array(providerSettingsEntrySchema).optional(), + pinnedApiConfigs: z.record(z.string(), z.boolean()).optional(), + + lastShownAnnouncementId: z.string().optional(), + customInstructions: z.string().optional(), + taskHistory: z.array(historyItemSchema).optional(), + + condensingApiConfigId: z.string().optional(), + customCondensingPrompt: z.string().optional(), + + autoApprovalEnabled: z.boolean().optional(), + alwaysAllowReadOnly: z.boolean().optional(), + alwaysAllowReadOnlyOutsideWorkspace: z.boolean().optional(), + codebaseIndexModels: codebaseIndexModelsSchema.optional(), + codebaseIndexConfig: codebaseIndexConfigSchema.optional(), + alwaysAllowWrite: z.boolean().optional(), + alwaysAllowWriteOutsideWorkspace: z.boolean().optional(), + writeDelayMs: z.number().optional(), + alwaysAllowBrowser: z.boolean().optional(), + alwaysApproveResubmit: z.boolean().optional(), + requestDelaySeconds: z.number().optional(), + alwaysAllowMcp: z.boolean().optional(), + alwaysAllowModeSwitch: z.boolean().optional(), + alwaysAllowSubtasks: z.boolean().optional(), + alwaysAllowExecute: z.boolean().optional(), + allowedCommands: z.array(z.string()).optional(), + allowedMaxRequests: z.number().nullish(), + autoCondenseContextPercent: z.number().optional(), + + browserToolEnabled: z.boolean().optional(), + browserViewportSize: z.string().optional(), + screenshotQuality: z.number().optional(), + remoteBrowserEnabled: z.boolean().optional(), + remoteBrowserHost: z.string().optional(), + cachedChromeHostUrl: z.string().optional(), + + enableCheckpoints: z.boolean().optional(), + + ttsEnabled: z.boolean().optional(), + ttsSpeed: z.number().optional(), + soundEnabled: z.boolean().optional(), + soundVolume: z.number().optional(), + + maxOpenTabsContext: z.number().optional(), + maxWorkspaceFiles: z.number().optional(), + showRooIgnoredFiles: z.boolean().optional(), + maxReadFileLine: z.number().optional(), + + terminalOutputLineLimit: z.number().optional(), + terminalShellIntegrationTimeout: z.number().optional(), + terminalShellIntegrationDisabled: z.boolean().optional(), + terminalCommandDelay: z.number().optional(), + terminalPowershellCounter: z.boolean().optional(), + terminalZshClearEolMark: z.boolean().optional(), + terminalZshOhMy: z.boolean().optional(), + terminalZshP10k: z.boolean().optional(), + terminalZdotdir: z.boolean().optional(), + terminalCompressProgressBar: z.boolean().optional(), + + rateLimitSeconds: z.number().optional(), + diffEnabled: z.boolean().optional(), + fuzzyMatchThreshold: z.number().optional(), + experiments: experimentsSchema.optional(), + + language: languagesSchema.optional(), + + telemetrySetting: telemetrySettingsSchema.optional(), + + mcpEnabled: z.boolean().optional(), + enableMcpServerCreation: z.boolean().optional(), + + mode: z.string().optional(), + modeApiConfigs: z.record(z.string(), z.string()).optional(), + customModes: z.array(modeConfigSchema).optional(), + customModePrompts: customModePromptsSchema.optional(), + customSupportPrompts: customSupportPromptsSchema.optional(), + enhancementApiConfigId: z.string().optional(), + historyPreviewCollapsed: z.boolean().optional(), +}) + +export type GlobalSettings = z.infer + +type GlobalSettingsRecord = Record, undefined> + +const globalSettingsRecord: GlobalSettingsRecord = { + codebaseIndexModels: undefined, + codebaseIndexConfig: undefined, + currentApiConfigName: undefined, + listApiConfigMeta: undefined, + pinnedApiConfigs: undefined, + + lastShownAnnouncementId: undefined, + customInstructions: undefined, + taskHistory: undefined, + + condensingApiConfigId: undefined, + customCondensingPrompt: undefined, + + autoApprovalEnabled: undefined, + alwaysAllowReadOnly: undefined, + alwaysAllowReadOnlyOutsideWorkspace: undefined, + alwaysAllowWrite: undefined, + alwaysAllowWriteOutsideWorkspace: undefined, + writeDelayMs: undefined, + alwaysAllowBrowser: undefined, + alwaysApproveResubmit: undefined, + requestDelaySeconds: undefined, + alwaysAllowMcp: undefined, + alwaysAllowModeSwitch: undefined, + alwaysAllowSubtasks: undefined, + alwaysAllowExecute: undefined, + allowedCommands: undefined, + allowedMaxRequests: undefined, + autoCondenseContextPercent: undefined, + + browserToolEnabled: undefined, + browserViewportSize: undefined, + screenshotQuality: undefined, + remoteBrowserEnabled: undefined, + remoteBrowserHost: undefined, + + enableCheckpoints: undefined, + + ttsEnabled: undefined, + ttsSpeed: undefined, + soundEnabled: undefined, + soundVolume: undefined, + + maxOpenTabsContext: undefined, + maxWorkspaceFiles: undefined, + showRooIgnoredFiles: undefined, + maxReadFileLine: undefined, + + terminalOutputLineLimit: undefined, + terminalShellIntegrationTimeout: undefined, + terminalShellIntegrationDisabled: undefined, + terminalCommandDelay: undefined, + terminalPowershellCounter: undefined, + terminalZshClearEolMark: undefined, + terminalZshOhMy: undefined, + terminalZshP10k: undefined, + terminalZdotdir: undefined, + terminalCompressProgressBar: undefined, + + rateLimitSeconds: undefined, + diffEnabled: undefined, + fuzzyMatchThreshold: undefined, + experiments: undefined, + + language: undefined, + + telemetrySetting: undefined, + + mcpEnabled: undefined, + enableMcpServerCreation: undefined, + + mode: undefined, + modeApiConfigs: undefined, + customModes: undefined, + customModePrompts: undefined, + customSupportPrompts: undefined, + enhancementApiConfigId: undefined, + cachedChromeHostUrl: undefined, + historyPreviewCollapsed: undefined, +} + +export const GLOBAL_SETTINGS_KEYS = Object.keys(globalSettingsRecord) as Keys[] + +/** + * RooCodeSettings + */ + +export const rooCodeSettingsSchema = providerSettingsSchema.merge(globalSettingsSchema) + +export type RooCodeSettings = GlobalSettings & ProviderSettings + +/** + * SecretState + */ + +export type SecretState = Pick< + ProviderSettings, + | "apiKey" + | "glamaApiKey" + | "openRouterApiKey" + | "awsAccessKey" + | "awsSecretKey" + | "awsSessionToken" + | "openAiApiKey" + | "geminiApiKey" + | "openAiNativeApiKey" + | "deepSeekApiKey" + | "mistralApiKey" + | "unboundApiKey" + | "requestyApiKey" + | "xaiApiKey" + | "groqApiKey" + | "chutesApiKey" + | "litellmApiKey" + | "codeIndexOpenAiKey" + | "codeIndexQdrantApiKey" +> + +export type CodeIndexSecrets = "codeIndexOpenAiKey" | "codeIndexQdrantApiKey" + +type SecretStateRecord = Record, undefined> + +const secretStateRecord: SecretStateRecord = { + apiKey: undefined, + glamaApiKey: undefined, + openRouterApiKey: undefined, + awsAccessKey: undefined, + awsSecretKey: undefined, + awsSessionToken: undefined, + openAiApiKey: undefined, + geminiApiKey: undefined, + openAiNativeApiKey: undefined, + deepSeekApiKey: undefined, + mistralApiKey: undefined, + unboundApiKey: undefined, + requestyApiKey: undefined, + xaiApiKey: undefined, + groqApiKey: undefined, + chutesApiKey: undefined, + litellmApiKey: undefined, + codeIndexOpenAiKey: undefined, + codeIndexQdrantApiKey: undefined, +} + +export const SECRET_STATE_KEYS = Object.keys(secretStateRecord) as Keys[] + +export const isSecretStateKey = (key: string): key is Keys => + SECRET_STATE_KEYS.includes(key as Keys) + +/** + * GlobalState + */ + +export type GlobalState = Omit> + +export const GLOBAL_STATE_KEYS = [...GLOBAL_SETTINGS_KEYS, ...PROVIDER_SETTINGS_KEYS].filter( + (key: Keys) => !SECRET_STATE_KEYS.includes(key as Keys), +) as Keys[] + +export const isGlobalStateKey = (key: string): key is Keys => + GLOBAL_STATE_KEYS.includes(key as Keys) diff --git a/packages/types/src/history.ts b/packages/types/src/history.ts new file mode 100644 index 0000000000..8c75024879 --- /dev/null +++ b/packages/types/src/history.ts @@ -0,0 +1,21 @@ +import { z } from "zod" + +/** + * HistoryItem + */ + +export const historyItemSchema = z.object({ + id: z.string(), + number: z.number(), + ts: z.number(), + task: z.string(), + tokensIn: z.number(), + tokensOut: z.number(), + cacheWrites: z.number().optional(), + cacheReads: z.number().optional(), + totalCost: z.number(), + size: z.number().optional(), + workspace: z.string().optional(), +}) + +export type HistoryItem = z.infer diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index b3656fb63a..8b49dc1d62 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,2 +1,15 @@ -export * from "./types.js" export * from "./api.js" +export * from "./codebase-index.js" +export * from "./experiment.js" +export * from "./global-settings.js" +export * from "./history.js" +export * from "./ipc.js" +export * from "./message.js" +export * from "./mode.js" +export * from "./model.js" +export * from "./provider-settings.js" +export * from "./telemetry.js" +export * from "./terminal.js" +export * from "./tool.js" +export * from "./type-fu.js" +export * from "./vscode.js" diff --git a/packages/types/src/ipc.ts b/packages/types/src/ipc.ts new file mode 100644 index 0000000000..aa35e194a9 --- /dev/null +++ b/packages/types/src/ipc.ts @@ -0,0 +1,183 @@ +import { z } from "zod" + +import { clineMessageSchema, tokenUsageSchema } from "./message.js" +import { toolNamesSchema, toolUsageSchema } from "./tool.js" +import { rooCodeSettingsSchema } from "./global-settings.js" + +/** + * RooCodeEvent + */ + +export enum RooCodeEventName { + Message = "message", + TaskCreated = "taskCreated", + TaskStarted = "taskStarted", + TaskModeSwitched = "taskModeSwitched", + TaskPaused = "taskPaused", + TaskUnpaused = "taskUnpaused", + TaskAskResponded = "taskAskResponded", + TaskAborted = "taskAborted", + TaskSpawned = "taskSpawned", + TaskCompleted = "taskCompleted", + TaskTokenUsageUpdated = "taskTokenUsageUpdated", + TaskToolFailed = "taskToolFailed", +} + +export const rooCodeEventsSchema = z.object({ + [RooCodeEventName.Message]: z.tuple([ + z.object({ + taskId: z.string(), + action: z.union([z.literal("created"), z.literal("updated")]), + message: clineMessageSchema, + }), + ]), + [RooCodeEventName.TaskCreated]: z.tuple([z.string()]), + [RooCodeEventName.TaskStarted]: z.tuple([z.string()]), + [RooCodeEventName.TaskModeSwitched]: z.tuple([z.string(), z.string()]), + [RooCodeEventName.TaskPaused]: z.tuple([z.string()]), + [RooCodeEventName.TaskUnpaused]: z.tuple([z.string()]), + [RooCodeEventName.TaskAskResponded]: z.tuple([z.string()]), + [RooCodeEventName.TaskAborted]: z.tuple([z.string()]), + [RooCodeEventName.TaskSpawned]: z.tuple([z.string(), z.string()]), + [RooCodeEventName.TaskCompleted]: z.tuple([z.string(), tokenUsageSchema, toolUsageSchema]), + [RooCodeEventName.TaskTokenUsageUpdated]: z.tuple([z.string(), tokenUsageSchema]), + [RooCodeEventName.TaskToolFailed]: z.tuple([z.string(), toolNamesSchema, z.string()]), +}) + +export type RooCodeEvents = z.infer + +/** + * Ack + */ + +export const ackSchema = z.object({ + clientId: z.string(), + pid: z.number(), + ppid: z.number(), +}) + +export type Ack = z.infer + +/** + * TaskCommand + */ + +export enum TaskCommandName { + StartNewTask = "StartNewTask", + CancelTask = "CancelTask", + CloseTask = "CloseTask", +} + +export const taskCommandSchema = z.discriminatedUnion("commandName", [ + z.object({ + commandName: z.literal(TaskCommandName.StartNewTask), + data: z.object({ + configuration: rooCodeSettingsSchema, + text: z.string(), + images: z.array(z.string()).optional(), + newTab: z.boolean().optional(), + }), + }), + z.object({ + commandName: z.literal(TaskCommandName.CancelTask), + data: z.string(), + }), + z.object({ + commandName: z.literal(TaskCommandName.CloseTask), + data: z.string(), + }), +]) + +export type TaskCommand = z.infer + +/** + * TaskEvent + */ + +export const taskEventSchema = z.discriminatedUnion("eventName", [ + z.object({ + eventName: z.literal(RooCodeEventName.Message), + payload: rooCodeEventsSchema.shape[RooCodeEventName.Message], + }), + z.object({ + eventName: z.literal(RooCodeEventName.TaskCreated), + payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskCreated], + }), + z.object({ + eventName: z.literal(RooCodeEventName.TaskStarted), + payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskStarted], + }), + z.object({ + eventName: z.literal(RooCodeEventName.TaskModeSwitched), + payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskModeSwitched], + }), + z.object({ + eventName: z.literal(RooCodeEventName.TaskPaused), + payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskPaused], + }), + z.object({ + eventName: z.literal(RooCodeEventName.TaskUnpaused), + payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskUnpaused], + }), + z.object({ + eventName: z.literal(RooCodeEventName.TaskAskResponded), + payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskAskResponded], + }), + z.object({ + eventName: z.literal(RooCodeEventName.TaskAborted), + payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskAborted], + }), + z.object({ + eventName: z.literal(RooCodeEventName.TaskSpawned), + payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskSpawned], + }), + z.object({ + eventName: z.literal(RooCodeEventName.TaskCompleted), + payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskCompleted], + }), + z.object({ + eventName: z.literal(RooCodeEventName.TaskTokenUsageUpdated), + payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskTokenUsageUpdated], + }), +]) + +export type TaskEvent = z.infer + +/** + * IpcMessage + */ + +export enum IpcMessageType { + Connect = "Connect", + Disconnect = "Disconnect", + Ack = "Ack", + TaskCommand = "TaskCommand", + TaskEvent = "TaskEvent", +} + +export enum IpcOrigin { + Client = "client", + Server = "server", +} + +export const ipcMessageSchema = z.discriminatedUnion("type", [ + z.object({ + type: z.literal(IpcMessageType.Ack), + origin: z.literal(IpcOrigin.Server), + data: ackSchema, + }), + z.object({ + type: z.literal(IpcMessageType.TaskCommand), + origin: z.literal(IpcOrigin.Client), + clientId: z.string(), + data: taskCommandSchema, + }), + z.object({ + type: z.literal(IpcMessageType.TaskEvent), + origin: z.literal(IpcOrigin.Server), + relayClientId: z.string().optional(), + data: taskEventSchema, + }), +]) + +export type IpcMessage = z.infer diff --git a/packages/types/src/message.ts b/packages/types/src/message.ts new file mode 100644 index 0000000000..e870e8d707 --- /dev/null +++ b/packages/types/src/message.ts @@ -0,0 +1,118 @@ +import { z } from "zod" + +/** + * ClineAsk + */ + +export const clineAsks = [ + "followup", + "command", + "command_output", + "completion_result", + "tool", + "api_req_failed", + "resume_task", + "resume_completed_task", + "mistake_limit_reached", + "browser_action_launch", + "use_mcp_server", + "auto_approval_max_req_reached", +] as const + +export const clineAskSchema = z.enum(clineAsks) + +export type ClineAsk = z.infer + +/** + * ClineSay + */ + +export const clineSays = [ + "error", + "api_req_started", + "api_req_finished", + "api_req_retried", + "api_req_retry_delayed", + "api_req_deleted", + "text", + "reasoning", + "completion_result", + "user_feedback", + "user_feedback_diff", + "command_output", + "shell_integration_warning", + "browser_action", + "browser_action_result", + "mcp_server_request_started", + "mcp_server_response", + "subtask_result", + "checkpoint_saved", + "rooignore_error", + "diff_error", + "condense_context", + "codebase_search_result", +] as const + +export const clineSaySchema = z.enum(clineSays) + +export type ClineSay = z.infer + +/** + * ToolProgressStatus + */ + +export const toolProgressStatusSchema = z.object({ + icon: z.string().optional(), + text: z.string().optional(), +}) + +export type ToolProgressStatus = z.infer + +/** + * ContextCondense + */ + +export const contextCondenseSchema = z.object({ + cost: z.number(), + prevContextTokens: z.number(), + newContextTokens: z.number(), + summary: z.string(), +}) + +export type ContextCondense = z.infer + +/** + * ClineMessage + */ + +export const clineMessageSchema = z.object({ + ts: z.number(), + type: z.union([z.literal("ask"), z.literal("say")]), + ask: clineAskSchema.optional(), + say: clineSaySchema.optional(), + text: z.string().optional(), + images: z.array(z.string()).optional(), + partial: z.boolean().optional(), + reasoning: z.string().optional(), + conversationHistoryIndex: z.number().optional(), + checkpoint: z.record(z.string(), z.unknown()).optional(), + progressStatus: toolProgressStatusSchema.optional(), + contextCondense: contextCondenseSchema.optional(), +}) + +export type ClineMessage = z.infer + +/** + * TokenUsage + */ + +export const tokenUsageSchema = z.object({ + totalTokensIn: z.number(), + totalTokensOut: z.number(), + totalCacheWrites: z.number().optional(), + totalCacheReads: z.number().optional(), + totalCost: z.number(), + contextTokens: z.number(), +}) + +export type TokenUsage = z.infer diff --git a/packages/types/src/mode.ts b/packages/types/src/mode.ts new file mode 100644 index 0000000000..dfe95f8d7e --- /dev/null +++ b/packages/types/src/mode.ts @@ -0,0 +1,128 @@ +import { z } from "zod" + +import { toolGroupsSchema } from "./tool.js" + +/** + * GroupOptions + */ + +export const groupOptionsSchema = z.object({ + fileRegex: z + .string() + .optional() + .refine( + (pattern) => { + if (!pattern) { + return true // Optional, so empty is valid. + } + + try { + new RegExp(pattern) + return true + } catch { + return false + } + }, + { message: "Invalid regular expression pattern" }, + ), + description: z.string().optional(), +}) + +export type GroupOptions = z.infer + +/** + * GroupEntry + */ + +export const groupEntrySchema = z.union([toolGroupsSchema, z.tuple([toolGroupsSchema, groupOptionsSchema])]) + +export type GroupEntry = z.infer + +/** + * ModeConfig + */ + +const groupEntryArraySchema = z.array(groupEntrySchema).refine( + (groups) => { + const seen = new Set() + + return groups.every((group) => { + // For tuples, check the group name (first element). + const groupName = Array.isArray(group) ? group[0] : group + + if (seen.has(groupName)) { + return false + } + + seen.add(groupName) + return true + }) + }, + { message: "Duplicate groups are not allowed" }, +) + +export const modeConfigSchema = z.object({ + slug: z.string().regex(/^[a-zA-Z0-9-]+$/, "Slug must contain only letters numbers and dashes"), + name: z.string().min(1, "Name is required"), + roleDefinition: z.string().min(1, "Role definition is required"), + whenToUse: z.string().optional(), + customInstructions: z.string().optional(), + groups: groupEntryArraySchema, + source: z.enum(["global", "project"]).optional(), +}) + +export type ModeConfig = z.infer + +/** + * CustomModesSettings + */ + +export const customModesSettingsSchema = z.object({ + customModes: z.array(modeConfigSchema).refine( + (modes) => { + const slugs = new Set() + + return modes.every((mode) => { + if (slugs.has(mode.slug)) { + return false + } + + slugs.add(mode.slug) + return true + }) + }, + { + message: "Duplicate mode slugs are not allowed", + }, + ), +}) + +export type CustomModesSettings = z.infer + +/** + * PromptComponent + */ + +export const promptComponentSchema = z.object({ + roleDefinition: z.string().optional(), + whenToUse: z.string().optional(), + customInstructions: z.string().optional(), +}) + +export type PromptComponent = z.infer + +/** + * CustomModePrompts + */ + +export const customModePromptsSchema = z.record(z.string(), promptComponentSchema.optional()) + +export type CustomModePrompts = z.infer + +/** + * CustomSupportPrompts + */ + +export const customSupportPromptsSchema = z.record(z.string(), z.string().optional()) + +export type CustomSupportPrompts = z.infer diff --git a/packages/types/src/model.ts b/packages/types/src/model.ts new file mode 100644 index 0000000000..3bd66782cf --- /dev/null +++ b/packages/types/src/model.ts @@ -0,0 +1,63 @@ +import { z } from "zod" + +/** + * ReasoningEffort + */ + +export const reasoningEfforts = ["low", "medium", "high"] as const + +export const reasoningEffortsSchema = z.enum(reasoningEfforts) + +export type ReasoningEffort = z.infer + +/** + * ModelParameter + */ + +export const modelParameters = ["max_tokens", "temperature", "reasoning", "include_reasoning"] as const + +export const modelParametersSchema = z.enum(modelParameters) + +export type ModelParameter = z.infer + +export const isModelParameter = (value: string): value is ModelParameter => + modelParameters.includes(value as ModelParameter) + +/** + * ModelInfo + */ + +export const modelInfoSchema = z.object({ + maxTokens: z.number().nullish(), + maxThinkingTokens: z.number().nullish(), + contextWindow: z.number(), + supportsImages: z.boolean().optional(), + supportsComputerUse: z.boolean().optional(), + supportsPromptCache: z.boolean(), + supportsReasoningBudget: z.boolean().optional(), + requiredReasoningBudget: z.boolean().optional(), + supportsReasoningEffort: z.boolean().optional(), + supportedParameters: z.array(modelParametersSchema).optional(), + inputPrice: z.number().optional(), + outputPrice: z.number().optional(), + cacheWritesPrice: z.number().optional(), + cacheReadsPrice: z.number().optional(), + description: z.string().optional(), + reasoningEffort: reasoningEffortsSchema.optional(), + 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/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts new file mode 100644 index 0000000000..6803cebc11 --- /dev/null +++ b/packages/types/src/provider-settings.ts @@ -0,0 +1,360 @@ +import { z } from "zod" + +import type { Keys } from "./type-fu.js" +import { reasoningEffortsSchema, modelInfoSchema } from "./model.js" +import { codebaseIndexProviderSchema } from "./codebase-index.js" + +/** + * ProviderName + */ + +export const providerNames = [ + "anthropic", + "glama", + "openrouter", + "bedrock", + "vertex", + "openai", + "ollama", + "vscode-lm", + "lmstudio", + "gemini", + "openai-native", + "mistral", + "deepseek", + "unbound", + "requesty", + "human-relay", + "fake-ai", + "xai", + "groq", + "chutes", + "litellm", +] as const + +export const providerNamesSchema = z.enum(providerNames) + +export type ProviderName = z.infer + +/** + * ProviderSettingsEntry + */ + +export const providerSettingsEntrySchema = z.object({ + id: z.string(), + name: z.string(), + apiProvider: providerNamesSchema.optional(), +}) + +export type ProviderSettingsEntry = z.infer + +/** + * ProviderSettings + */ + +const baseProviderSettingsSchema = z.object({ + includeMaxTokens: z.boolean().optional(), + diffEnabled: z.boolean().optional(), + fuzzyMatchThreshold: z.number().optional(), + modelTemperature: z.number().nullish(), + rateLimitSeconds: z.number().optional(), + + // Model reasoning. + enableReasoningEffort: z.boolean().optional(), + reasoningEffort: reasoningEffortsSchema.optional(), + modelMaxTokens: z.number().optional(), + modelMaxThinkingTokens: z.number().optional(), +}) + +// Several of the providers share common model config properties. +const apiModelIdProviderModelSchema = baseProviderSettingsSchema.extend({ + apiModelId: z.string().optional(), +}) + +const anthropicSchema = apiModelIdProviderModelSchema.extend({ + apiKey: z.string().optional(), + anthropicBaseUrl: z.string().optional(), + anthropicUseAuthToken: z.boolean().optional(), +}) + +const glamaSchema = baseProviderSettingsSchema.extend({ + glamaModelId: z.string().optional(), + glamaApiKey: z.string().optional(), +}) + +const openRouterSchema = baseProviderSettingsSchema.extend({ + openRouterApiKey: z.string().optional(), + openRouterModelId: z.string().optional(), + openRouterBaseUrl: z.string().optional(), + openRouterSpecificProvider: z.string().optional(), + openRouterUseMiddleOutTransform: z.boolean().optional(), +}) + +const bedrockSchema = apiModelIdProviderModelSchema.extend({ + awsAccessKey: z.string().optional(), + awsSecretKey: z.string().optional(), + awsSessionToken: z.string().optional(), + awsRegion: z.string().optional(), + awsUseCrossRegionInference: z.boolean().optional(), + awsUsePromptCache: z.boolean().optional(), + awsProfile: z.string().optional(), + awsUseProfile: z.boolean().optional(), + awsCustomArn: z.string().optional(), +}) + +const vertexSchema = apiModelIdProviderModelSchema.extend({ + vertexKeyFile: z.string().optional(), + vertexJsonCredentials: z.string().optional(), + vertexProjectId: z.string().optional(), + vertexRegion: z.string().optional(), +}) + +const openAiSchema = baseProviderSettingsSchema.extend({ + openAiBaseUrl: z.string().optional(), + openAiApiKey: z.string().optional(), + openAiLegacyFormat: z.boolean().optional(), + openAiR1FormatEnabled: z.boolean().optional(), + openAiModelId: z.string().optional(), + openAiCustomModelInfo: modelInfoSchema.nullish(), + openAiUseAzure: z.boolean().optional(), + azureApiVersion: z.string().optional(), + openAiStreamingEnabled: z.boolean().optional(), + openAiHostHeader: z.string().optional(), // Keep temporarily for backward compatibility during migration. + openAiHeaders: z.record(z.string(), z.string()).optional(), +}) + +const ollamaSchema = baseProviderSettingsSchema.extend({ + ollamaModelId: z.string().optional(), + ollamaBaseUrl: z.string().optional(), +}) + +const vsCodeLmSchema = baseProviderSettingsSchema.extend({ + vsCodeLmModelSelector: z + .object({ + vendor: z.string().optional(), + family: z.string().optional(), + version: z.string().optional(), + id: z.string().optional(), + }) + .optional(), +}) + +const lmStudioSchema = baseProviderSettingsSchema.extend({ + lmStudioModelId: z.string().optional(), + lmStudioBaseUrl: z.string().optional(), + lmStudioDraftModelId: z.string().optional(), + lmStudioSpeculativeDecodingEnabled: z.boolean().optional(), +}) + +const geminiSchema = apiModelIdProviderModelSchema.extend({ + geminiApiKey: z.string().optional(), + googleGeminiBaseUrl: z.string().optional(), +}) + +const openAiNativeSchema = apiModelIdProviderModelSchema.extend({ + openAiNativeApiKey: z.string().optional(), + openAiNativeBaseUrl: z.string().optional(), +}) + +const mistralSchema = apiModelIdProviderModelSchema.extend({ + mistralApiKey: z.string().optional(), + mistralCodestralUrl: z.string().optional(), +}) + +const deepSeekSchema = apiModelIdProviderModelSchema.extend({ + deepSeekBaseUrl: z.string().optional(), + deepSeekApiKey: z.string().optional(), +}) + +const unboundSchema = baseProviderSettingsSchema.extend({ + unboundApiKey: z.string().optional(), + unboundModelId: z.string().optional(), +}) + +const requestySchema = baseProviderSettingsSchema.extend({ + requestyApiKey: z.string().optional(), + requestyModelId: z.string().optional(), +}) + +const humanRelaySchema = baseProviderSettingsSchema + +const fakeAiSchema = baseProviderSettingsSchema.extend({ + fakeAi: z.unknown().optional(), +}) + +const xaiSchema = apiModelIdProviderModelSchema.extend({ + xaiApiKey: z.string().optional(), +}) + +const groqSchema = apiModelIdProviderModelSchema.extend({ + groqApiKey: z.string().optional(), +}) + +const chutesSchema = apiModelIdProviderModelSchema.extend({ + chutesApiKey: z.string().optional(), +}) + +const litellmSchema = baseProviderSettingsSchema.extend({ + litellmBaseUrl: z.string().optional(), + litellmApiKey: z.string().optional(), + litellmModelId: z.string().optional(), +}) + +const defaultSchema = z.object({ + apiProvider: z.undefined(), +}) + +export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProvider", [ + anthropicSchema.merge(z.object({ apiProvider: z.literal("anthropic") })), + glamaSchema.merge(z.object({ apiProvider: z.literal("glama") })), + openRouterSchema.merge(z.object({ apiProvider: z.literal("openrouter") })), + bedrockSchema.merge(z.object({ apiProvider: z.literal("bedrock") })), + vertexSchema.merge(z.object({ apiProvider: z.literal("vertex") })), + openAiSchema.merge(z.object({ apiProvider: z.literal("openai") })), + ollamaSchema.merge(z.object({ apiProvider: z.literal("ollama") })), + vsCodeLmSchema.merge(z.object({ apiProvider: z.literal("vscode-lm") })), + lmStudioSchema.merge(z.object({ apiProvider: z.literal("lmstudio") })), + geminiSchema.merge(z.object({ apiProvider: z.literal("gemini") })), + openAiNativeSchema.merge(z.object({ apiProvider: z.literal("openai-native") })), + mistralSchema.merge(z.object({ apiProvider: z.literal("mistral") })), + deepSeekSchema.merge(z.object({ apiProvider: z.literal("deepseek") })), + unboundSchema.merge(z.object({ apiProvider: z.literal("unbound") })), + requestySchema.merge(z.object({ apiProvider: z.literal("requesty") })), + humanRelaySchema.merge(z.object({ apiProvider: z.literal("human-relay") })), + fakeAiSchema.merge(z.object({ apiProvider: z.literal("fake-ai") })), + xaiSchema.merge(z.object({ apiProvider: z.literal("xai") })), + groqSchema.merge(z.object({ apiProvider: z.literal("groq") })), + chutesSchema.merge(z.object({ apiProvider: z.literal("chutes") })), + litellmSchema.merge(z.object({ apiProvider: z.literal("litellm") })), + defaultSchema, +]) + +export const providerSettingsSchema = z.object({ + apiProvider: providerNamesSchema.optional(), + ...anthropicSchema.shape, + ...glamaSchema.shape, + ...openRouterSchema.shape, + ...bedrockSchema.shape, + ...vertexSchema.shape, + ...openAiSchema.shape, + ...ollamaSchema.shape, + ...vsCodeLmSchema.shape, + ...lmStudioSchema.shape, + ...geminiSchema.shape, + ...openAiNativeSchema.shape, + ...mistralSchema.shape, + ...deepSeekSchema.shape, + ...unboundSchema.shape, + ...requestySchema.shape, + ...humanRelaySchema.shape, + ...fakeAiSchema.shape, + ...xaiSchema.shape, + ...groqSchema.shape, + ...chutesSchema.shape, + ...litellmSchema.shape, + ...codebaseIndexProviderSchema.shape, +}) + +export type ProviderSettings = z.infer + +type ProviderSettingsRecord = Record, undefined> + +const providerSettingsRecord: ProviderSettingsRecord = { + apiProvider: undefined, + // Anthropic + apiModelId: undefined, + apiKey: undefined, + anthropicBaseUrl: undefined, + anthropicUseAuthToken: undefined, + // Glama + glamaModelId: undefined, + glamaApiKey: undefined, + // OpenRouter + openRouterApiKey: undefined, + openRouterModelId: undefined, + openRouterBaseUrl: undefined, + openRouterSpecificProvider: undefined, + openRouterUseMiddleOutTransform: undefined, + // Amazon Bedrock + awsAccessKey: undefined, + awsSecretKey: undefined, + awsSessionToken: undefined, + awsRegion: undefined, + awsUseCrossRegionInference: undefined, + awsUsePromptCache: undefined, + awsProfile: undefined, + awsUseProfile: undefined, + awsCustomArn: undefined, + // Google Vertex + vertexKeyFile: undefined, + vertexJsonCredentials: undefined, + vertexProjectId: undefined, + vertexRegion: undefined, + // OpenAI + openAiBaseUrl: undefined, + openAiApiKey: undefined, + openAiLegacyFormat: undefined, + openAiR1FormatEnabled: undefined, + openAiModelId: undefined, + openAiCustomModelInfo: undefined, + openAiUseAzure: undefined, + azureApiVersion: undefined, + openAiStreamingEnabled: undefined, + openAiHostHeader: undefined, // Keep temporarily for backward compatibility during migration + openAiHeaders: undefined, + // Ollama + ollamaModelId: undefined, + ollamaBaseUrl: undefined, + // VS Code LM + vsCodeLmModelSelector: undefined, + lmStudioModelId: undefined, + lmStudioBaseUrl: undefined, + lmStudioDraftModelId: undefined, + lmStudioSpeculativeDecodingEnabled: undefined, + // Gemini + geminiApiKey: undefined, + googleGeminiBaseUrl: undefined, + // OpenAI Native + openAiNativeApiKey: undefined, + openAiNativeBaseUrl: undefined, + // Mistral + mistralApiKey: undefined, + mistralCodestralUrl: undefined, + // DeepSeek + deepSeekBaseUrl: undefined, + deepSeekApiKey: undefined, + // Unbound + unboundApiKey: undefined, + unboundModelId: undefined, + // Requesty + requestyApiKey: undefined, + requestyModelId: undefined, + // Code Index + codeIndexOpenAiKey: undefined, + codeIndexQdrantApiKey: undefined, + // Reasoning + enableReasoningEffort: undefined, + reasoningEffort: undefined, + modelMaxTokens: undefined, + modelMaxThinkingTokens: undefined, + // Generic + includeMaxTokens: undefined, + diffEnabled: undefined, + fuzzyMatchThreshold: undefined, + modelTemperature: undefined, + rateLimitSeconds: undefined, + // Fake AI + fakeAi: undefined, + // X.AI (Grok) + xaiApiKey: undefined, + // Groq + groqApiKey: undefined, + // Chutes AI + chutesApiKey: undefined, + // LiteLLM + litellmBaseUrl: undefined, + litellmApiKey: undefined, + litellmModelId: undefined, +} + +export const PROVIDER_SETTINGS_KEYS = Object.keys(providerSettingsRecord) as Keys[] diff --git a/packages/types/src/telemetry.ts b/packages/types/src/telemetry.ts new file mode 100644 index 0000000000..78e996f766 --- /dev/null +++ b/packages/types/src/telemetry.ts @@ -0,0 +1,134 @@ +import { z } from "zod" + +import { providerNames } from "./provider-settings.js" + +/** + * TelemetrySetting + */ + +export const telemetrySettings = ["unset", "enabled", "disabled"] as const + +export const telemetrySettingsSchema = z.enum(telemetrySettings) + +export type TelemetrySetting = z.infer + +/** + * TelemetryEventName + */ + +export enum TelemetryEventName { + TASK_CREATED = "Task Created", + TASK_RESTARTED = "Task Reopened", + TASK_COMPLETED = "Task Completed", + TASK_CONVERSATION_MESSAGE = "Conversation Message", + LLM_COMPLETION = "LLM Completion", + MODE_SWITCH = "Mode Switched", + TOOL_USED = "Tool Used", + + CHECKPOINT_CREATED = "Checkpoint Created", + CHECKPOINT_RESTORED = "Checkpoint Restored", + CHECKPOINT_DIFFED = "Checkpoint Diffed", + + CONTEXT_CONDENSED = "Context Condensed", + SLIDING_WINDOW_TRUNCATION = "Sliding Window Truncation", + + CODE_ACTION_USED = "Code Action Used", + PROMPT_ENHANCED = "Prompt Enhanced", + + TITLE_BUTTON_CLICKED = "Title Button Clicked", + + AUTHENTICATION_INITIATED = "Authentication Initiated", + + SCHEMA_VALIDATION_ERROR = "Schema Validation Error", + DIFF_APPLICATION_ERROR = "Diff Application Error", + SHELL_INTEGRATION_ERROR = "Shell Integration Error", + CONSECUTIVE_MISTAKE_ERROR = "Consecutive Mistake Error", +} + +/** + * TelemetryProperties + */ + +export const appPropertiesSchema = z.object({ + appVersion: z.string(), + vscodeVersion: z.string(), + platform: z.string(), + editorName: z.string(), + language: z.string(), + mode: z.string(), +}) + +export const taskPropertiesSchema = z.object({ + taskId: z.string().optional(), + apiProvider: z.enum(providerNames).optional(), + modelId: z.string().optional(), + diffStrategy: z.string().optional(), + isSubtask: z.boolean().optional(), +}) + +export const telemetryPropertiesSchema = z.object({ + ...appPropertiesSchema.shape, + ...taskPropertiesSchema.shape, +}) + +export type TelemetryProperties = z.infer + +/** + * TelemetryEvent + */ + +export type TelemetryEvent = { + event: TelemetryEventName + // eslint-disable-next-line @typescript-eslint/no-explicit-any + properties?: Record +} + +/** + * RooCodeTelemetryEvent + */ + +const completionPropertiesSchema = z.object({ + inputTokens: z.number(), + outputTokens: z.number(), + cacheReadTokens: z.number().optional(), + cacheWriteTokens: z.number().optional(), + cost: z.number().optional(), +}) + +export const rooCodeTelemetryEventSchema = z.discriminatedUnion("type", [ + z.object({ + type: z.enum([ + TelemetryEventName.TASK_CREATED, + TelemetryEventName.TASK_RESTARTED, + TelemetryEventName.TASK_COMPLETED, + TelemetryEventName.TASK_CONVERSATION_MESSAGE, + TelemetryEventName.MODE_SWITCH, + TelemetryEventName.TOOL_USED, + TelemetryEventName.CHECKPOINT_CREATED, + TelemetryEventName.CHECKPOINT_RESTORED, + TelemetryEventName.CHECKPOINT_DIFFED, + TelemetryEventName.CODE_ACTION_USED, + TelemetryEventName.PROMPT_ENHANCED, + TelemetryEventName.TITLE_BUTTON_CLICKED, + TelemetryEventName.AUTHENTICATION_INITIATED, + TelemetryEventName.SCHEMA_VALIDATION_ERROR, + TelemetryEventName.DIFF_APPLICATION_ERROR, + TelemetryEventName.SHELL_INTEGRATION_ERROR, + TelemetryEventName.CONSECUTIVE_MISTAKE_ERROR, + ]), + properties: z.object({ + ...appPropertiesSchema.shape, + ...taskPropertiesSchema.shape, + }), + }), + z.object({ + type: z.literal(TelemetryEventName.LLM_COMPLETION), + properties: z.object({ + ...appPropertiesSchema.shape, + ...taskPropertiesSchema.shape, + ...completionPropertiesSchema.shape, + }), + }), +]) + +export type RooCodeTelemetryEvent = z.infer diff --git a/packages/types/src/terminal.ts b/packages/types/src/terminal.ts new file mode 100644 index 0000000000..51d6f252a9 --- /dev/null +++ b/packages/types/src/terminal.ts @@ -0,0 +1,30 @@ +import { z } from "zod" + +/** + * CommandExecutionStatus + */ + +export const commandExecutionStatusSchema = z.discriminatedUnion("status", [ + z.object({ + executionId: z.string(), + status: z.literal("started"), + pid: z.number().optional(), + command: z.string(), + }), + z.object({ + executionId: z.string(), + status: z.literal("output"), + output: z.string(), + }), + z.object({ + executionId: z.string(), + status: z.literal("exited"), + exitCode: z.number().optional(), + }), + z.object({ + executionId: z.string(), + status: z.literal("fallback"), + }), +]) + +export type CommandExecutionStatus = z.infer diff --git a/packages/types/src/tool.ts b/packages/types/src/tool.ts new file mode 100644 index 0000000000..9e807d639d --- /dev/null +++ b/packages/types/src/tool.ts @@ -0,0 +1,54 @@ +import { z } from "zod" + +/** + * ToolGroup + */ + +export const toolGroups = ["read", "edit", "browser", "command", "mcp", "modes"] as const + +export const toolGroupsSchema = z.enum(toolGroups) + +export type ToolGroup = z.infer + +/** + * ToolName + */ + +export const toolNames = [ + "execute_command", + "read_file", + "write_to_file", + "apply_diff", + "insert_content", + "search_and_replace", + "search_files", + "list_files", + "list_code_definition_names", + "browser_action", + "use_mcp_tool", + "access_mcp_resource", + "ask_followup_question", + "attempt_completion", + "switch_mode", + "new_task", + "fetch_instructions", + "codebase_search", +] as const + +export const toolNamesSchema = z.enum(toolNames) + +export type ToolName = z.infer + +/** + * ToolUsage + */ + +export const toolUsageSchema = z.record( + toolNamesSchema, + z.object({ + attempts: z.number(), + failures: z.number(), + }), +) + +export type ToolUsage = z.infer diff --git a/packages/types/src/type-fu.ts b/packages/types/src/type-fu.ts new file mode 100644 index 0000000000..0014e9b187 --- /dev/null +++ b/packages/types/src/type-fu.ts @@ -0,0 +1,11 @@ +/** + * TS + */ + +export type Keys = keyof T + +export type Values = T[keyof T] + +export type Equals = (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? true : false + +export type AssertEqual = T diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts deleted file mode 100644 index 0bb2f71de2..0000000000 --- a/packages/types/src/types.ts +++ /dev/null @@ -1,1344 +0,0 @@ -import { z } from "zod" - -/** - * TS - */ - -export type Keys = keyof T - -export type Values = T[keyof T] - -export type Equals = (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? true : false - -export type AssertEqual = T - -/** - * CodeAction - */ - -export const codeActionIds = ["explainCode", "fixCode", "improveCode", "addToContext", "newTask"] as const - -export type CodeActionId = (typeof codeActionIds)[number] - -export type CodeActionName = "EXPLAIN" | "FIX" | "IMPROVE" | "ADD_TO_CONTEXT" | "NEW_TASK" - -/** - * TerminalAction - */ - -export const terminalActionIds = ["terminalAddToContext", "terminalFixCommand", "terminalExplainCommand"] as const - -export type TerminalActionId = (typeof terminalActionIds)[number] - -export type TerminalActionName = "ADD_TO_CONTEXT" | "FIX" | "EXPLAIN" - -export type TerminalActionPromptType = `TERMINAL_${TerminalActionName}` - -/** - * Command - */ - -export const commandIds = [ - "activationCompleted", - - "plusButtonClicked", - "promptsButtonClicked", - "mcpButtonClicked", - "historyButtonClicked", - "popoutButtonClicked", - "settingsButtonClicked", - - "openInNewTab", - - "showHumanRelayDialog", - "registerHumanRelayCallback", - "unregisterHumanRelayCallback", - "handleHumanRelayResponse", - - "newTask", - - "setCustomStoragePath", - - "focusInput", - "acceptInput", -] as const - -export type CommandId = (typeof commandIds)[number] - -/** - * ProviderName - */ - -export const providerNames = [ - "anthropic", - "glama", - "openrouter", - "bedrock", - "vertex", - "openai", - "ollama", - "vscode-lm", - "lmstudio", - "gemini", - "openai-native", - "mistral", - "deepseek", - "unbound", - "requesty", - "human-relay", - "fake-ai", - "xai", - "groq", - "chutes", - "litellm", -] as const - -export const providerNamesSchema = z.enum(providerNames) - -export type ProviderName = z.infer - -/** - * ToolGroup - */ - -export const toolGroups = ["read", "edit", "browser", "command", "mcp", "modes"] as const - -export const toolGroupsSchema = z.enum(toolGroups) - -export type ToolGroup = z.infer - -/** - * Language - */ - -export const languages = [ - "ca", - "de", - "en", - "es", - "fr", - "hi", - "it", - "ja", - "ko", - "nl", - "pl", - "pt-BR", - "ru", - "tr", - "vi", - "zh-CN", - "zh-TW", -] as const - -export const languagesSchema = z.enum(languages) - -export type Language = z.infer - -export const isLanguage = (value: string): value is Language => languages.includes(value as Language) - -/** - * TelemetrySetting - */ - -export const telemetrySettings = ["unset", "enabled", "disabled"] as const - -export const telemetrySettingsSchema = z.enum(telemetrySettings) - -export type TelemetrySetting = z.infer - -/** - * ReasoningEffort - */ - -export const reasoningEfforts = ["low", "medium", "high"] as const - -export const reasoningEffortsSchema = z.enum(reasoningEfforts) - -export type ReasoningEffort = z.infer - -/** - * ModelParameter - */ - -export const modelParameters = ["max_tokens", "temperature", "reasoning", "include_reasoning"] as const - -export const modelParametersSchema = z.enum(modelParameters) - -export type ModelParameter = z.infer - -export const isModelParameter = (value: string): value is ModelParameter => - modelParameters.includes(value as ModelParameter) - -/** - * ModelInfo - */ - -export const modelInfoSchema = z.object({ - maxTokens: z.number().nullish(), - maxThinkingTokens: z.number().nullish(), - contextWindow: z.number(), - supportsImages: z.boolean().optional(), - supportsComputerUse: z.boolean().optional(), - supportsPromptCache: z.boolean(), - supportsReasoningBudget: z.boolean().optional(), - requiredReasoningBudget: z.boolean().optional(), - supportsReasoningEffort: z.boolean().optional(), - supportedParameters: z.array(modelParametersSchema).optional(), - inputPrice: z.number().optional(), - outputPrice: z.number().optional(), - cacheWritesPrice: z.number().optional(), - cacheReadsPrice: z.number().optional(), - description: z.string().optional(), - reasoningEffort: reasoningEffortsSchema.optional(), - 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 - -/** - * Codebase Index Config - */ -export const codebaseIndexConfigSchema = z.object({ - codebaseIndexEnabled: z.boolean().optional(), - codebaseIndexQdrantUrl: z.string().optional(), - codebaseIndexEmbedderProvider: z.enum(["openai", "ollama"]).optional(), - codebaseIndexEmbedderBaseUrl: z.string().optional(), - codebaseIndexEmbedderModelId: z.string().optional(), -}) - -export type CodebaseIndexConfig = z.infer - -export const codebaseIndexModelsSchema = z.object({ - openai: z.record(z.string(), z.object({ dimension: z.number() })).optional(), - ollama: z.record(z.string(), z.object({ dimension: z.number() })).optional(), -}) - -export type CodebaseIndexModels = z.infer - -export const codebaseIndexProviderSchema = z.object({ - codeIndexOpenAiKey: z.string().optional(), - codeIndexQdrantApiKey: z.string().optional(), -}) - -/** - * HistoryItem - */ - -export const historyItemSchema = z.object({ - id: z.string(), - number: z.number(), - ts: z.number(), - task: z.string(), - tokensIn: z.number(), - tokensOut: z.number(), - cacheWrites: z.number().optional(), - cacheReads: z.number().optional(), - totalCost: z.number(), - size: z.number().optional(), - workspace: z.string().optional(), -}) - -export type HistoryItem = z.infer - -/** - * GroupOptions - */ - -export const groupOptionsSchema = z.object({ - fileRegex: z - .string() - .optional() - .refine( - (pattern) => { - if (!pattern) { - return true // Optional, so empty is valid. - } - - try { - new RegExp(pattern) - return true - } catch { - return false - } - }, - { message: "Invalid regular expression pattern" }, - ), - description: z.string().optional(), -}) - -export type GroupOptions = z.infer - -/** - * GroupEntry - */ - -export const groupEntrySchema = z.union([toolGroupsSchema, z.tuple([toolGroupsSchema, groupOptionsSchema])]) - -export type GroupEntry = z.infer - -/** - * ModeConfig - */ - -const groupEntryArraySchema = z.array(groupEntrySchema).refine( - (groups) => { - const seen = new Set() - - return groups.every((group) => { - // For tuples, check the group name (first element). - const groupName = Array.isArray(group) ? group[0] : group - - if (seen.has(groupName)) { - return false - } - - seen.add(groupName) - return true - }) - }, - { message: "Duplicate groups are not allowed" }, -) - -export const modeConfigSchema = z.object({ - slug: z.string().regex(/^[a-zA-Z0-9-]+$/, "Slug must contain only letters numbers and dashes"), - name: z.string().min(1, "Name is required"), - roleDefinition: z.string().min(1, "Role definition is required"), - whenToUse: z.string().optional(), - customInstructions: z.string().optional(), - groups: groupEntryArraySchema, - source: z.enum(["global", "project"]).optional(), -}) - -export type ModeConfig = z.infer - -/** - * CustomModesSettings - */ - -export const customModesSettingsSchema = z.object({ - customModes: z.array(modeConfigSchema).refine( - (modes) => { - const slugs = new Set() - - return modes.every((mode) => { - if (slugs.has(mode.slug)) { - return false - } - - slugs.add(mode.slug) - return true - }) - }, - { - message: "Duplicate mode slugs are not allowed", - }, - ), -}) - -export type CustomModesSettings = z.infer - -/** - * PromptComponent - */ - -export const promptComponentSchema = z.object({ - roleDefinition: z.string().optional(), - whenToUse: z.string().optional(), - customInstructions: z.string().optional(), -}) - -export type PromptComponent = z.infer - -/** - * CustomModePrompts - */ - -export const customModePromptsSchema = z.record(z.string(), promptComponentSchema.optional()) - -export type CustomModePrompts = z.infer - -/** - * CustomSupportPrompts - */ - -export const customSupportPromptsSchema = z.record(z.string(), z.string().optional()) - -export type CustomSupportPrompts = z.infer - -/** - * CommandExecutionStatus - */ - -export const commandExecutionStatusSchema = z.discriminatedUnion("status", [ - z.object({ - executionId: z.string(), - status: z.literal("started"), - pid: z.number().optional(), - command: z.string(), - }), - z.object({ - executionId: z.string(), - status: z.literal("output"), - output: z.string(), - }), - z.object({ - executionId: z.string(), - status: z.literal("exited"), - exitCode: z.number().optional(), - }), - z.object({ - executionId: z.string(), - status: z.literal("fallback"), - }), -]) - -export type CommandExecutionStatus = z.infer - -/** - * ExperimentId - */ - -export const experimentIds = ["autoCondenseContext", "powerSteering"] as const - -export const experimentIdsSchema = z.enum(experimentIds) - -export type ExperimentId = z.infer - -/** - * Experiments - */ - -const experimentsSchema = z.object({ - autoCondenseContext: z.boolean(), - powerSteering: z.boolean(), -}) - -export type Experiments = z.infer - -type _AssertExperiments = AssertEqual>> - -/** - * ProviderSettingsEntry - */ - -export const providerSettingsEntrySchema = z.object({ - id: z.string(), - name: z.string(), - apiProvider: providerNamesSchema.optional(), -}) - -export type ProviderSettingsEntry = z.infer - -/** - * ProviderSettings - */ - -const baseProviderSettingsSchema = z.object({ - includeMaxTokens: z.boolean().optional(), - diffEnabled: z.boolean().optional(), - fuzzyMatchThreshold: z.number().optional(), - modelTemperature: z.number().nullish(), - rateLimitSeconds: z.number().optional(), - - // Model reasoning. - enableReasoningEffort: z.boolean().optional(), - reasoningEffort: reasoningEffortsSchema.optional(), - modelMaxTokens: z.number().optional(), - modelMaxThinkingTokens: z.number().optional(), -}) - -// Several of the providers share common model config properties. -const apiModelIdProviderModelSchema = baseProviderSettingsSchema.extend({ - apiModelId: z.string().optional(), -}) - -const anthropicSchema = apiModelIdProviderModelSchema.extend({ - apiKey: z.string().optional(), - anthropicBaseUrl: z.string().optional(), - anthropicUseAuthToken: z.boolean().optional(), -}) - -const glamaSchema = baseProviderSettingsSchema.extend({ - glamaModelId: z.string().optional(), - glamaApiKey: z.string().optional(), -}) - -const openRouterSchema = baseProviderSettingsSchema.extend({ - openRouterApiKey: z.string().optional(), - openRouterModelId: z.string().optional(), - openRouterBaseUrl: z.string().optional(), - openRouterSpecificProvider: z.string().optional(), - openRouterUseMiddleOutTransform: z.boolean().optional(), -}) - -const bedrockSchema = apiModelIdProviderModelSchema.extend({ - awsAccessKey: z.string().optional(), - awsSecretKey: z.string().optional(), - awsSessionToken: z.string().optional(), - awsRegion: z.string().optional(), - awsUseCrossRegionInference: z.boolean().optional(), - awsUsePromptCache: z.boolean().optional(), - awsProfile: z.string().optional(), - awsUseProfile: z.boolean().optional(), - awsCustomArn: z.string().optional(), -}) - -const vertexSchema = apiModelIdProviderModelSchema.extend({ - vertexKeyFile: z.string().optional(), - vertexJsonCredentials: z.string().optional(), - vertexProjectId: z.string().optional(), - vertexRegion: z.string().optional(), -}) - -const openAiSchema = baseProviderSettingsSchema.extend({ - openAiBaseUrl: z.string().optional(), - openAiApiKey: z.string().optional(), - openAiLegacyFormat: z.boolean().optional(), - openAiR1FormatEnabled: z.boolean().optional(), - openAiModelId: z.string().optional(), - openAiCustomModelInfo: modelInfoSchema.nullish(), - openAiUseAzure: z.boolean().optional(), - azureApiVersion: z.string().optional(), - openAiStreamingEnabled: z.boolean().optional(), - openAiHostHeader: z.string().optional(), // Keep temporarily for backward compatibility during migration. - openAiHeaders: z.record(z.string(), z.string()).optional(), -}) - -const ollamaSchema = baseProviderSettingsSchema.extend({ - ollamaModelId: z.string().optional(), - ollamaBaseUrl: z.string().optional(), -}) - -const vsCodeLmSchema = baseProviderSettingsSchema.extend({ - vsCodeLmModelSelector: z - .object({ - vendor: z.string().optional(), - family: z.string().optional(), - version: z.string().optional(), - id: z.string().optional(), - }) - .optional(), -}) - -const lmStudioSchema = baseProviderSettingsSchema.extend({ - lmStudioModelId: z.string().optional(), - lmStudioBaseUrl: z.string().optional(), - lmStudioDraftModelId: z.string().optional(), - lmStudioSpeculativeDecodingEnabled: z.boolean().optional(), -}) - -const geminiSchema = apiModelIdProviderModelSchema.extend({ - geminiApiKey: z.string().optional(), - googleGeminiBaseUrl: z.string().optional(), -}) - -const openAiNativeSchema = apiModelIdProviderModelSchema.extend({ - openAiNativeApiKey: z.string().optional(), - openAiNativeBaseUrl: z.string().optional(), -}) - -const mistralSchema = apiModelIdProviderModelSchema.extend({ - mistralApiKey: z.string().optional(), - mistralCodestralUrl: z.string().optional(), -}) - -const deepSeekSchema = apiModelIdProviderModelSchema.extend({ - deepSeekBaseUrl: z.string().optional(), - deepSeekApiKey: z.string().optional(), -}) - -const unboundSchema = baseProviderSettingsSchema.extend({ - unboundApiKey: z.string().optional(), - unboundModelId: z.string().optional(), -}) - -const requestySchema = baseProviderSettingsSchema.extend({ - requestyApiKey: z.string().optional(), - requestyModelId: z.string().optional(), -}) - -const humanRelaySchema = baseProviderSettingsSchema - -const fakeAiSchema = baseProviderSettingsSchema.extend({ - fakeAi: z.unknown().optional(), -}) - -const xaiSchema = apiModelIdProviderModelSchema.extend({ - xaiApiKey: z.string().optional(), -}) - -const groqSchema = apiModelIdProviderModelSchema.extend({ - groqApiKey: z.string().optional(), -}) - -const chutesSchema = apiModelIdProviderModelSchema.extend({ - chutesApiKey: z.string().optional(), -}) - -const litellmSchema = baseProviderSettingsSchema.extend({ - litellmBaseUrl: z.string().optional(), - litellmApiKey: z.string().optional(), - litellmModelId: z.string().optional(), -}) - -const defaultSchema = z.object({ - apiProvider: z.undefined(), -}) - -export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProvider", [ - anthropicSchema.merge(z.object({ apiProvider: z.literal("anthropic") })), - glamaSchema.merge(z.object({ apiProvider: z.literal("glama") })), - openRouterSchema.merge(z.object({ apiProvider: z.literal("openrouter") })), - bedrockSchema.merge(z.object({ apiProvider: z.literal("bedrock") })), - vertexSchema.merge(z.object({ apiProvider: z.literal("vertex") })), - openAiSchema.merge(z.object({ apiProvider: z.literal("openai") })), - ollamaSchema.merge(z.object({ apiProvider: z.literal("ollama") })), - vsCodeLmSchema.merge(z.object({ apiProvider: z.literal("vscode-lm") })), - lmStudioSchema.merge(z.object({ apiProvider: z.literal("lmstudio") })), - geminiSchema.merge(z.object({ apiProvider: z.literal("gemini") })), - openAiNativeSchema.merge(z.object({ apiProvider: z.literal("openai-native") })), - mistralSchema.merge(z.object({ apiProvider: z.literal("mistral") })), - deepSeekSchema.merge(z.object({ apiProvider: z.literal("deepseek") })), - unboundSchema.merge(z.object({ apiProvider: z.literal("unbound") })), - requestySchema.merge(z.object({ apiProvider: z.literal("requesty") })), - humanRelaySchema.merge(z.object({ apiProvider: z.literal("human-relay") })), - fakeAiSchema.merge(z.object({ apiProvider: z.literal("fake-ai") })), - xaiSchema.merge(z.object({ apiProvider: z.literal("xai") })), - groqSchema.merge(z.object({ apiProvider: z.literal("groq") })), - chutesSchema.merge(z.object({ apiProvider: z.literal("chutes") })), - litellmSchema.merge(z.object({ apiProvider: z.literal("litellm") })), - defaultSchema, -]) - -export const providerSettingsSchema = z.object({ - apiProvider: providerNamesSchema.optional(), - ...anthropicSchema.shape, - ...glamaSchema.shape, - ...openRouterSchema.shape, - ...bedrockSchema.shape, - ...vertexSchema.shape, - ...openAiSchema.shape, - ...ollamaSchema.shape, - ...vsCodeLmSchema.shape, - ...lmStudioSchema.shape, - ...geminiSchema.shape, - ...openAiNativeSchema.shape, - ...mistralSchema.shape, - ...deepSeekSchema.shape, - ...unboundSchema.shape, - ...requestySchema.shape, - ...humanRelaySchema.shape, - ...fakeAiSchema.shape, - ...xaiSchema.shape, - ...groqSchema.shape, - ...chutesSchema.shape, - ...litellmSchema.shape, - ...codebaseIndexProviderSchema.shape, -}) - -export type ProviderSettings = z.infer - -type ProviderSettingsRecord = Record, undefined> - -const providerSettingsRecord: ProviderSettingsRecord = { - apiProvider: undefined, - // Anthropic - apiModelId: undefined, - apiKey: undefined, - anthropicBaseUrl: undefined, - anthropicUseAuthToken: undefined, - // Glama - glamaModelId: undefined, - glamaApiKey: undefined, - // OpenRouter - openRouterApiKey: undefined, - openRouterModelId: undefined, - openRouterBaseUrl: undefined, - openRouterSpecificProvider: undefined, - openRouterUseMiddleOutTransform: undefined, - // Amazon Bedrock - awsAccessKey: undefined, - awsSecretKey: undefined, - awsSessionToken: undefined, - awsRegion: undefined, - awsUseCrossRegionInference: undefined, - awsUsePromptCache: undefined, - awsProfile: undefined, - awsUseProfile: undefined, - awsCustomArn: undefined, - // Google Vertex - vertexKeyFile: undefined, - vertexJsonCredentials: undefined, - vertexProjectId: undefined, - vertexRegion: undefined, - // OpenAI - openAiBaseUrl: undefined, - openAiApiKey: undefined, - openAiLegacyFormat: undefined, - openAiR1FormatEnabled: undefined, - openAiModelId: undefined, - openAiCustomModelInfo: undefined, - openAiUseAzure: undefined, - azureApiVersion: undefined, - openAiStreamingEnabled: undefined, - openAiHostHeader: undefined, // Keep temporarily for backward compatibility during migration - openAiHeaders: undefined, - // Ollama - ollamaModelId: undefined, - ollamaBaseUrl: undefined, - // VS Code LM - vsCodeLmModelSelector: undefined, - lmStudioModelId: undefined, - lmStudioBaseUrl: undefined, - lmStudioDraftModelId: undefined, - lmStudioSpeculativeDecodingEnabled: undefined, - // Gemini - geminiApiKey: undefined, - googleGeminiBaseUrl: undefined, - // OpenAI Native - openAiNativeApiKey: undefined, - openAiNativeBaseUrl: undefined, - // Mistral - mistralApiKey: undefined, - mistralCodestralUrl: undefined, - // DeepSeek - deepSeekBaseUrl: undefined, - deepSeekApiKey: undefined, - // Unbound - unboundApiKey: undefined, - unboundModelId: undefined, - // Requesty - requestyApiKey: undefined, - requestyModelId: undefined, - // Code Index - codeIndexOpenAiKey: undefined, - codeIndexQdrantApiKey: undefined, - // Reasoning - enableReasoningEffort: undefined, - reasoningEffort: undefined, - modelMaxTokens: undefined, - modelMaxThinkingTokens: undefined, - // Generic - includeMaxTokens: undefined, - diffEnabled: undefined, - fuzzyMatchThreshold: undefined, - modelTemperature: undefined, - rateLimitSeconds: undefined, - // Fake AI - fakeAi: undefined, - // X.AI (Grok) - xaiApiKey: undefined, - // Groq - groqApiKey: undefined, - // Chutes AI - chutesApiKey: undefined, - // LiteLLM - litellmBaseUrl: undefined, - litellmApiKey: undefined, - litellmModelId: undefined, -} - -export const PROVIDER_SETTINGS_KEYS = Object.keys(providerSettingsRecord) as Keys[] - -/** - * GlobalSettings - */ - -export const globalSettingsSchema = z.object({ - currentApiConfigName: z.string().optional(), - listApiConfigMeta: z.array(providerSettingsEntrySchema).optional(), - pinnedApiConfigs: z.record(z.string(), z.boolean()).optional(), - - lastShownAnnouncementId: z.string().optional(), - customInstructions: z.string().optional(), - taskHistory: z.array(historyItemSchema).optional(), - - condensingApiConfigId: z.string().optional(), - customCondensingPrompt: z.string().optional(), - - autoApprovalEnabled: z.boolean().optional(), - alwaysAllowReadOnly: z.boolean().optional(), - alwaysAllowReadOnlyOutsideWorkspace: z.boolean().optional(), - codebaseIndexModels: codebaseIndexModelsSchema.optional(), - codebaseIndexConfig: codebaseIndexConfigSchema.optional(), - alwaysAllowWrite: z.boolean().optional(), - alwaysAllowWriteOutsideWorkspace: z.boolean().optional(), - writeDelayMs: z.number().optional(), - alwaysAllowBrowser: z.boolean().optional(), - alwaysApproveResubmit: z.boolean().optional(), - requestDelaySeconds: z.number().optional(), - alwaysAllowMcp: z.boolean().optional(), - alwaysAllowModeSwitch: z.boolean().optional(), - alwaysAllowSubtasks: z.boolean().optional(), - alwaysAllowExecute: z.boolean().optional(), - allowedCommands: z.array(z.string()).optional(), - allowedMaxRequests: z.number().nullish(), - autoCondenseContextPercent: z.number().optional(), - - browserToolEnabled: z.boolean().optional(), - browserViewportSize: z.string().optional(), - screenshotQuality: z.number().optional(), - remoteBrowserEnabled: z.boolean().optional(), - remoteBrowserHost: z.string().optional(), - cachedChromeHostUrl: z.string().optional(), - - enableCheckpoints: z.boolean().optional(), - - ttsEnabled: z.boolean().optional(), - ttsSpeed: z.number().optional(), - soundEnabled: z.boolean().optional(), - soundVolume: z.number().optional(), - - maxOpenTabsContext: z.number().optional(), - maxWorkspaceFiles: z.number().optional(), - showRooIgnoredFiles: z.boolean().optional(), - maxReadFileLine: z.number().optional(), - - terminalOutputLineLimit: z.number().optional(), - terminalShellIntegrationTimeout: z.number().optional(), - terminalShellIntegrationDisabled: z.boolean().optional(), - terminalCommandDelay: z.number().optional(), - terminalPowershellCounter: z.boolean().optional(), - terminalZshClearEolMark: z.boolean().optional(), - terminalZshOhMy: z.boolean().optional(), - terminalZshP10k: z.boolean().optional(), - terminalZdotdir: z.boolean().optional(), - terminalCompressProgressBar: z.boolean().optional(), - - rateLimitSeconds: z.number().optional(), - diffEnabled: z.boolean().optional(), - fuzzyMatchThreshold: z.number().optional(), - experiments: experimentsSchema.optional(), - - language: languagesSchema.optional(), - - telemetrySetting: telemetrySettingsSchema.optional(), - - mcpEnabled: z.boolean().optional(), - enableMcpServerCreation: z.boolean().optional(), - - mode: z.string().optional(), - modeApiConfigs: z.record(z.string(), z.string()).optional(), - customModes: z.array(modeConfigSchema).optional(), - customModePrompts: customModePromptsSchema.optional(), - customSupportPrompts: customSupportPromptsSchema.optional(), - enhancementApiConfigId: z.string().optional(), - historyPreviewCollapsed: z.boolean().optional(), -}) - -export type GlobalSettings = z.infer - -type GlobalSettingsRecord = Record, undefined> - -const globalSettingsRecord: GlobalSettingsRecord = { - codebaseIndexModels: undefined, - codebaseIndexConfig: undefined, - currentApiConfigName: undefined, - listApiConfigMeta: undefined, - pinnedApiConfigs: undefined, - - lastShownAnnouncementId: undefined, - customInstructions: undefined, - taskHistory: undefined, - - condensingApiConfigId: undefined, - customCondensingPrompt: undefined, - - autoApprovalEnabled: undefined, - alwaysAllowReadOnly: undefined, - alwaysAllowReadOnlyOutsideWorkspace: undefined, - alwaysAllowWrite: undefined, - alwaysAllowWriteOutsideWorkspace: undefined, - writeDelayMs: undefined, - alwaysAllowBrowser: undefined, - alwaysApproveResubmit: undefined, - requestDelaySeconds: undefined, - alwaysAllowMcp: undefined, - alwaysAllowModeSwitch: undefined, - alwaysAllowSubtasks: undefined, - alwaysAllowExecute: undefined, - allowedCommands: undefined, - allowedMaxRequests: undefined, - autoCondenseContextPercent: undefined, - - browserToolEnabled: undefined, - browserViewportSize: undefined, - screenshotQuality: undefined, - remoteBrowserEnabled: undefined, - remoteBrowserHost: undefined, - - enableCheckpoints: undefined, - - ttsEnabled: undefined, - ttsSpeed: undefined, - soundEnabled: undefined, - soundVolume: undefined, - - maxOpenTabsContext: undefined, - maxWorkspaceFiles: undefined, - showRooIgnoredFiles: undefined, - maxReadFileLine: undefined, - - terminalOutputLineLimit: undefined, - terminalShellIntegrationTimeout: undefined, - terminalShellIntegrationDisabled: undefined, - terminalCommandDelay: undefined, - terminalPowershellCounter: undefined, - terminalZshClearEolMark: undefined, - terminalZshOhMy: undefined, - terminalZshP10k: undefined, - terminalZdotdir: undefined, - terminalCompressProgressBar: undefined, - - rateLimitSeconds: undefined, - diffEnabled: undefined, - fuzzyMatchThreshold: undefined, - experiments: undefined, - - language: undefined, - - telemetrySetting: undefined, - - mcpEnabled: undefined, - enableMcpServerCreation: undefined, - - mode: undefined, - modeApiConfigs: undefined, - customModes: undefined, - customModePrompts: undefined, - customSupportPrompts: undefined, - enhancementApiConfigId: undefined, - cachedChromeHostUrl: undefined, - historyPreviewCollapsed: undefined, -} - -export const GLOBAL_SETTINGS_KEYS = Object.keys(globalSettingsRecord) as Keys[] - -/** - * RooCodeSettings - */ - -export const rooCodeSettingsSchema = providerSettingsSchema.merge(globalSettingsSchema) - -export type RooCodeSettings = GlobalSettings & ProviderSettings - -/** - * SecretState - */ - -export type SecretState = Pick< - ProviderSettings, - | "apiKey" - | "glamaApiKey" - | "openRouterApiKey" - | "awsAccessKey" - | "awsSecretKey" - | "awsSessionToken" - | "openAiApiKey" - | "geminiApiKey" - | "openAiNativeApiKey" - | "deepSeekApiKey" - | "mistralApiKey" - | "unboundApiKey" - | "requestyApiKey" - | "xaiApiKey" - | "groqApiKey" - | "chutesApiKey" - | "litellmApiKey" - | "codeIndexOpenAiKey" - | "codeIndexQdrantApiKey" -> - -export type CodeIndexSecrets = "codeIndexOpenAiKey" | "codeIndexQdrantApiKey" - -type SecretStateRecord = Record, undefined> - -const secretStateRecord: SecretStateRecord = { - apiKey: undefined, - glamaApiKey: undefined, - openRouterApiKey: undefined, - awsAccessKey: undefined, - awsSecretKey: undefined, - awsSessionToken: undefined, - openAiApiKey: undefined, - geminiApiKey: undefined, - openAiNativeApiKey: undefined, - deepSeekApiKey: undefined, - mistralApiKey: undefined, - unboundApiKey: undefined, - requestyApiKey: undefined, - xaiApiKey: undefined, - groqApiKey: undefined, - chutesApiKey: undefined, - litellmApiKey: undefined, - codeIndexOpenAiKey: undefined, - codeIndexQdrantApiKey: undefined, -} - -export const SECRET_STATE_KEYS = Object.keys(secretStateRecord) as Keys[] - -export const isSecretStateKey = (key: string): key is Keys => - SECRET_STATE_KEYS.includes(key as Keys) - -/** - * GlobalState - */ - -export type GlobalState = Omit> - -export const GLOBAL_STATE_KEYS = [...GLOBAL_SETTINGS_KEYS, ...PROVIDER_SETTINGS_KEYS].filter( - (key: Keys) => !SECRET_STATE_KEYS.includes(key as Keys), -) as Keys[] - -export const isGlobalStateKey = (key: string): key is Keys => - GLOBAL_STATE_KEYS.includes(key as Keys) - -/** - * ClineAsk - */ - -export const clineAsks = [ - "followup", - "command", - "command_output", - "completion_result", - "tool", - "api_req_failed", - "resume_task", - "resume_completed_task", - "mistake_limit_reached", - "browser_action_launch", - "use_mcp_server", - "auto_approval_max_req_reached", -] as const - -export const clineAskSchema = z.enum(clineAsks) - -export type ClineAsk = z.infer - -// ClineSay - -export const clineSays = [ - "error", - "api_req_started", - "api_req_finished", - "api_req_retried", - "api_req_retry_delayed", - "api_req_deleted", - "text", - "reasoning", - "completion_result", - "user_feedback", - "user_feedback_diff", - "command_output", - "shell_integration_warning", - "browser_action", - "browser_action_result", - "mcp_server_request_started", - "mcp_server_response", - "subtask_result", - "checkpoint_saved", - "rooignore_error", - "diff_error", - "condense_context", - "codebase_search_result", -] as const - -export const clineSaySchema = z.enum(clineSays) - -export type ClineSay = z.infer - -/** - * ToolProgressStatus - */ - -export const toolProgressStatusSchema = z.object({ - icon: z.string().optional(), - text: z.string().optional(), -}) - -export type ToolProgressStatus = z.infer - -/** - * ContextCondense - */ - -export const contextCondenseSchema = z.object({ - cost: z.number(), - prevContextTokens: z.number(), - newContextTokens: z.number(), - summary: z.string(), -}) - -export type ContextCondense = z.infer - -/** - * ClineMessage - */ - -export const clineMessageSchema = z.object({ - ts: z.number(), - type: z.union([z.literal("ask"), z.literal("say")]), - ask: clineAskSchema.optional(), - say: clineSaySchema.optional(), - text: z.string().optional(), - images: z.array(z.string()).optional(), - partial: z.boolean().optional(), - reasoning: z.string().optional(), - conversationHistoryIndex: z.number().optional(), - checkpoint: z.record(z.string(), z.unknown()).optional(), - progressStatus: toolProgressStatusSchema.optional(), - contextCondense: contextCondenseSchema.optional(), -}) - -export type ClineMessage = z.infer - -/** - * TokenUsage - */ - -export const tokenUsageSchema = z.object({ - totalTokensIn: z.number(), - totalTokensOut: z.number(), - totalCacheWrites: z.number().optional(), - totalCacheReads: z.number().optional(), - totalCost: z.number(), - contextTokens: z.number(), -}) - -export type TokenUsage = z.infer - -/** - * ToolName - */ - -export const toolNames = [ - "execute_command", - "read_file", - "write_to_file", - "apply_diff", - "insert_content", - "search_and_replace", - "search_files", - "list_files", - "list_code_definition_names", - "browser_action", - "use_mcp_tool", - "access_mcp_resource", - "ask_followup_question", - "attempt_completion", - "switch_mode", - "new_task", - "fetch_instructions", - "codebase_search", -] as const - -export const toolNamesSchema = z.enum(toolNames) - -export type ToolName = z.infer - -/** - * ToolUsage - */ - -export const toolUsageSchema = z.record( - toolNamesSchema, - z.object({ - attempts: z.number(), - failures: z.number(), - }), -) - -export type ToolUsage = z.infer - -/** - * RooCodeEvent - */ - -export enum RooCodeEventName { - Message = "message", - TaskCreated = "taskCreated", - TaskStarted = "taskStarted", - TaskModeSwitched = "taskModeSwitched", - TaskPaused = "taskPaused", - TaskUnpaused = "taskUnpaused", - TaskAskResponded = "taskAskResponded", - TaskAborted = "taskAborted", - TaskSpawned = "taskSpawned", - TaskCompleted = "taskCompleted", - TaskTokenUsageUpdated = "taskTokenUsageUpdated", - TaskToolFailed = "taskToolFailed", -} - -export const rooCodeEventsSchema = z.object({ - [RooCodeEventName.Message]: z.tuple([ - z.object({ - taskId: z.string(), - action: z.union([z.literal("created"), z.literal("updated")]), - message: clineMessageSchema, - }), - ]), - [RooCodeEventName.TaskCreated]: z.tuple([z.string()]), - [RooCodeEventName.TaskStarted]: z.tuple([z.string()]), - [RooCodeEventName.TaskModeSwitched]: z.tuple([z.string(), z.string()]), - [RooCodeEventName.TaskPaused]: z.tuple([z.string()]), - [RooCodeEventName.TaskUnpaused]: z.tuple([z.string()]), - [RooCodeEventName.TaskAskResponded]: z.tuple([z.string()]), - [RooCodeEventName.TaskAborted]: z.tuple([z.string()]), - [RooCodeEventName.TaskSpawned]: z.tuple([z.string(), z.string()]), - [RooCodeEventName.TaskCompleted]: z.tuple([z.string(), tokenUsageSchema, toolUsageSchema]), - [RooCodeEventName.TaskTokenUsageUpdated]: z.tuple([z.string(), tokenUsageSchema]), - [RooCodeEventName.TaskToolFailed]: z.tuple([z.string(), toolNamesSchema, z.string()]), -}) - -export type RooCodeEvents = z.infer - -/** - * Ack - */ - -export const ackSchema = z.object({ - clientId: z.string(), - pid: z.number(), - ppid: z.number(), -}) - -export type Ack = z.infer - -/** - * TaskCommand - */ - -export enum TaskCommandName { - StartNewTask = "StartNewTask", - CancelTask = "CancelTask", - CloseTask = "CloseTask", -} - -export const taskCommandSchema = z.discriminatedUnion("commandName", [ - z.object({ - commandName: z.literal(TaskCommandName.StartNewTask), - data: z.object({ - configuration: rooCodeSettingsSchema, - text: z.string(), - images: z.array(z.string()).optional(), - newTab: z.boolean().optional(), - }), - }), - z.object({ - commandName: z.literal(TaskCommandName.CancelTask), - data: z.string(), - }), - z.object({ - commandName: z.literal(TaskCommandName.CloseTask), - data: z.string(), - }), -]) - -export type TaskCommand = z.infer - -/** - * TaskEvent - */ - -export const taskEventSchema = z.discriminatedUnion("eventName", [ - z.object({ - eventName: z.literal(RooCodeEventName.Message), - payload: rooCodeEventsSchema.shape[RooCodeEventName.Message], - }), - z.object({ - eventName: z.literal(RooCodeEventName.TaskCreated), - payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskCreated], - }), - z.object({ - eventName: z.literal(RooCodeEventName.TaskStarted), - payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskStarted], - }), - z.object({ - eventName: z.literal(RooCodeEventName.TaskModeSwitched), - payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskModeSwitched], - }), - z.object({ - eventName: z.literal(RooCodeEventName.TaskPaused), - payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskPaused], - }), - z.object({ - eventName: z.literal(RooCodeEventName.TaskUnpaused), - payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskUnpaused], - }), - z.object({ - eventName: z.literal(RooCodeEventName.TaskAskResponded), - payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskAskResponded], - }), - z.object({ - eventName: z.literal(RooCodeEventName.TaskAborted), - payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskAborted], - }), - z.object({ - eventName: z.literal(RooCodeEventName.TaskSpawned), - payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskSpawned], - }), - z.object({ - eventName: z.literal(RooCodeEventName.TaskCompleted), - payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskCompleted], - }), - z.object({ - eventName: z.literal(RooCodeEventName.TaskTokenUsageUpdated), - payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskTokenUsageUpdated], - }), -]) - -export type TaskEvent = z.infer - -/** - * IpcMessage - */ - -export enum IpcMessageType { - Connect = "Connect", - Disconnect = "Disconnect", - Ack = "Ack", - TaskCommand = "TaskCommand", - TaskEvent = "TaskEvent", -} - -export enum IpcOrigin { - Client = "client", - Server = "server", -} - -export const ipcMessageSchema = z.discriminatedUnion("type", [ - z.object({ - type: z.literal(IpcMessageType.Ack), - origin: z.literal(IpcOrigin.Server), - data: ackSchema, - }), - z.object({ - type: z.literal(IpcMessageType.TaskCommand), - origin: z.literal(IpcOrigin.Client), - clientId: z.string(), - data: taskCommandSchema, - }), - z.object({ - type: z.literal(IpcMessageType.TaskEvent), - origin: z.literal(IpcOrigin.Server), - relayClientId: z.string().optional(), - data: taskEventSchema, - }), -]) - -export type IpcMessage = z.infer diff --git a/packages/types/src/vscode.ts b/packages/types/src/vscode.ts new file mode 100644 index 0000000000..f12b71cdf7 --- /dev/null +++ b/packages/types/src/vscode.ts @@ -0,0 +1,84 @@ +import { z } from "zod" + +/** + * CodeAction + */ + +export const codeActionIds = ["explainCode", "fixCode", "improveCode", "addToContext", "newTask"] as const + +export type CodeActionId = (typeof codeActionIds)[number] + +export type CodeActionName = "EXPLAIN" | "FIX" | "IMPROVE" | "ADD_TO_CONTEXT" | "NEW_TASK" + +/** + * TerminalAction + */ + +export const terminalActionIds = ["terminalAddToContext", "terminalFixCommand", "terminalExplainCommand"] as const + +export type TerminalActionId = (typeof terminalActionIds)[number] + +export type TerminalActionName = "ADD_TO_CONTEXT" | "FIX" | "EXPLAIN" + +export type TerminalActionPromptType = `TERMINAL_${TerminalActionName}` + +/** + * Command + */ + +export const commandIds = [ + "activationCompleted", + + "plusButtonClicked", + "promptsButtonClicked", + "mcpButtonClicked", + "historyButtonClicked", + "popoutButtonClicked", + "settingsButtonClicked", + + "openInNewTab", + + "showHumanRelayDialog", + "registerHumanRelayCallback", + "unregisterHumanRelayCallback", + "handleHumanRelayResponse", + + "newTask", + + "setCustomStoragePath", + + "focusInput", + "acceptInput", +] as const + +export type CommandId = (typeof commandIds)[number] + +/** + * Language + */ + +export const languages = [ + "ca", + "de", + "en", + "es", + "fr", + "hi", + "it", + "ja", + "ko", + "nl", + "pl", + "pt-BR", + "ru", + "tr", + "vi", + "zh-CN", + "zh-TW", +] as const + +export const languagesSchema = z.enum(languages) + +export type Language = z.infer + +export const isLanguage = (value: string): value is Language => languages.includes(value as Language) diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index c53385d2e3..c9f2b4a100 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -1323,6 +1323,21 @@ export class Task extends EventEmitter { } finally { this.isStreaming = false } + if ( + inputTokens > 0 || + outputTokens > 0 || + cacheWriteTokens > 0 || + cacheReadTokens > 0 || + typeof totalCost !== "undefined" + ) { + telemetryService.captureLlmCompletion(this.taskId, { + inputTokens, + outputTokens, + cacheWriteTokens, + cacheReadTokens, + cost: totalCost, + }) + } // Need to call here in case the stream was aborted. if (this.abort || this.abandoned) { diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 25db95ac2a..13121531a7 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -15,6 +15,7 @@ import type { ProviderSettings, RooCodeSettings, ProviderSettingsEntry, + TelemetryProperties, CodeActionId, CodeActionName, TerminalActionId, @@ -52,7 +53,7 @@ import { Task, TaskOptions } from "../task/Task" import { getNonce } from "./getNonce" import { getUri } from "./getUri" import { getSystemPromptFilePath } from "../prompts/sections/custom-system-prompt" -import { telemetryService } from "../../services/telemetry/TelemetryService" +import { TelemetryPropertiesProvider, telemetryService } from "../../services/telemetry" import { getWorkspacePath } from "../../utils/path" import { webviewMessageHandler } from "./webviewMessageHandler" import { WebviewMessage } from "../../shared/WebviewMessage" @@ -67,7 +68,10 @@ export type ClineProviderEvents = { clineCreated: [cline: Task] } -export class ClineProvider extends EventEmitter implements vscode.WebviewViewProvider { +export class ClineProvider + extends EventEmitter + implements vscode.WebviewViewProvider, TelemetryPropertiesProvider +{ // Used in package.json as the view's id. This value cannot be changed due // to how VSCode caches views based on their id, and updating the id would // break existing instances of the extension. @@ -1566,59 +1570,21 @@ export class ClineProvider extends EventEmitter implements * This method is called by the telemetry service to get context information * like the current mode, API provider, etc. */ - public async getTelemetryProperties(): Promise> { + public async getTelemetryProperties(): Promise { const { mode, apiConfiguration, language } = await this.getState() - const appVersion = this.context.extension?.packageJSON?.version - const vscodeVersion = vscode.version - const platform = process.platform - const editorName = vscode.env.appName // Get the editor name (VS Code, Cursor, etc.) + const task = this.getCurrentCline() - const properties: Record = { - vscodeVersion, - platform, - editorName, + return { + appVersion: this.context.extension?.packageJSON?.version, + vscodeVersion: vscode.version, + platform: process.platform, + editorName: vscode.env.appName, + language, + mode, + apiProvider: apiConfiguration?.apiProvider, + modelId: task?.api?.getModel().id, + diffStrategy: task?.diffStrategy?.getName(), + isSubtask: task ? !!task.parentTask : undefined, } - - // Add extension version - if (appVersion) { - properties.appVersion = appVersion - } - - // Add language - if (language) { - properties.language = language - } - - // Add current mode - if (mode) { - properties.mode = mode - } - - // Add API provider - if (apiConfiguration?.apiProvider) { - properties.apiProvider = apiConfiguration.apiProvider - } - - // Add model ID if available - const currentCline = this.getCurrentCline() - - if (currentCline?.api) { - const { id: modelId } = currentCline.api.getModel() - - if (modelId) { - properties.modelId = modelId - } - } - - if (currentCline?.diffStrategy) { - properties.diffStrategy = currentCline.diffStrategy.getName() - } - - // Add isSubtask property that indicates whether this task is a subtask - if (currentCline) { - properties.isSubtask = !!currentCline.parentTask - } - - return properties } } diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 0acae75884..1a0b64605d 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -29,7 +29,7 @@ import { getOllamaModels } from "../../api/providers/ollama" import { getVsCodeLmModels } from "../../api/providers/vscode-lm" import { getLmStudioModels } from "../../api/providers/lmstudio" import { openMention } from "../mentions" -import { telemetryService } from "../../services/telemetry/TelemetryService" +import { telemetryService } from "../../services/telemetry" import { TelemetrySetting } from "../../shared/TelemetrySetting" import { getWorkspacePath } from "../../utils/path" import { Mode, defaultModeSlug } from "../../shared/modes" diff --git a/src/extension.ts b/src/extension.ts index db4edd7b26..70d078363d 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -58,7 +58,7 @@ export async function activate(context: vscode.ExtensionContext) { await migrateSettings(context, outputChannel) // Initialize telemetry service after environment variables are loaded. - telemetryService.initialize() + telemetryService.initialize(context) // Initialize i18n for internationalization support initializeI18n(context.globalState.get("language") ?? formatLanguage(vscode.env.language)) diff --git a/src/services/telemetry/PostHogClient.ts b/src/services/telemetry/PostHogClient.ts deleted file mode 100644 index 22fce0beb3..0000000000 --- a/src/services/telemetry/PostHogClient.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { PostHog } from "posthog-node" -import * as vscode from "vscode" - -import { logger } from "../../utils/logging" - -// This forward declaration is needed to avoid circular dependencies -export interface ClineProviderInterface { - // Gets telemetry properties to attach to every event - getTelemetryProperties(): Promise> -} - -/** - * PostHogClient handles telemetry event tracking for the Roo Code extension - * Uses PostHog analytics to track user interactions and system events - * Respects user privacy settings and VSCode's global telemetry configuration - */ -export class PostHogClient { - public static readonly EVENTS = { - TASK: { - CREATED: "Task Created", - RESTARTED: "Task Reopened", - COMPLETED: "Task Completed", - CONVERSATION_MESSAGE: "Conversation Message", - MODE_SWITCH: "Mode Switched", - TOOL_USED: "Tool Used", - CHECKPOINT_CREATED: "Checkpoint Created", - CHECKPOINT_RESTORED: "Checkpoint Restored", - CHECKPOINT_DIFFED: "Checkpoint Diffed", - CODE_ACTION_USED: "Code Action Used", - PROMPT_ENHANCED: "Prompt Enhanced", - CONTEXT_CONDENSED: "Context Condensed", - SLIDING_WINDOW_TRUNCATION: "Sliding Window Truncation", - }, - ERRORS: { - SCHEMA_VALIDATION_ERROR: "Schema Validation Error", - DIFF_APPLICATION_ERROR: "Diff Application Error", - SHELL_INTEGRATION_ERROR: "Shell Integration Error", - CONSECUTIVE_MISTAKE_ERROR: "Consecutive Mistake Error", - }, - } - - private static instance: PostHogClient - private client: PostHog - private distinctId: string = vscode.env.machineId - private telemetryEnabled: boolean = false - private providerRef: WeakRef | null = null - - private constructor() { - this.client = new PostHog(process.env.POSTHOG_API_KEY || "", { host: "https://us.i.posthog.com" }) - } - - /** - * Updates the telemetry state based on user preferences and VSCode settings - * Only enables telemetry if both VSCode global telemetry is enabled and user has opted in - * @param didUserOptIn Whether the user has explicitly opted into telemetry - */ - public updateTelemetryState(didUserOptIn: boolean): void { - this.telemetryEnabled = false - - // First check global telemetry level - telemetry should only be enabled when level is "all" - const telemetryLevel = vscode.workspace.getConfiguration("telemetry").get("telemetryLevel", "all") - const globalTelemetryEnabled = telemetryLevel === "all" - - // We only enable telemetry if global vscode telemetry is enabled - if (globalTelemetryEnabled) { - this.telemetryEnabled = didUserOptIn - } - - // Update PostHog client state based on telemetry preference - if (this.telemetryEnabled) { - this.client.optIn() - } else { - this.client.optOut() - } - } - - /** - * Gets or creates the singleton instance of PostHogClient - * @returns The PostHogClient instance - */ - public static getInstance(): PostHogClient { - if (!PostHogClient.instance) { - PostHogClient.instance = new PostHogClient() - } - - return PostHogClient.instance - } - - /** - * Sets the ClineProvider reference to use for global properties - * @param provider A ClineProvider instance to use - */ - public setProvider(provider: ClineProviderInterface): void { - this.providerRef = new WeakRef(provider) - logger.debug("PostHogClient: ClineProvider reference set") - } - - /** - * Captures a telemetry event if telemetry is enabled - * @param event The event to capture with its properties - */ - public async capture(event: { event: string; properties?: any }): Promise { - // Only send events if telemetry is enabled - if (this.telemetryEnabled) { - // Get global properties from ClineProvider if available - let globalProperties: Record = {} - const provider = this.providerRef?.deref() - - if (provider) { - try { - // Get the telemetry properties directly from the provider - globalProperties = await provider.getTelemetryProperties() - } catch (error) { - // Log error but continue with capturing the event - logger.error( - `Error getting telemetry properties: ${error instanceof Error ? error.message : String(error)}`, - ) - } - } - - // Merge global properties with event-specific properties - // Event properties take precedence in case of conflicts - const mergedProperties = { - ...globalProperties, - ...(event.properties || {}), - } - - this.client.capture({ - distinctId: this.distinctId, - event: event.event, - properties: mergedProperties, - }) - } - } - - /** - * Checks if telemetry is currently enabled - * @returns Whether telemetry is enabled - */ - public isTelemetryEnabled(): boolean { - return this.telemetryEnabled - } - - /** - * Shuts down the PostHog client - */ - public async shutdown(): Promise { - await this.client.shutdown() - } -} diff --git a/src/services/telemetry/TelemetryService.ts b/src/services/telemetry/TelemetryService.ts index c0ddbe4edc..cc1248f1b7 100644 --- a/src/services/telemetry/TelemetryService.ts +++ b/src/services/telemetry/TelemetryService.ts @@ -1,28 +1,35 @@ +import * as vscode from "vscode" import { ZodError } from "zod" +import { TelemetryEventName } from "@roo-code/types" + import { logger } from "../../utils/logging" -import { PostHogClient, ClineProviderInterface } from "./PostHogClient" + +import { PostHogTelemetryClient } from "./clients/PostHogTelemetryClient" +import { type TelemetryClient, type TelemetryPropertiesProvider } from "./types" /** - * TelemetryService wrapper class that defers PostHogClient initialization - * This ensures that we only create the PostHogClient after environment variables are loaded + * TelemetryService wrapper class that defers initialization. + * This ensures that we only create the various clients after environment + * variables are loaded. */ class TelemetryService { - private client: PostHogClient | null = null + private clients: TelemetryClient[] = [] private initialized = false /** - * Initialize the telemetry service with the PostHog client - * This should be called after environment variables are loaded + * Initialize the telemetry client. This should be called after environment + * variables are loaded. */ - public initialize(): void { + public async initialize(context: vscode.ExtensionContext): Promise { if (this.initialized) { return } + this.initialized = true + try { - this.client = PostHogClient.getInstance() - this.initialized = true + this.clients.push(PostHogTelemetryClient.getInstance()) } catch (error) { console.warn("Failed to initialize telemetry service:", error) } @@ -32,10 +39,10 @@ class TelemetryService { * Sets the ClineProvider reference to use for global properties * @param provider A ClineProvider instance to use */ - public setProvider(provider: ClineProviderInterface): void { - // If client is initialized, pass the provider reference + public setProvider(provider: TelemetryPropertiesProvider): void { + // If client is initialized, pass the provider reference. if (this.isReady) { - this.client!.setProvider(provider) + this.clients.forEach((client) => client.setProvider(provider)) } logger.debug("TelemetryService: ClineProvider reference set") @@ -47,7 +54,7 @@ class TelemetryService { * @returns Whether the service is ready to use */ private get isReady(): boolean { - return this.initialized && this.client !== null + return this.initialized && this.clients.length > 0 } /** @@ -59,19 +66,7 @@ class TelemetryService { return } - this.client!.updateTelemetryState(didUserOptIn) - } - - /** - * Captures a telemetry event if telemetry is enabled - * @param event The event to capture with its properties - */ - public capture(event: { event: string; properties?: any }): void { - if (!this.isReady) { - return - } - - this.client!.capture(event) + this.clients.forEach((client) => client.updateTelemetryState(didUserOptIn)) } /** @@ -79,45 +74,61 @@ class TelemetryService { * @param eventName The event name to capture * @param properties The event properties */ - public captureEvent(eventName: string, properties?: any): void { - this.capture({ event: eventName, properties }) + public captureEvent(eventName: TelemetryEventName, properties?: any): void { + if (!this.isReady) { + return + } + + this.clients.forEach((client) => client.capture({ event: eventName, properties })) } - // Task events convenience methods public captureTaskCreated(taskId: string): void { - this.captureEvent(PostHogClient.EVENTS.TASK.CREATED, { taskId }) + this.captureEvent(TelemetryEventName.TASK_CREATED, { taskId }) } public captureTaskRestarted(taskId: string): void { - this.captureEvent(PostHogClient.EVENTS.TASK.RESTARTED, { taskId }) + this.captureEvent(TelemetryEventName.TASK_RESTARTED, { taskId }) } public captureTaskCompleted(taskId: string): void { - this.captureEvent(PostHogClient.EVENTS.TASK.COMPLETED, { taskId }) + this.captureEvent(TelemetryEventName.TASK_COMPLETED, { taskId }) } public captureConversationMessage(taskId: string, source: "user" | "assistant"): void { - this.captureEvent(PostHogClient.EVENTS.TASK.CONVERSATION_MESSAGE, { taskId, source }) + this.captureEvent(TelemetryEventName.TASK_CONVERSATION_MESSAGE, { taskId, source }) + } + + public captureLlmCompletion( + taskId: string, + properties: { + inputTokens: number + outputTokens: number + cacheWriteTokens: number + cacheReadTokens: number + cost?: number + }, + ): void { + this.captureEvent(TelemetryEventName.LLM_COMPLETION, { taskId, ...properties }) } public captureModeSwitch(taskId: string, newMode: string): void { - this.captureEvent(PostHogClient.EVENTS.TASK.MODE_SWITCH, { taskId, newMode }) + this.captureEvent(TelemetryEventName.MODE_SWITCH, { taskId, newMode }) } public captureToolUsage(taskId: string, tool: string): void { - this.captureEvent(PostHogClient.EVENTS.TASK.TOOL_USED, { taskId, tool }) + this.captureEvent(TelemetryEventName.TOOL_USED, { taskId, tool }) } public captureCheckpointCreated(taskId: string): void { - this.captureEvent(PostHogClient.EVENTS.TASK.CHECKPOINT_CREATED, { taskId }) + this.captureEvent(TelemetryEventName.CHECKPOINT_CREATED, { taskId }) } public captureCheckpointDiffed(taskId: string): void { - this.captureEvent(PostHogClient.EVENTS.TASK.CHECKPOINT_DIFFED, { taskId }) + this.captureEvent(TelemetryEventName.CHECKPOINT_DIFFED, { taskId }) } public captureCheckpointRestored(taskId: string): void { - this.captureEvent(PostHogClient.EVENTS.TASK.CHECKPOINT_RESTORED, { taskId }) + this.captureEvent(TelemetryEventName.CHECKPOINT_RESTORED, { taskId }) } public captureContextCondensed( @@ -126,7 +137,7 @@ class TelemetryService { usedCustomPrompt?: boolean, usedCustomApiHandler?: boolean, ): void { - this.captureEvent(PostHogClient.EVENTS.TASK.CONTEXT_CONDENSED, { + this.captureEvent(TelemetryEventName.CONTEXT_CONDENSED, { taskId, isAutomaticTrigger, ...(usedCustomPrompt !== undefined && { usedCustomPrompt }), @@ -135,32 +146,32 @@ class TelemetryService { } public captureSlidingWindowTruncation(taskId: string): void { - this.captureEvent(PostHogClient.EVENTS.TASK.SLIDING_WINDOW_TRUNCATION, { taskId }) + this.captureEvent(TelemetryEventName.SLIDING_WINDOW_TRUNCATION, { taskId }) } public captureCodeActionUsed(actionType: string): void { - this.captureEvent(PostHogClient.EVENTS.TASK.CODE_ACTION_USED, { actionType }) + this.captureEvent(TelemetryEventName.CODE_ACTION_USED, { actionType }) } public capturePromptEnhanced(taskId?: string): void { - this.captureEvent(PostHogClient.EVENTS.TASK.PROMPT_ENHANCED, { ...(taskId && { taskId }) }) + this.captureEvent(TelemetryEventName.PROMPT_ENHANCED, { ...(taskId && { taskId }) }) } public captureSchemaValidationError({ schemaName, error }: { schemaName: string; error: ZodError }): void { // https://zod.dev/ERROR_HANDLING?id=formatting-errors - this.captureEvent(PostHogClient.EVENTS.ERRORS.SCHEMA_VALIDATION_ERROR, { schemaName, error: error.format() }) + this.captureEvent(TelemetryEventName.SCHEMA_VALIDATION_ERROR, { schemaName, error: error.format() }) } public captureDiffApplicationError(taskId: string, consecutiveMistakeCount: number): void { - this.captureEvent(PostHogClient.EVENTS.ERRORS.DIFF_APPLICATION_ERROR, { taskId, consecutiveMistakeCount }) + this.captureEvent(TelemetryEventName.DIFF_APPLICATION_ERROR, { taskId, consecutiveMistakeCount }) } public captureShellIntegrationError(taskId: string): void { - this.captureEvent(PostHogClient.EVENTS.ERRORS.SHELL_INTEGRATION_ERROR, { taskId }) + this.captureEvent(TelemetryEventName.SHELL_INTEGRATION_ERROR, { taskId }) } public captureConsecutiveMistakeError(taskId: string): void { - this.captureEvent(PostHogClient.EVENTS.ERRORS.CONSECUTIVE_MISTAKE_ERROR, { taskId }) + this.captureEvent(TelemetryEventName.CONSECUTIVE_MISTAKE_ERROR, { taskId }) } /** @@ -168,7 +179,7 @@ class TelemetryService { * @param button The button that was clicked */ public captureTitleButtonClicked(button: string): void { - this.captureEvent("Title Button Clicked", { button }) + this.captureEvent(TelemetryEventName.TITLE_BUTTON_CLICKED, { button }) } /** @@ -176,20 +187,16 @@ class TelemetryService { * @returns Whether telemetry is enabled */ public isTelemetryEnabled(): boolean { - return this.isReady && this.client!.isTelemetryEnabled() + return this.isReady && this.clients.some((client) => client.isTelemetryEnabled()) } - /** - * Shuts down the PostHog client - */ public async shutdown(): Promise { if (!this.isReady) { return } - await this.client!.shutdown() + this.clients.forEach((client) => client.shutdown()) } } -// Export a singleton instance of the telemetry service wrapper export const telemetryService = new TelemetryService() diff --git a/src/services/telemetry/clients/BaseTelemetryClient.ts b/src/services/telemetry/clients/BaseTelemetryClient.ts new file mode 100644 index 0000000000..24a486a2ea --- /dev/null +++ b/src/services/telemetry/clients/BaseTelemetryClient.ts @@ -0,0 +1,58 @@ +import { TelemetryEvent, TelemetryEventName } from "@roo-code/types" + +import { TelemetryClient, TelemetryPropertiesProvider, TelemetryEventSubscription } from "../types" + +export abstract class BaseTelemetryClient implements TelemetryClient { + protected providerRef: WeakRef | null = null + protected telemetryEnabled: boolean = false + + constructor( + public readonly subscription?: TelemetryEventSubscription, + protected readonly debug = false, + ) {} + + protected isEventCapturable(eventName: TelemetryEventName): boolean { + if (!this.subscription) { + return true + } + + return this.subscription.type === "include" + ? this.subscription.events.includes(eventName) + : !this.subscription.events.includes(eventName) + } + + protected async getEventProperties(event: TelemetryEvent): Promise { + let providerProperties: TelemetryEvent["properties"] = {} + const provider = this.providerRef?.deref() + + if (provider) { + try { + // Get the telemetry properties directly from the provider. + providerProperties = await provider.getTelemetryProperties() + } catch (error) { + // Log error but continue with capturing the event. + console.error( + `Error getting telemetry properties: ${error instanceof Error ? error.message : String(error)}`, + ) + } + } + + // Merge provider properties with event-specific properties. + // Event properties take precedence in case of conflicts. + return { ...providerProperties, ...(event.properties || {}) } + } + + public abstract capture(event: TelemetryEvent): Promise + + public setProvider(provider: TelemetryPropertiesProvider): void { + this.providerRef = new WeakRef(provider) + } + + public abstract updateTelemetryState(didUserOptIn: boolean): void + + public isTelemetryEnabled(): boolean { + return this.telemetryEnabled + } + + public abstract shutdown(): Promise +} diff --git a/src/services/telemetry/clients/PostHogTelemetryClient.ts b/src/services/telemetry/clients/PostHogTelemetryClient.ts new file mode 100644 index 0000000000..b554d962e3 --- /dev/null +++ b/src/services/telemetry/clients/PostHogTelemetryClient.ts @@ -0,0 +1,88 @@ +import { PostHog } from "posthog-node" +import * as vscode from "vscode" + +import { TelemetryEventName, type TelemetryEvent } from "@roo-code/types" + +import { BaseTelemetryClient } from "./BaseTelemetryClient" + +/** + * PostHogTelemetryClient handles telemetry event tracking for the Roo Code extension. + * Uses PostHog analytics to track user interactions and system events. + * Respects user privacy settings and VSCode's global telemetry configuration. + */ +export class PostHogTelemetryClient extends BaseTelemetryClient { + private client: PostHog + private distinctId: string = vscode.env.machineId + + private constructor(debug = false) { + super( + { + type: "exclude", + events: [TelemetryEventName.LLM_COMPLETION], + }, + debug, + ) + + this.client = new PostHog(process.env.POSTHOG_API_KEY || "", { host: "https://us.i.posthog.com" }) + } + + public override async capture(event: TelemetryEvent): Promise { + if (!this.isTelemetryEnabled() || !this.isEventCapturable(event.event)) { + if (this.debug) { + console.info(`[PostHogTelemetryClient#capture] Skipping event: ${event.event}`) + } + + return + } + + if (this.debug) { + console.info(`[PostHogTelemetryClient#capture] ${event.event}`) + } + + this.client.capture({ + distinctId: this.distinctId, + event: event.event, + properties: await this.getEventProperties(event), + }) + } + + /** + * Updates the telemetry state based on user preferences and VSCode settings. + * Only enables telemetry if both VSCode global telemetry is enabled and + * user has opted in. + * @param didUserOptIn Whether the user has explicitly opted into telemetry + */ + public override updateTelemetryState(didUserOptIn: boolean): void { + this.telemetryEnabled = false + + // First check global telemetry level - telemetry should only be enabled when level is "all". + const telemetryLevel = vscode.workspace.getConfiguration("telemetry").get("telemetryLevel", "all") + const globalTelemetryEnabled = telemetryLevel === "all" + + // We only enable telemetry if global vscode telemetry is enabled. + if (globalTelemetryEnabled) { + this.telemetryEnabled = didUserOptIn + } + + // Update PostHog client state based on telemetry preference. + if (this.telemetryEnabled) { + this.client.optIn() + } else { + this.client.optOut() + } + } + + public override async shutdown(): Promise { + await this.client.shutdown() + } + + private static _instance: PostHogTelemetryClient | null = null + + public static getInstance(): PostHogTelemetryClient { + if (!PostHogTelemetryClient._instance) { + PostHogTelemetryClient._instance = new PostHogTelemetryClient() + } + + return PostHogTelemetryClient._instance + } +} diff --git a/src/services/telemetry/clients/__tests__/PostHogTelemetryClient.test.ts b/src/services/telemetry/clients/__tests__/PostHogTelemetryClient.test.ts new file mode 100644 index 0000000000..89bb16e81a --- /dev/null +++ b/src/services/telemetry/clients/__tests__/PostHogTelemetryClient.test.ts @@ -0,0 +1,270 @@ +// npx jest src/services/telemetry/clients/__tests__/PostHogTelemetryClient.test.ts + +import * as vscode from "vscode" +import { PostHog } from "posthog-node" + +import { TelemetryEventName } from "@roo-code/types" + +import { TelemetryPropertiesProvider } from "../../types" +import { PostHogTelemetryClient } from "../PostHogTelemetryClient" + +jest.mock("posthog-node") + +jest.mock("vscode", () => ({ + env: { + machineId: "test-machine-id", + }, + workspace: { + getConfiguration: jest.fn(), + }, +})) + +describe("PostHogTelemetryClient", () => { + const getPrivateProperty = (instance: any, propertyName: string): T => { + return instance[propertyName] + } + + let mockPostHogClient: jest.Mocked + + beforeEach(() => { + jest.clearAllMocks() + + mockPostHogClient = { + capture: jest.fn(), + optIn: jest.fn(), + optOut: jest.fn(), + shutdown: jest.fn().mockResolvedValue(undefined), + } as unknown as jest.Mocked + ;(PostHog as unknown as jest.Mock).mockImplementation(() => mockPostHogClient) + + // @ts-ignore - Accessing private static property for testing + PostHogTelemetryClient._instance = undefined + ;(vscode.workspace.getConfiguration as jest.Mock).mockReturnValue({ + get: jest.fn().mockReturnValue("all"), + }) + }) + + describe("getInstance", () => { + it("should return the same instance when called multiple times", () => { + const instance1 = PostHogTelemetryClient.getInstance() + const instance2 = PostHogTelemetryClient.getInstance() + expect(instance1).toBe(instance2) + }) + }) + + describe("isEventCapturable", () => { + it("should return true for events not in exclude list", () => { + const client = PostHogTelemetryClient.getInstance() + + const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>( + client, + "isEventCapturable", + ).bind(client) + + expect(isEventCapturable(TelemetryEventName.TASK_CREATED)).toBe(true) + expect(isEventCapturable(TelemetryEventName.MODE_SWITCH)).toBe(true) + }) + + it("should return false for events in exclude list", () => { + const client = PostHogTelemetryClient.getInstance() + + const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>( + client, + "isEventCapturable", + ).bind(client) + + expect(isEventCapturable(TelemetryEventName.LLM_COMPLETION)).toBe(false) + }) + }) + + describe("getEventProperties", () => { + it("should merge provider properties with event properties", async () => { + const client = PostHogTelemetryClient.getInstance() + + const mockProvider: TelemetryPropertiesProvider = { + getTelemetryProperties: jest.fn().mockResolvedValue({ + appVersion: "1.0.0", + vscodeVersion: "1.60.0", + platform: "darwin", + editorName: "vscode", + language: "en", + mode: "code", + }), + } + + client.setProvider(mockProvider) + + const getEventProperties = getPrivateProperty< + (event: { event: TelemetryEventName; properties?: Record }) => Promise> + >(client, "getEventProperties").bind(client) + + const result = await getEventProperties({ + event: TelemetryEventName.TASK_CREATED, + properties: { + customProp: "value", + mode: "override", // This should override the provider's mode. + }, + }) + + expect(result).toEqual({ + appVersion: "1.0.0", + vscodeVersion: "1.60.0", + platform: "darwin", + editorName: "vscode", + language: "en", + mode: "override", // Event property takes precedence. + customProp: "value", + }) + + expect(mockProvider.getTelemetryProperties).toHaveBeenCalledTimes(1) + }) + + it("should handle errors from provider gracefully", async () => { + const client = PostHogTelemetryClient.getInstance() + + const mockProvider: TelemetryPropertiesProvider = { + getTelemetryProperties: jest.fn().mockRejectedValue(new Error("Provider error")), + } + + const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation() + client.setProvider(mockProvider) + + const getEventProperties = getPrivateProperty< + (event: { event: TelemetryEventName; properties?: Record }) => Promise> + >(client, "getEventProperties").bind(client) + + const result = await getEventProperties({ + event: TelemetryEventName.TASK_CREATED, + properties: { customProp: "value" }, + }) + + expect(result).toEqual({ customProp: "value" }) + expect(consoleErrorSpy).toHaveBeenCalledWith( + expect.stringContaining("Error getting telemetry properties: Provider error"), + ) + + consoleErrorSpy.mockRestore() + }) + + it("should return event properties when no provider is set", async () => { + const client = PostHogTelemetryClient.getInstance() + + const getEventProperties = getPrivateProperty< + (event: { event: TelemetryEventName; properties?: Record }) => Promise> + >(client, "getEventProperties").bind(client) + + const result = await getEventProperties({ + event: TelemetryEventName.TASK_CREATED, + properties: { customProp: "value" }, + }) + + expect(result).toEqual({ customProp: "value" }) + }) + }) + + describe("capture", () => { + it("should not capture events when telemetry is disabled", async () => { + const client = PostHogTelemetryClient.getInstance() + client.updateTelemetryState(false) + + await client.capture({ + event: TelemetryEventName.TASK_CREATED, + properties: { test: "value" }, + }) + + expect(mockPostHogClient.capture).not.toHaveBeenCalled() + }) + + it("should not capture events that are not capturable", async () => { + const client = PostHogTelemetryClient.getInstance() + client.updateTelemetryState(true) + + await client.capture({ + event: TelemetryEventName.LLM_COMPLETION, // This is in the exclude list. + properties: { test: "value" }, + }) + + expect(mockPostHogClient.capture).not.toHaveBeenCalled() + }) + + it("should capture events when telemetry is enabled and event is capturable", async () => { + const client = PostHogTelemetryClient.getInstance() + client.updateTelemetryState(true) + + const mockProvider: TelemetryPropertiesProvider = { + getTelemetryProperties: jest.fn().mockResolvedValue({ + appVersion: "1.0.0", + vscodeVersion: "1.60.0", + platform: "darwin", + editorName: "vscode", + language: "en", + mode: "code", + }), + } + + client.setProvider(mockProvider) + + await client.capture({ + event: TelemetryEventName.TASK_CREATED, + properties: { test: "value" }, + }) + + expect(mockPostHogClient.capture).toHaveBeenCalledWith({ + distinctId: "test-machine-id", + event: TelemetryEventName.TASK_CREATED, + properties: expect.objectContaining({ + appVersion: "1.0.0", + test: "value", + }), + }) + }) + }) + + describe("updateTelemetryState", () => { + it("should enable telemetry when user opts in and global telemetry is enabled", () => { + const client = PostHogTelemetryClient.getInstance() + + ;(vscode.workspace.getConfiguration as jest.Mock).mockReturnValue({ + get: jest.fn().mockReturnValue("all"), + }) + + client.updateTelemetryState(true) + + expect(client.isTelemetryEnabled()).toBe(true) + expect(mockPostHogClient.optIn).toHaveBeenCalled() + }) + + it("should disable telemetry when user opts out", () => { + const client = PostHogTelemetryClient.getInstance() + + ;(vscode.workspace.getConfiguration as jest.Mock).mockReturnValue({ + get: jest.fn().mockReturnValue("all"), + }) + + client.updateTelemetryState(false) + + expect(client.isTelemetryEnabled()).toBe(false) + expect(mockPostHogClient.optOut).toHaveBeenCalled() + }) + + it("should disable telemetry when global telemetry is disabled, regardless of user opt-in", () => { + const client = PostHogTelemetryClient.getInstance() + + ;(vscode.workspace.getConfiguration as jest.Mock).mockReturnValue({ + get: jest.fn().mockReturnValue("off"), + }) + + client.updateTelemetryState(true) + expect(client.isTelemetryEnabled()).toBe(false) + expect(mockPostHogClient.optOut).toHaveBeenCalled() + }) + }) + + describe("shutdown", () => { + it("should call shutdown on the PostHog client", async () => { + const client = PostHogTelemetryClient.getInstance() + await client.shutdown() + expect(mockPostHogClient.shutdown).toHaveBeenCalled() + }) + }) +}) diff --git a/src/services/telemetry/index.ts b/src/services/telemetry/index.ts new file mode 100644 index 0000000000..6700a8085e --- /dev/null +++ b/src/services/telemetry/index.ts @@ -0,0 +1,2 @@ +export * from "./TelemetryService" +export * from "./types" diff --git a/src/services/telemetry/types.ts b/src/services/telemetry/types.ts new file mode 100644 index 0000000000..b6fb038484 --- /dev/null +++ b/src/services/telemetry/types.ts @@ -0,0 +1,19 @@ +import { TelemetryEventName, type TelemetryProperties, type TelemetryEvent } from "@roo-code/types" + +export type TelemetryEventSubscription = + | { type: "include"; events: TelemetryEventName[] } + | { type: "exclude"; events: TelemetryEventName[] } + +export interface TelemetryPropertiesProvider { + getTelemetryProperties(): Promise +} + +export interface TelemetryClient { + subscription?: TelemetryEventSubscription + + setProvider(provider: TelemetryPropertiesProvider): void + capture(options: TelemetryEvent): Promise + updateTelemetryState(didUserOptIn: boolean): void + isTelemetryEnabled(): boolean + shutdown(): Promise +} diff --git a/src/utils/__tests__/refresh-timer.test.ts b/src/utils/__tests__/refresh-timer.test.ts new file mode 100644 index 0000000000..11911494f6 --- /dev/null +++ b/src/utils/__tests__/refresh-timer.test.ts @@ -0,0 +1,210 @@ +import { RefreshTimer } from "../refresh-timer" + +// Mock timers +jest.useFakeTimers() + +describe("RefreshTimer", () => { + let mockCallback: jest.Mock + let refreshTimer: RefreshTimer + + beforeEach(() => { + // Reset mocks before each test + mockCallback = jest.fn() + + // Default mock implementation returns success + mockCallback.mockResolvedValue(true) + }) + + afterEach(() => { + // Clean up after each test + if (refreshTimer) { + refreshTimer.stop() + } + jest.clearAllTimers() + jest.clearAllMocks() + }) + + it("should execute callback immediately when started", () => { + refreshTimer = new RefreshTimer({ + callback: mockCallback, + }) + + refreshTimer.start() + + expect(mockCallback).toHaveBeenCalledTimes(1) + }) + + it("should schedule next attempt after success interval when callback succeeds", async () => { + mockCallback.mockResolvedValue(true) + + refreshTimer = new RefreshTimer({ + callback: mockCallback, + successInterval: 50000, // 50 seconds + }) + + refreshTimer.start() + + // Fast-forward to execute the first callback + await Promise.resolve() + + expect(mockCallback).toHaveBeenCalledTimes(1) + + // Fast-forward 50 seconds + jest.advanceTimersByTime(50000) + + // Callback should be called again + expect(mockCallback).toHaveBeenCalledTimes(2) + }) + + it("should use exponential backoff when callback fails", async () => { + mockCallback.mockResolvedValue(false) + + refreshTimer = new RefreshTimer({ + callback: mockCallback, + initialBackoffMs: 1000, // 1 second + }) + + refreshTimer.start() + + // Fast-forward to execute the first callback + await Promise.resolve() + + expect(mockCallback).toHaveBeenCalledTimes(1) + + // Fast-forward 1 second + jest.advanceTimersByTime(1000) + + // Callback should be called again + expect(mockCallback).toHaveBeenCalledTimes(2) + + // Fast-forward to execute the second callback + await Promise.resolve() + + // Fast-forward 2 seconds + jest.advanceTimersByTime(2000) + + // Callback should be called again + expect(mockCallback).toHaveBeenCalledTimes(3) + + // Fast-forward to execute the third callback + await Promise.resolve() + }) + + it("should not exceed maximum backoff interval", async () => { + mockCallback.mockResolvedValue(false) + + refreshTimer = new RefreshTimer({ + callback: mockCallback, + initialBackoffMs: 1000, // 1 second + maxBackoffMs: 5000, // 5 seconds + }) + + refreshTimer.start() + + // Fast-forward through multiple failures to reach max backoff + await Promise.resolve() // First attempt + jest.advanceTimersByTime(1000) + + await Promise.resolve() // Second attempt (backoff = 2000ms) + jest.advanceTimersByTime(2000) + + await Promise.resolve() // Third attempt (backoff = 4000ms) + jest.advanceTimersByTime(4000) + + await Promise.resolve() // Fourth attempt (backoff would be 8000ms but max is 5000ms) + + // Should be capped at maxBackoffMs (no way to verify without logger) + }) + + it("should reset backoff after a successful attempt", async () => { + // First call fails, second succeeds, third fails + mockCallback.mockResolvedValueOnce(false).mockResolvedValueOnce(true).mockResolvedValueOnce(false) + + refreshTimer = new RefreshTimer({ + callback: mockCallback, + initialBackoffMs: 1000, + successInterval: 5000, + }) + + refreshTimer.start() + + // First attempt (fails) + await Promise.resolve() + + // Fast-forward 1 second + jest.advanceTimersByTime(1000) + + // Second attempt (succeeds) + await Promise.resolve() + + // Fast-forward 5 seconds + jest.advanceTimersByTime(5000) + + // Third attempt (fails) + await Promise.resolve() + + // Backoff should be reset to initial value (no way to verify without logger) + }) + + it("should handle errors in callback as failures", async () => { + mockCallback.mockRejectedValue(new Error("Test error")) + + refreshTimer = new RefreshTimer({ + callback: mockCallback, + initialBackoffMs: 1000, + }) + + refreshTimer.start() + + // Fast-forward to execute the callback + await Promise.resolve() + + // Error should be treated as a failure (no way to verify without logger) + }) + + it("should stop the timer and cancel pending executions", () => { + refreshTimer = new RefreshTimer({ + callback: mockCallback, + }) + + refreshTimer.start() + + // Stop the timer + refreshTimer.stop() + + // Fast-forward a long time + jest.advanceTimersByTime(1000000) + + // Callback should only have been called once (the initial call) + expect(mockCallback).toHaveBeenCalledTimes(1) + }) + + it("should reset the backoff state", async () => { + mockCallback.mockResolvedValue(false) + + refreshTimer = new RefreshTimer({ + callback: mockCallback, + initialBackoffMs: 1000, + }) + + refreshTimer.start() + + // Fast-forward through a few failures + await Promise.resolve() + jest.advanceTimersByTime(1000) + + await Promise.resolve() + jest.advanceTimersByTime(2000) + + // Reset the timer + refreshTimer.reset() + + // Stop and restart to trigger a new execution + refreshTimer.stop() + refreshTimer.start() + + await Promise.resolve() + + // Backoff should be back to initial value (no way to verify without logger) + }) +}) diff --git a/src/utils/refresh-timer.ts b/src/utils/refresh-timer.ts new file mode 100644 index 0000000000..3138031665 --- /dev/null +++ b/src/utils/refresh-timer.ts @@ -0,0 +1,154 @@ +/** + * RefreshTimer - A utility for executing a callback with configurable retry behavior + * + * This timer executes a callback function and schedules the next execution based on the result: + * - If the callback succeeds (returns true), it schedules the next attempt after a fixed interval + * - If the callback fails (returns false), it uses exponential backoff up to a maximum interval + */ + +/** + * Configuration options for the RefreshTimer + */ +export interface RefreshTimerOptions { + /** + * The callback function to execute + * Should return a Promise that resolves to a boolean indicating success (true) or failure (false) + */ + callback: () => Promise + + /** + * Time in milliseconds to wait before next attempt after success + * @default 50000 (50 seconds) + */ + successInterval?: number + + /** + * Initial backoff time in milliseconds for the first failure + * @default 1000 (1 second) + */ + initialBackoffMs?: number + + /** + * Maximum backoff time in milliseconds + * @default 300000 (5 minutes) + */ + maxBackoffMs?: number +} + +/** + * A timer utility that executes a callback with configurable retry behavior + */ +export class RefreshTimer { + private callback: () => Promise + private successInterval: number + private initialBackoffMs: number + private maxBackoffMs: number + private currentBackoffMs: number + private attemptCount: number + private timerId: NodeJS.Timeout | null + private isRunning: boolean + + /** + * Creates a new RefreshTimer + * + * @param options Configuration options for the timer + */ + constructor(options: RefreshTimerOptions) { + this.callback = options.callback + this.successInterval = options.successInterval ?? 50000 // 50 seconds + this.initialBackoffMs = options.initialBackoffMs ?? 1000 // 1 second + this.maxBackoffMs = options.maxBackoffMs ?? 300000 // 5 minutes + this.currentBackoffMs = this.initialBackoffMs + this.attemptCount = 0 + this.timerId = null + this.isRunning = false + } + + /** + * Starts the timer and executes the callback immediately + */ + public start(): void { + if (this.isRunning) { + return + } + + this.isRunning = true + + // Execute the callback immediately + this.executeCallback() + } + + /** + * Stops the timer and cancels any pending execution + */ + public stop(): void { + if (!this.isRunning) { + return + } + + if (this.timerId) { + clearTimeout(this.timerId) + this.timerId = null + } + + this.isRunning = false + } + + /** + * Resets the backoff state and attempt count + * Does not affect whether the timer is running + */ + public reset(): void { + this.currentBackoffMs = this.initialBackoffMs + this.attemptCount = 0 + } + + /** + * Schedules the next attempt based on the success/failure of the current attempt + * + * @param wasSuccessful Whether the current attempt was successful + */ + private scheduleNextAttempt(wasSuccessful: boolean): void { + if (!this.isRunning) { + return + } + + if (wasSuccessful) { + // Reset backoff on success + this.currentBackoffMs = this.initialBackoffMs + this.attemptCount = 0 + + this.timerId = setTimeout(() => this.executeCallback(), this.successInterval) + } else { + // Increment attempt count + this.attemptCount++ + + // Calculate backoff time with exponential increase + // Formula: initialBackoff * 2^(attemptCount - 1) + this.currentBackoffMs = Math.min( + this.initialBackoffMs * Math.pow(2, this.attemptCount - 1), + this.maxBackoffMs, + ) + + this.timerId = setTimeout(() => this.executeCallback(), this.currentBackoffMs) + } + } + + /** + * Executes the callback and handles the result + */ + private async executeCallback(): Promise { + if (!this.isRunning) { + return + } + + try { + const result = await this.callback() + + this.scheduleNextAttempt(result) + } catch (error) { + // Treat errors as failed attempts + this.scheduleNextAttempt(false) + } + } +} From b7d1c9ae4a5b88e9057c06551ce9c482af11ac74 Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Mon, 26 May 2025 21:27:10 -0700 Subject: [PATCH 015/104] More elegant way to generate arrays of keys from a type with full type safety (#4025) --- packages/types/src/__tests__/index.test.ts | 2 +- packages/types/src/global-settings.ts | 195 ++++++++++----------- packages/types/src/provider-settings.ts | 156 ++++++++--------- packages/types/src/type-fu.ts | 10 ++ 4 files changed, 180 insertions(+), 183 deletions(-) diff --git a/packages/types/src/__tests__/index.test.ts b/packages/types/src/__tests__/index.test.ts index c3df37fa97..fd1e9e1c88 100644 --- a/packages/types/src/__tests__/index.test.ts +++ b/packages/types/src/__tests__/index.test.ts @@ -1,4 +1,4 @@ -// npx vitest run src/__tests__/index.test.ts +// npx vitest run --globals src/__tests__/index.test.ts import { GLOBAL_STATE_KEYS } from "../index.js" diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index d69a77fa53..10b7d6ab18 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -1,6 +1,6 @@ import { z } from "zod" -import type { Keys } from "./type-fu.js" +import { type Keys, keysOf } from "./type-fu.js" import { type ProviderSettings, PROVIDER_SETTINGS_KEYS, @@ -34,8 +34,6 @@ export const globalSettingsSchema = z.object({ autoApprovalEnabled: z.boolean().optional(), alwaysAllowReadOnly: z.boolean().optional(), alwaysAllowReadOnlyOutsideWorkspace: z.boolean().optional(), - codebaseIndexModels: codebaseIndexModelsSchema.optional(), - codebaseIndexConfig: codebaseIndexConfigSchema.optional(), alwaysAllowWrite: z.boolean().optional(), alwaysAllowWriteOutsideWorkspace: z.boolean().optional(), writeDelayMs: z.number().optional(), @@ -85,6 +83,9 @@ export const globalSettingsSchema = z.object({ fuzzyMatchThreshold: z.number().optional(), experiments: experimentsSchema.optional(), + codebaseIndexModels: codebaseIndexModelsSchema.optional(), + codebaseIndexConfig: codebaseIndexConfigSchema.optional(), + language: languagesSchema.optional(), telemetrySetting: telemetrySettingsSchema.optional(), @@ -103,91 +104,87 @@ export const globalSettingsSchema = z.object({ export type GlobalSettings = z.infer -type GlobalSettingsRecord = Record, undefined> +export const GLOBAL_SETTINGS_KEYS = keysOf()([ + "currentApiConfigName", + "listApiConfigMeta", + "pinnedApiConfigs", -const globalSettingsRecord: GlobalSettingsRecord = { - codebaseIndexModels: undefined, - codebaseIndexConfig: undefined, - currentApiConfigName: undefined, - listApiConfigMeta: undefined, - pinnedApiConfigs: undefined, + "lastShownAnnouncementId", + "customInstructions", + "taskHistory", - lastShownAnnouncementId: undefined, - customInstructions: undefined, - taskHistory: undefined, + "condensingApiConfigId", + "customCondensingPrompt", - condensingApiConfigId: undefined, - customCondensingPrompt: undefined, + "autoApprovalEnabled", + "alwaysAllowReadOnly", + "alwaysAllowReadOnlyOutsideWorkspace", + "alwaysAllowWrite", + "alwaysAllowWriteOutsideWorkspace", + "writeDelayMs", + "alwaysAllowBrowser", + "alwaysApproveResubmit", + "requestDelaySeconds", + "alwaysAllowMcp", + "alwaysAllowModeSwitch", + "alwaysAllowSubtasks", + "alwaysAllowExecute", + "allowedCommands", + "allowedMaxRequests", + "autoCondenseContextPercent", - autoApprovalEnabled: undefined, - alwaysAllowReadOnly: undefined, - alwaysAllowReadOnlyOutsideWorkspace: undefined, - alwaysAllowWrite: undefined, - alwaysAllowWriteOutsideWorkspace: undefined, - writeDelayMs: undefined, - alwaysAllowBrowser: undefined, - alwaysApproveResubmit: undefined, - requestDelaySeconds: undefined, - alwaysAllowMcp: undefined, - alwaysAllowModeSwitch: undefined, - alwaysAllowSubtasks: undefined, - alwaysAllowExecute: undefined, - allowedCommands: undefined, - allowedMaxRequests: undefined, - autoCondenseContextPercent: undefined, + "browserToolEnabled", + "browserViewportSize", + "screenshotQuality", + "remoteBrowserEnabled", + "remoteBrowserHost", - browserToolEnabled: undefined, - browserViewportSize: undefined, - screenshotQuality: undefined, - remoteBrowserEnabled: undefined, - remoteBrowserHost: undefined, + "enableCheckpoints", - enableCheckpoints: undefined, + "ttsEnabled", + "ttsSpeed", + "soundEnabled", + "soundVolume", - ttsEnabled: undefined, - ttsSpeed: undefined, - soundEnabled: undefined, - soundVolume: undefined, + "maxOpenTabsContext", + "maxWorkspaceFiles", + "showRooIgnoredFiles", + "maxReadFileLine", - maxOpenTabsContext: undefined, - maxWorkspaceFiles: undefined, - showRooIgnoredFiles: undefined, - maxReadFileLine: undefined, + "terminalOutputLineLimit", + "terminalShellIntegrationTimeout", + "terminalShellIntegrationDisabled", + "terminalCommandDelay", + "terminalPowershellCounter", + "terminalZshClearEolMark", + "terminalZshOhMy", + "terminalZshP10k", + "terminalZdotdir", + "terminalCompressProgressBar", - terminalOutputLineLimit: undefined, - terminalShellIntegrationTimeout: undefined, - terminalShellIntegrationDisabled: undefined, - terminalCommandDelay: undefined, - terminalPowershellCounter: undefined, - terminalZshClearEolMark: undefined, - terminalZshOhMy: undefined, - terminalZshP10k: undefined, - terminalZdotdir: undefined, - terminalCompressProgressBar: undefined, + "rateLimitSeconds", + "diffEnabled", + "fuzzyMatchThreshold", + "experiments", - rateLimitSeconds: undefined, - diffEnabled: undefined, - fuzzyMatchThreshold: undefined, - experiments: undefined, + "codebaseIndexModels", + "codebaseIndexConfig", - language: undefined, + "language", - telemetrySetting: undefined, + "telemetrySetting", + "mcpEnabled", + "enableMcpServerCreation", - mcpEnabled: undefined, - enableMcpServerCreation: undefined, - - mode: undefined, - modeApiConfigs: undefined, - customModes: undefined, - customModePrompts: undefined, - customSupportPrompts: undefined, - enhancementApiConfigId: undefined, - cachedChromeHostUrl: undefined, - historyPreviewCollapsed: undefined, -} - -export const GLOBAL_SETTINGS_KEYS = Object.keys(globalSettingsRecord) as Keys[] + "mode", + "modeApiConfigs", + "customModes", + "customModePrompts", + "customSupportPrompts", + "enhancementApiConfigId", + "cachedChromeHostUrl", + "historyPreviewCollapsed", +]) /** * RooCodeSettings @@ -224,33 +221,27 @@ export type SecretState = Pick< | "codeIndexQdrantApiKey" > -export type CodeIndexSecrets = "codeIndexOpenAiKey" | "codeIndexQdrantApiKey" - -type SecretStateRecord = Record, undefined> - -const secretStateRecord: SecretStateRecord = { - apiKey: undefined, - glamaApiKey: undefined, - openRouterApiKey: undefined, - awsAccessKey: undefined, - awsSecretKey: undefined, - awsSessionToken: undefined, - openAiApiKey: undefined, - geminiApiKey: undefined, - openAiNativeApiKey: undefined, - deepSeekApiKey: undefined, - mistralApiKey: undefined, - unboundApiKey: undefined, - requestyApiKey: undefined, - xaiApiKey: undefined, - groqApiKey: undefined, - chutesApiKey: undefined, - litellmApiKey: undefined, - codeIndexOpenAiKey: undefined, - codeIndexQdrantApiKey: undefined, -} - -export const SECRET_STATE_KEYS = Object.keys(secretStateRecord) as Keys[] +export const SECRET_STATE_KEYS = keysOf()([ + "apiKey", + "glamaApiKey", + "openRouterApiKey", + "awsAccessKey", + "awsSecretKey", + "awsSessionToken", + "openAiApiKey", + "geminiApiKey", + "openAiNativeApiKey", + "deepSeekApiKey", + "mistralApiKey", + "unboundApiKey", + "requestyApiKey", + "xaiApiKey", + "groqApiKey", + "chutesApiKey", + "litellmApiKey", + "codeIndexOpenAiKey", + "codeIndexQdrantApiKey", +]) export const isSecretStateKey = (key: string): key is Keys => SECRET_STATE_KEYS.includes(key as Keys) diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index 6803cebc11..7076361ea5 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -1,6 +1,6 @@ import { z } from "zod" -import type { Keys } from "./type-fu.js" +import { keysOf } from "./type-fu.js" import { reasoningEffortsSchema, modelInfoSchema } from "./model.js" import { codebaseIndexProviderSchema } from "./codebase-index.js" @@ -257,104 +257,100 @@ export const providerSettingsSchema = z.object({ export type ProviderSettings = z.infer -type ProviderSettingsRecord = Record, undefined> - -const providerSettingsRecord: ProviderSettingsRecord = { - apiProvider: undefined, +export const PROVIDER_SETTINGS_KEYS = keysOf()([ + "apiProvider", // Anthropic - apiModelId: undefined, - apiKey: undefined, - anthropicBaseUrl: undefined, - anthropicUseAuthToken: undefined, + "apiModelId", + "apiKey", + "anthropicBaseUrl", + "anthropicUseAuthToken", // Glama - glamaModelId: undefined, - glamaApiKey: undefined, + "glamaModelId", + "glamaApiKey", // OpenRouter - openRouterApiKey: undefined, - openRouterModelId: undefined, - openRouterBaseUrl: undefined, - openRouterSpecificProvider: undefined, - openRouterUseMiddleOutTransform: undefined, + "openRouterApiKey", + "openRouterModelId", + "openRouterBaseUrl", + "openRouterSpecificProvider", + "openRouterUseMiddleOutTransform", // Amazon Bedrock - awsAccessKey: undefined, - awsSecretKey: undefined, - awsSessionToken: undefined, - awsRegion: undefined, - awsUseCrossRegionInference: undefined, - awsUsePromptCache: undefined, - awsProfile: undefined, - awsUseProfile: undefined, - awsCustomArn: undefined, + "awsAccessKey", + "awsSecretKey", + "awsSessionToken", + "awsRegion", + "awsUseCrossRegionInference", + "awsUsePromptCache", + "awsProfile", + "awsUseProfile", + "awsCustomArn", // Google Vertex - vertexKeyFile: undefined, - vertexJsonCredentials: undefined, - vertexProjectId: undefined, - vertexRegion: undefined, + "vertexKeyFile", + "vertexJsonCredentials", + "vertexProjectId", + "vertexRegion", // OpenAI - openAiBaseUrl: undefined, - openAiApiKey: undefined, - openAiLegacyFormat: undefined, - openAiR1FormatEnabled: undefined, - openAiModelId: undefined, - openAiCustomModelInfo: undefined, - openAiUseAzure: undefined, - azureApiVersion: undefined, - openAiStreamingEnabled: undefined, - openAiHostHeader: undefined, // Keep temporarily for backward compatibility during migration - openAiHeaders: undefined, + "openAiBaseUrl", + "openAiApiKey", + "openAiLegacyFormat", + "openAiR1FormatEnabled", + "openAiModelId", + "openAiCustomModelInfo", + "openAiUseAzure", + "azureApiVersion", + "openAiStreamingEnabled", + "openAiHostHeader", // Keep temporarily for backward compatibility during migration. + "openAiHeaders", // Ollama - ollamaModelId: undefined, - ollamaBaseUrl: undefined, + "ollamaModelId", + "ollamaBaseUrl", // VS Code LM - vsCodeLmModelSelector: undefined, - lmStudioModelId: undefined, - lmStudioBaseUrl: undefined, - lmStudioDraftModelId: undefined, - lmStudioSpeculativeDecodingEnabled: undefined, + "vsCodeLmModelSelector", + "lmStudioModelId", + "lmStudioBaseUrl", + "lmStudioDraftModelId", + "lmStudioSpeculativeDecodingEnabled", // Gemini - geminiApiKey: undefined, - googleGeminiBaseUrl: undefined, + "geminiApiKey", + "googleGeminiBaseUrl", // OpenAI Native - openAiNativeApiKey: undefined, - openAiNativeBaseUrl: undefined, + "openAiNativeApiKey", + "openAiNativeBaseUrl", // Mistral - mistralApiKey: undefined, - mistralCodestralUrl: undefined, + "mistralApiKey", + "mistralCodestralUrl", // DeepSeek - deepSeekBaseUrl: undefined, - deepSeekApiKey: undefined, + "deepSeekBaseUrl", + "deepSeekApiKey", // Unbound - unboundApiKey: undefined, - unboundModelId: undefined, + "unboundApiKey", + "unboundModelId", // Requesty - requestyApiKey: undefined, - requestyModelId: undefined, + "requestyApiKey", + "requestyModelId", // Code Index - codeIndexOpenAiKey: undefined, - codeIndexQdrantApiKey: undefined, + "codeIndexOpenAiKey", + "codeIndexQdrantApiKey", // Reasoning - enableReasoningEffort: undefined, - reasoningEffort: undefined, - modelMaxTokens: undefined, - modelMaxThinkingTokens: undefined, + "enableReasoningEffort", + "reasoningEffort", + "modelMaxTokens", + "modelMaxThinkingTokens", // Generic - includeMaxTokens: undefined, - diffEnabled: undefined, - fuzzyMatchThreshold: undefined, - modelTemperature: undefined, - rateLimitSeconds: undefined, + "includeMaxTokens", + "diffEnabled", + "fuzzyMatchThreshold", + "modelTemperature", + "rateLimitSeconds", // Fake AI - fakeAi: undefined, + "fakeAi", // X.AI (Grok) - xaiApiKey: undefined, + "xaiApiKey", // Groq - groqApiKey: undefined, + "groqApiKey", // Chutes AI - chutesApiKey: undefined, + "chutesApiKey", // LiteLLM - litellmBaseUrl: undefined, - litellmApiKey: undefined, - litellmModelId: undefined, -} - -export const PROVIDER_SETTINGS_KEYS = Object.keys(providerSettingsRecord) as Keys[] + "litellmBaseUrl", + "litellmApiKey", + "litellmModelId", +]) diff --git a/packages/types/src/type-fu.ts b/packages/types/src/type-fu.ts index 0014e9b187..f5962de6f0 100644 --- a/packages/types/src/type-fu.ts +++ b/packages/types/src/type-fu.ts @@ -9,3 +9,13 @@ export type Values = T[keyof T] export type Equals = (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? true : false export type AssertEqual = T + +/** + * Creates a type-safe keys array that enforces ALL keys from type T are present. + * Returns a compile-time error if any keys are missing or extra keys are provided. + */ +export function keysOf() { + return ( + keys: keyof T extends U[number] ? (U[number] extends keyof T ? U : never) : never, + ): U => keys +} From 057ac3e42837101ea6b8ab13893e8ad65d322b9b Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Mon, 26 May 2025 22:00:39 -0700 Subject: [PATCH 016/104] Add `dependsOn` for watch:esbuild script (#4026) --- .vscode/tasks.json | 10 +++++----- src/package.json | 2 +- turbo.json | 7 +++++++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 4236934f1a..549a1174a9 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -5,7 +5,7 @@ "tasks": [ { "label": "watch", - "dependsOn": ["webview", "watch:tsc", "watch:esbuild"], + "dependsOn": ["watch:webview", "watch:bundle", "watch:tsc"], "presentation": { "reveal": "never" }, @@ -15,7 +15,7 @@ } }, { - "label": "webview", + "label": "watch:webview", "type": "shell", "command": "pnpm --filter @roo-code/vscode-webview dev", "group": "build", @@ -37,9 +37,9 @@ } }, { - "label": "watch:esbuild", + "label": "watch:bundle", "type": "shell", - "command": "pnpm --filter roo-cline watch:esbuild", + "command": "npx turbo watch:bundle", "group": "build", "problemMatcher": { "owner": "esbuild", @@ -61,7 +61,7 @@ { "label": "watch:tsc", "type": "shell", - "command": "pnpm --filter roo-cline watch:tsc", + "command": "npx turbo watch:tsc", "group": "build", "problemMatcher": "$tsc-watch", "isBackground": true, diff --git a/src/package.json b/src/package.json index dba6499244..2ad46c4c13 100644 --- a/src/package.json +++ b/src/package.json @@ -327,7 +327,7 @@ "vscode:prepublish": "pnpm bundle --production", "vsix": "mkdirp ../bin && npx vsce package --no-dependencies --out ../bin", "publish:marketplace": "vsce publish --no-dependencies && ovsx publish --no-dependencies", - "watch:esbuild": "pnpm bundle --watch", + "watch:bundle": "pnpm bundle --watch", "watch:tsc": "tsc --noEmit --watch --project tsconfig.json", "clean": "rimraf README.md CHANGELOG.md LICENSE dist webview-ui out mock .turbo" }, diff --git a/turbo.json b/turbo.json index 55ef646fa6..ea05254302 100644 --- a/turbo.json +++ b/turbo.json @@ -30,6 +30,13 @@ "vsix:nightly": { "dependsOn": ["bundle:nightly", "@roo-code/vscode-webview#build:nightly"], "cache": false + }, + "watch:bundle": { + "dependsOn": ["@roo-code/build#build", "@roo-code/types#build"], + "cache": false + }, + "watch:tsc": { + "cache": false } } } From 82f9e9e47c3faee5e33f251fb48c30314ad62237 Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Mon, 26 May 2025 22:05:47 -0700 Subject: [PATCH 017/104] Fix the build_extension step of the evals setup script (#4028) --- evals/scripts/setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evals/scripts/setup.sh b/evals/scripts/setup.sh index 69a43273dd..8b031bddb5 100755 --- a/evals/scripts/setup.sh +++ b/evals/scripts/setup.sh @@ -28,7 +28,7 @@ build_extension() { echo "🔨 Building the Roo Code extension..." cd .. mkdir -p bin - pnpm build --out ../bin/roo-code-$(git rev-parse --short HEAD).vsix || exit 1 + pnpm build -- --out ../bin/roo-code-$(git rev-parse --short HEAD).vsix || exit 1 code --install-extension bin/roo-code-$(git rev-parse --short HEAD).vsix || exit 1 cd evals } From 9d4b4ebff09e1b0a4ced5f71360252b70d0746a5 Mon Sep 17 00:00:00 2001 From: slytechnical <139649758+slytechnical@users.noreply.github.com> Date: Tue, 27 May 2025 05:54:57 -0500 Subject: [PATCH 018/104] Added support for dynamic litellm supports_computer_use (#4027) --- .../fetchers/__tests__/litellm.test.ts | 30 +++++++++++++++++-- src/api/providers/fetchers/litellm.ts | 8 ++--- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/api/providers/fetchers/__tests__/litellm.test.ts b/src/api/providers/fetchers/__tests__/litellm.test.ts index e908b6cef0..4e474cd995 100644 --- a/src/api/providers/fetchers/__tests__/litellm.test.ts +++ b/src/api/providers/fetchers/__tests__/litellm.test.ts @@ -1,6 +1,5 @@ import axios from "axios" import { getLiteLLMModels } from "../litellm" -import { OPEN_ROUTER_COMPUTER_USE_MODELS } from "../../../../shared/api" // Mock axios jest.mock("axios") @@ -26,6 +25,7 @@ describe("getLiteLLMModels", () => { supports_prompt_caching: false, input_cost_per_token: 0.000003, output_cost_per_token: 0.000015, + supports_computer_use: true, }, litellm_params: { model: "anthropic/claude-3.5-sonnet", @@ -40,6 +40,7 @@ describe("getLiteLLMModels", () => { supports_prompt_caching: false, input_cost_per_token: 0.00001, output_cost_per_token: 0.00003, + supports_computer_use: false, }, litellm_params: { model: "openai/gpt-4-turbo", @@ -105,7 +106,6 @@ describe("getLiteLLMModels", () => { }) it("handles computer use models correctly", async () => { - const computerUseModel = Array.from(OPEN_ROUTER_COMPUTER_USE_MODELS)[0] const mockResponse = { data: { data: [ @@ -115,9 +115,22 @@ describe("getLiteLLMModels", () => { max_tokens: 4096, max_input_tokens: 200000, supports_vision: true, + supports_computer_use: true, }, litellm_params: { - model: `anthropic/${computerUseModel}`, + model: `anthropic/test-computer-model`, + }, + }, + { + model_name: "test-non-computer-model", + model_info: { + max_tokens: 4096, + max_input_tokens: 200000, + supports_vision: false, + supports_computer_use: false, + }, + litellm_params: { + model: `anthropic/test-non-computer-model`, }, }, ], @@ -138,6 +151,17 @@ describe("getLiteLLMModels", () => { outputPrice: undefined, description: "test-computer-model via LiteLLM proxy", }) + + expect(result["test-non-computer-model"]).toEqual({ + maxTokens: 4096, + contextWindow: 200000, + supportsImages: false, + supportsComputerUse: false, + supportsPromptCache: false, + inputPrice: undefined, + outputPrice: undefined, + description: "test-non-computer-model via LiteLLM proxy", + }) }) it("throws error for unexpected response format", async () => { diff --git a/src/api/providers/fetchers/litellm.ts b/src/api/providers/fetchers/litellm.ts index a3591d7466..dbd6ccc73a 100644 --- a/src/api/providers/fetchers/litellm.ts +++ b/src/api/providers/fetchers/litellm.ts @@ -1,6 +1,6 @@ import axios from "axios" -import { OPEN_ROUTER_COMPUTER_USE_MODELS, ModelRecord } from "../../../shared/api" +import { ModelRecord } from "../../../shared/api" /** * Fetches available models from a LiteLLM server @@ -23,8 +23,6 @@ export async function getLiteLLMModels(apiKey: string, baseUrl: string): Promise const response = await axios.get(`${baseUrl}/v1/model/info`, { headers, timeout: 5000 }) const models: ModelRecord = {} - const computerModels = Array.from(OPEN_ROUTER_COMPUTER_USE_MODELS) - // Process the model info from the response if (response.data && response.data.data && Array.isArray(response.data.data)) { for (const model of response.data.data) { @@ -39,9 +37,7 @@ export async function getLiteLLMModels(apiKey: string, baseUrl: string): Promise contextWindow: modelInfo.max_input_tokens || 200000, supportsImages: Boolean(modelInfo.supports_vision), // litellm_params.model may have a prefix like openrouter/ - supportsComputerUse: computerModels.some((computer_model) => - litellmModelName.endsWith(computer_model), - ), + supportsComputerUse: Boolean(modelInfo.supports_computer_use), supportsPromptCache: Boolean(modelInfo.supports_prompt_caching), inputPrice: modelInfo.input_cost_per_token ? modelInfo.input_cost_per_token * 1000000 : undefined, outputPrice: modelInfo.output_cost_per_token From 4ea75629e96366e05fa1d9cd518a4b9eeebb663d Mon Sep 17 00:00:00 2001 From: Daniel Trugman Date: Tue, 27 May 2025 16:16:25 +0100 Subject: [PATCH 019/104] Add thinking to Requesty provider (#4041) --- .changeset/new-shoes-flow.md | 5 +++++ src/api/providers/fetchers/requesty.ts | 5 +++++ src/api/providers/requesty.ts | 23 ++++++++++++++++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 .changeset/new-shoes-flow.md diff --git a/.changeset/new-shoes-flow.md b/.changeset/new-shoes-flow.md new file mode 100644 index 0000000000..9d68182278 --- /dev/null +++ b/.changeset/new-shoes-flow.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Add thinking controls for Requesty diff --git a/src/api/providers/fetchers/requesty.ts b/src/api/providers/fetchers/requesty.ts index 41f2bdb9b6..c629666f82 100644 --- a/src/api/providers/fetchers/requesty.ts +++ b/src/api/providers/fetchers/requesty.ts @@ -19,12 +19,17 @@ export async function getRequestyModels(apiKey?: string): Promise Date: Tue, 27 May 2025 18:22:28 +0200 Subject: [PATCH 020/104] Improve zh-TW Traditional Chinese locale (#4048) Hello Roo Team! We received this contribution on the Kilo side and thought it might be useful to you! These changes were originally submitted by a (presumably) native speaker of the language: https://github.com/Kilo-Org/kilocode/pull/516 --- src/package.nls.zh-TW.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/package.nls.zh-TW.json b/src/package.nls.zh-TW.json index e9349416f2..bb31058fc6 100644 --- a/src/package.nls.zh-TW.json +++ b/src/package.nls.zh-TW.json @@ -5,11 +5,11 @@ "command.explainCode.title": "解釋程式碼", "command.fixCode.title": "修復程式碼", "command.improveCode.title": "改進程式碼", - "command.addToContext.title": "添加到上下文", + "command.addToContext.title": "新增到上下文", "command.openInNewTab.title": "在新分頁中開啟", "command.focusInput.title": "聚焦輸入框", "command.setCustomStoragePath.title": "設定自訂儲存路徑", - "command.terminal.addToContext.title": "將終端內容添加到上下文", + "command.terminal.addToContext.title": "將終端內容新增到上下文", "command.terminal.fixCommand.title": "修復此命令", "command.terminal.explainCommand.title": "解釋此命令", "command.acceptInput.title": "接受輸入/建議", @@ -25,7 +25,7 @@ "configuration.title": "Roo Code", "commands.allowedCommands.description": "當啟用'始終批准執行操作'時可以自動執行的命令", "settings.vsCodeLmModelSelector.description": "VSCode 語言模型 API 的設定", - "settings.vsCodeLmModelSelector.vendor.description": "語言模型的供應商(例如:copilot)", - "settings.vsCodeLmModelSelector.family.description": "語言模型的系列(例如:gpt-4)", + "settings.vsCodeLmModelSelector.vendor.description": "語言模型供應商(例如:copilot)", + "settings.vsCodeLmModelSelector.family.description": "語言模型系列(例如:gpt-4)", "settings.customStoragePath.description": "自訂儲存路徑。留空以使用預設位置。支援絕對路徑(例如:'D:\\RooCodeStorage')" } From f37e6f6fcee26e7ce1997f205962ac39b32ccf5a Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Tue, 27 May 2025 11:16:39 -0700 Subject: [PATCH 021/104] Fix Requesty extended thinking (#4051) --- src/api/providers/__tests__/deepseek.test.ts | 8 +- src/api/providers/__tests__/requesty.test.ts | 10 +- src/api/providers/mistral.ts | 68 ++++++------ src/api/providers/openai.ts | 1 + src/api/providers/requesty.ts | 110 +++++++------------ 5 files changed, 82 insertions(+), 115 deletions(-) diff --git a/src/api/providers/__tests__/deepseek.test.ts b/src/api/providers/__tests__/deepseek.test.ts index eb00bf6d65..0fc7509296 100644 --- a/src/api/providers/__tests__/deepseek.test.ts +++ b/src/api/providers/__tests__/deepseek.test.ts @@ -140,12 +140,8 @@ describe("DeepSeekHandler", () => { it("should set includeMaxTokens to true", () => { // Create a new handler and verify OpenAI client was called with includeMaxTokens - new DeepSeekHandler(mockOptions) - expect(OpenAI).toHaveBeenCalledWith( - expect.objectContaining({ - apiKey: mockOptions.deepSeekApiKey, - }), - ) + const _handler = new DeepSeekHandler(mockOptions) + expect(OpenAI).toHaveBeenCalledWith(expect.objectContaining({ apiKey: mockOptions.deepSeekApiKey })) }) }) diff --git a/src/api/providers/__tests__/requesty.test.ts b/src/api/providers/__tests__/requesty.test.ts index 43d71a7d9d..355918227d 100644 --- a/src/api/providers/__tests__/requesty.test.ts +++ b/src/api/providers/__tests__/requesty.test.ts @@ -7,7 +7,9 @@ import { RequestyHandler } from "../requesty" import { ApiHandlerOptions } from "../../../shared/api" jest.mock("openai") + jest.mock("delay", () => jest.fn(() => Promise.resolve())) + jest.mock("../fetchers/modelCache", () => ({ getModels: jest.fn().mockImplementation(() => { return Promise.resolve({ @@ -150,7 +152,7 @@ describe("RequestyHandler", () => { // Verify OpenAI client was called with correct parameters expect(mockCreate).toHaveBeenCalledWith( expect.objectContaining({ - max_tokens: undefined, + max_tokens: 8192, messages: [ { role: "system", @@ -164,7 +166,7 @@ describe("RequestyHandler", () => { model: "coding/claude-4-sonnet", stream: true, stream_options: { include_usage: true }, - temperature: undefined, + temperature: 0, }), ) }) @@ -198,9 +200,9 @@ describe("RequestyHandler", () => { expect(mockCreate).toHaveBeenCalledWith({ model: mockOptions.requestyModelId, - max_tokens: undefined, + max_tokens: 8192, messages: [{ role: "system", content: "test prompt" }], - temperature: undefined, + temperature: 0, }) }) diff --git a/src/api/providers/mistral.ts b/src/api/providers/mistral.ts index 5aafb16012..5ed3140aff 100644 --- a/src/api/providers/mistral.ts +++ b/src/api/providers/mistral.ts @@ -18,60 +18,50 @@ export class MistralHandler extends BaseProvider implements SingleCompletionHand constructor(options: ApiHandlerOptions) { super() + if (!options.mistralApiKey) { throw new Error("Mistral API key is required") } - // Set default model ID if not provided - this.options = { - ...options, - apiModelId: options.apiModelId || mistralDefaultModelId, - } + // Set default model ID if not provided. + const apiModelId = options.apiModelId || mistralDefaultModelId + this.options = { ...options, apiModelId } - const baseUrl = this.getBaseUrl() - console.debug(`[Roo Code] MistralHandler using baseUrl: ${baseUrl}`) this.client = new Mistral({ - serverURL: baseUrl, + serverURL: apiModelId.startsWith("codestral-") + ? this.options.mistralCodestralUrl || "https://codestral.mistral.ai" + : "https://api.mistral.ai", apiKey: this.options.mistralApiKey, }) } - private getBaseUrl(): string { - const modelId = this.options.apiModelId ?? mistralDefaultModelId - console.debug(`[Roo Code] MistralHandler using modelId: ${modelId}`) - if (modelId?.startsWith("codestral-")) { - return this.options.mistralCodestralUrl || "https://codestral.mistral.ai" - } - return "https://api.mistral.ai" - } - override async *createMessage( systemPrompt: string, messages: Anthropic.Messages.MessageParam[], metadata?: ApiHandlerCreateMessageMetadata, ): ApiStream { - const { id: model } = this.getModel() + const { id: model, maxTokens, temperature } = this.getModel() const response = await this.client.chat.stream({ - model: this.options.apiModelId || mistralDefaultModelId, + model, messages: [{ role: "system", content: systemPrompt }, ...convertToMistralMessages(messages)], - maxTokens: this.options.includeMaxTokens ? this.getModel().info.maxTokens : undefined, - temperature: this.options.modelTemperature ?? MISTRAL_DEFAULT_TEMPERATURE, + maxTokens, + temperature, }) for await (const chunk of response) { const delta = chunk.data.choices[0]?.delta + if (delta?.content) { let content: string = "" + if (typeof delta.content === "string") { content = delta.content } else if (Array.isArray(delta.content)) { content = delta.content.map((c) => (c.type === "text" ? c.text : "")).join("") } - yield { - type: "text", - text: content, - } + + yield { type: "text", text: content } } if (chunk.data.usage) { @@ -84,35 +74,39 @@ export class MistralHandler extends BaseProvider implements SingleCompletionHand } } - override getModel(): { id: MistralModelId; info: ModelInfo } { - const modelId = this.options.apiModelId - if (modelId && modelId in mistralModels) { - const id = modelId as MistralModelId - return { id, info: mistralModels[id] } - } - return { - id: mistralDefaultModelId, - info: mistralModels[mistralDefaultModelId], - } + override getModel() { + const id = this.options.apiModelId ?? mistralDefaultModelId + const info = mistralModels[id as MistralModelId] ?? mistralModels[mistralDefaultModelId] + + // @TODO: Move this to the `getModelParams` function. + const maxTokens = this.options.includeMaxTokens ? info.maxTokens : undefined + const temperature = this.options.modelTemperature ?? MISTRAL_DEFAULT_TEMPERATURE + + return { id, info, maxTokens, temperature } } async completePrompt(prompt: string): Promise { try { + const { id: model, temperature } = this.getModel() + const response = await this.client.chat.complete({ - model: this.options.apiModelId || mistralDefaultModelId, + model, messages: [{ role: "user", content: prompt }], - temperature: this.options.modelTemperature ?? MISTRAL_DEFAULT_TEMPERATURE, + temperature, }) const content = response.choices?.[0]?.message.content + if (Array.isArray(content)) { return content.map((c) => (c.type === "text" ? c.text : "")).join("") } + return content || "" } catch (error) { if (error instanceof Error) { throw new Error(`Mistral completion error: ${error.message}`) } + throw error } } diff --git a/src/api/providers/openai.ts b/src/api/providers/openai.ts index 43c5a0e6da..3e7324f5d9 100644 --- a/src/api/providers/openai.ts +++ b/src/api/providers/openai.ts @@ -154,6 +154,7 @@ export class OpenAiHandler extends BaseProvider implements SingleCompletionHandl ...(reasoning && reasoning), } + // @TODO: Move this to the `getModelParams` function. if (this.options.includeMaxTokens) { requestOptions.max_tokens = modelInfo.maxTokens } diff --git a/src/api/providers/requesty.ts b/src/api/providers/requesty.ts index e581b94e8c..8317ad250c 100644 --- a/src/api/providers/requesty.ts +++ b/src/api/providers/requesty.ts @@ -8,11 +8,13 @@ import { calculateApiCostOpenAI } from "../../shared/cost" import { convertToOpenAiMessages } from "../transform/openai-format" import { ApiStream, ApiStreamUsageChunk } from "../transform/stream" +import { getModelParams } from "../transform/model-params" +import { AnthropicReasoningParams } from "../transform/reasoning" import { DEFAULT_HEADERS } from "./constants" import { getModels } from "./fetchers/modelCache" import { BaseProvider } from "./base-provider" -import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../" +import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index" // Requesty usage includes an extra field for Anthropic use cases. // Safely cast the prompt token details section to the appropriate structure. @@ -31,10 +33,7 @@ type RequestyChatCompletionParams = OpenAI.Chat.ChatCompletionCreateParams & { mode?: string } } - thinking?: { - type: string - budget_tokens?: number - } + thinking?: AnthropicReasoningParams } export class RequestyHandler extends BaseProvider implements SingleCompletionHandler { @@ -44,14 +43,14 @@ export class RequestyHandler extends BaseProvider implements SingleCompletionHan constructor(options: ApiHandlerOptions) { super() + this.options = options - const apiKey = this.options.requestyApiKey ?? "not-provided" - const baseURL = "https://router.requesty.ai/v1" - - const defaultHeaders = DEFAULT_HEADERS - - this.client = new OpenAI({ baseURL, apiKey, defaultHeaders }) + this.client = new OpenAI({ + baseURL: "https://router.requesty.ai/v1", + apiKey: this.options.requestyApiKey ?? "not-provided", + defaultHeaders: DEFAULT_HEADERS, + }) } public async fetchModel() { @@ -59,10 +58,18 @@ export class RequestyHandler extends BaseProvider implements SingleCompletionHan return this.getModel() } - override getModel(): { id: string; info: ModelInfo } { + override getModel() { const id = this.options.requestyModelId ?? requestyDefaultModelId const info = this.models[id] ?? requestyDefaultModelInfo - return { id, info } + + const params = getModelParams({ + format: "anthropic", + modelId: id, + model: info, + settings: this.options, + }) + + return { id, info, ...params } } protected processUsageMetrics(usage: any, modelInfo?: ModelInfo): ApiStreamUsageChunk { @@ -90,70 +97,44 @@ export class RequestyHandler extends BaseProvider implements SingleCompletionHan messages: Anthropic.Messages.MessageParam[], metadata?: ApiHandlerCreateMessageMetadata, ): ApiStream { - const model = await this.fetchModel() + const { + id: model, + info, + maxTokens: max_tokens, + temperature, + reasoningEffort: reasoning_effort, + reasoning: thinking, + } = await this.fetchModel() - let openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [ + const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [ { role: "system", content: systemPrompt }, ...convertToOpenAiMessages(messages), ] - let maxTokens = undefined - if (this.options.modelMaxTokens) { - maxTokens = this.options.modelMaxTokens - } else if (this.options.includeMaxTokens) { - maxTokens = model.info.maxTokens - } - - let reasoningEffort = undefined - if (this.options.reasoningEffort) { - reasoningEffort = this.options.reasoningEffort - } - - let thinking = undefined - if (this.options.modelMaxThinkingTokens) { - thinking = { - type: "enabled", - budget_tokens: this.options.modelMaxThinkingTokens, - } - } - - const temperature = this.options.modelTemperature - const completionParams: RequestyChatCompletionParams = { - model: model.id, - max_tokens: maxTokens, messages: openAiMessages, - temperature: temperature, + model, + max_tokens, + temperature, + ...(reasoning_effort && { reasoning_effort }), + ...(thinking && { thinking }), stream: true, stream_options: { include_usage: true }, - reasoning_effort: reasoningEffort, - thinking: thinking, - requesty: { - trace_id: metadata?.taskId, - extra: { - mode: metadata?.mode, - }, - }, + requesty: { trace_id: metadata?.taskId, extra: { mode: metadata?.mode } }, } const stream = await this.client.chat.completions.create(completionParams) - let lastUsage: any = undefined for await (const chunk of stream) { const delta = chunk.choices[0]?.delta + if (delta?.content) { - yield { - type: "text", - text: delta.content, - } + yield { type: "text", text: delta.content } } if (delta && "reasoning_content" in delta && delta.reasoning_content) { - yield { - type: "reasoning", - text: (delta.reasoning_content as string | undefined) || "", - } + yield { type: "reasoning", text: (delta.reasoning_content as string | undefined) || "" } } if (chunk.usage) { @@ -162,25 +143,18 @@ export class RequestyHandler extends BaseProvider implements SingleCompletionHan } if (lastUsage) { - yield this.processUsageMetrics(lastUsage, model.info) + yield this.processUsageMetrics(lastUsage, info) } } async completePrompt(prompt: string): Promise { - const model = await this.fetchModel() + const { id: model, maxTokens: max_tokens, temperature } = await this.fetchModel() let openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [{ role: "system", content: prompt }] - let maxTokens = undefined - if (this.options.includeMaxTokens) { - maxTokens = model.info.maxTokens - } - - const temperature = this.options.modelTemperature - const completionParams: RequestyChatCompletionParams = { - model: model.id, - max_tokens: maxTokens, + model, + max_tokens, messages: openAiMessages, temperature: temperature, } From ed24f65b300b56145f4a6614f8b0477b6f4ae25f Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Tue, 27 May 2025 11:19:21 -0700 Subject: [PATCH 022/104] Fix Posthog by correctly copying .env in the build process (#4049) --- .changeset/thirty-cases-fail.md | 5 +++ .github/workflows/marketplace-publish.yml | 25 +++++++----- .prettierignore | 6 --- .prettierrc.json | 3 +- apps/vscode-nightly/esbuild.mjs | 1 + packages/build/src/esbuild.ts | 49 +++++++++++++++-------- src/esbuild.mjs | 1 + 7 files changed, 58 insertions(+), 32 deletions(-) create mode 100644 .changeset/thirty-cases-fail.md delete mode 100644 .prettierignore diff --git a/.changeset/thirty-cases-fail.md b/.changeset/thirty-cases-fail.md new file mode 100644 index 0000000000..cfa78e2102 --- /dev/null +++ b/.changeset/thirty-cases-fail.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Fix Posthog by correctly copying .env in the build process diff --git a/.github/workflows/marketplace-publish.yml b/.github/workflows/marketplace-publish.yml index d86be2083b..6e2dc09a01 100644 --- a/.github/workflows/marketplace-publish.yml +++ b/.github/workflows/marketplace-publish.yml @@ -45,15 +45,22 @@ jobs: run: | current_package_version=$(node -p "require('./src/package.json').version") pnpm build - package=$(unzip -l bin/roo-cline-${current_package_version}.vsix) - echo "$package" | grep -q "extension/package.json" || exit 1 - echo "$package" | grep -q "extension/package.nls.json" || exit 1 - echo "$package" | grep -q "extension/dist/extension.js" || exit 1 - echo "$package" | grep -q "extension/webview-ui/audio/celebration.wav" || exit 1 - echo "$package" | grep -q "extension/webview-ui/build/assets/index.js" || exit 1 - echo "$package" | grep -q "extension/assets/codicons/codicon.ttf" || exit 1 - echo "$package" | grep -q "extension/assets/vscode-material-icons/icons/3d.svg" || exit 1 - echo "$package" | grep -q ".env" || exit 1 + + # Save VSIX contents to a temporary file to avoid broken pipe issues. + unzip -l bin/roo-cline-${current_package_version}.vsix > /tmp/roo-code-vsix-contents.txt + + # Check for required files. + grep -q "extension/package.json" /tmp/roo-code-vsix-contents.txt || exit 1 + grep -q "extension/package.nls.json" /tmp/roo-code-vsix-contents.txt || exit 1 + grep -q "extension/dist/extension.js" /tmp/roo-code-vsix-contents.txt || exit 1 + grep -q "extension/webview-ui/audio/celebration.wav" /tmp/roo-code-vsix-contents.txt || exit 1 + grep -q "extension/webview-ui/build/assets/index.js" /tmp/roo-code-vsix-contents.txt || exit 1 + grep -q "extension/assets/codicons/codicon.ttf" /tmp/roo-code-vsix-contents.txt || exit 1 + grep -q "extension/assets/vscode-material-icons/icons/3d.svg" /tmp/roo-code-vsix-contents.txt || exit 1 + grep -q ".env" /tmp/roo-code-vsix-contents.txt || exit 1 + + # Clean up temporary file. + rm /tmp/roo-code-vsix-contents.txt - name: Create and Push Git Tag run: | current_package_version=$(node -p "require('./src/package.json').version") diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 7d8675f8cb..0000000000 --- a/.prettierignore +++ /dev/null @@ -1,6 +0,0 @@ -dist -build -out -.next -.venv -pnpm-lock.yaml diff --git a/.prettierrc.json b/.prettierrc.json index cd4329335c..520c1bd5f7 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -3,5 +3,6 @@ "useTabs": true, "printWidth": 120, "semi": false, - "bracketSameLine": true + "bracketSameLine": true, + "ignore": ["node_modules", "dist", "build", "out", ".next", ".venv", "pnpm-lock.yaml"] } diff --git a/apps/vscode-nightly/esbuild.mjs b/apps/vscode-nightly/esbuild.mjs index ccc999e78b..d4302fc100 100644 --- a/apps/vscode-nightly/esbuild.mjs +++ b/apps/vscode-nightly/esbuild.mjs @@ -67,6 +67,7 @@ async function main() { ["../README.md", "README.md"], ["../CHANGELOG.md", "CHANGELOG.md"], ["../LICENSE", "LICENSE"], + ["../.env", ".env", { optional: true }], [".vscodeignore", ".vscodeignore"], ["assets", "assets"], ["integrations", "integrations"], diff --git a/packages/build/src/esbuild.ts b/packages/build/src/esbuild.ts index e91275447d..ab76ca2ff7 100644 --- a/packages/build/src/esbuild.ts +++ b/packages/build/src/esbuild.ts @@ -29,7 +29,9 @@ function rmDir(dirPath: string, maxRetries: number = 3): void { return } catch (error) { const isLastAttempt = attempt === maxRetries - const isEnotemptyError = error instanceof Error && "code" in error && (error.code === 'ENOTEMPTY' || error.code === 'EBUSY') + + const isEnotemptyError = + error instanceof Error && "code" in error && (error.code === "ENOTEMPTY" || error.code === "EBUSY") if (isLastAttempt || !isEnotemptyError) { throw error // Re-throw if it's the last attempt or not a locking error. @@ -41,27 +43,42 @@ function rmDir(dirPath: string, maxRetries: number = 3): void { // Synchronous sleep for simplicity in build scripts. const start = Date.now() - while (Date.now() - start < delay) { /* Busy wait */ } + + while (Date.now() - start < delay) { + /* Busy wait */ + } } } } -export function copyPaths(copyPaths: [string, string][], srcDir: string, dstDir: string) { - copyPaths.forEach(([srcRelPath, dstRelPath]) => { - const stats = fs.lstatSync(path.join(srcDir, srcRelPath)) +type CopyPathOptions = { + optional?: boolean +} - if (stats.isDirectory()) { - if (fs.existsSync(path.join(dstDir, dstRelPath))) { - rmDir(path.join(dstDir, dstRelPath)) +export function copyPaths(copyPaths: [string, string, CopyPathOptions?][], srcDir: string, dstDir: string) { + copyPaths.forEach(([srcRelPath, dstRelPath, options = {}]) => { + try { + const stats = fs.lstatSync(path.join(srcDir, srcRelPath)) + + if (stats.isDirectory()) { + if (fs.existsSync(path.join(dstDir, dstRelPath))) { + rmDir(path.join(dstDir, dstRelPath)) + } + + fs.mkdirSync(path.join(dstDir, dstRelPath), { recursive: true }) + + const count = copyDir(path.join(srcDir, srcRelPath), path.join(dstDir, dstRelPath), 0) + console.log(`[copyPaths] Copied ${count} files from ${srcRelPath} to ${dstRelPath}`) + } else { + fs.copyFileSync(path.join(srcDir, srcRelPath), path.join(dstDir, dstRelPath)) + console.log(`[copyPaths] Copied ${srcRelPath} to ${dstRelPath}`) + } + } catch (error) { + if (options.optional) { + console.warn(`[copyPaths] Optional file not found: ${srcRelPath}`) + } else { + throw error } - - fs.mkdirSync(path.join(dstDir, dstRelPath), { recursive: true }) - - const count = copyDir(path.join(srcDir, srcRelPath), path.join(dstDir, dstRelPath), 0) - console.log(`[copyPaths] Copied ${count} files from ${srcRelPath} to ${dstRelPath}`) - } else { - fs.copyFileSync(path.join(srcDir, srcRelPath), path.join(dstDir, dstRelPath)) - console.log(`[copyPaths] Copied ${srcRelPath} to ${dstRelPath}`) } }) } diff --git a/src/esbuild.mjs b/src/esbuild.mjs index d8c96b4ede..3f7986bfc5 100644 --- a/src/esbuild.mjs +++ b/src/esbuild.mjs @@ -52,6 +52,7 @@ async function main() { ["../README.md", "README.md"], ["../CHANGELOG.md", "CHANGELOG.md"], ["../LICENSE", "LICENSE"], + ["../.env", ".env", { optional: true }], ["node_modules/vscode-material-icons/generated", "assets/vscode-material-icons"], ["../webview-ui/audio", "webview-ui/audio"], ], From 1441112c37d49acb12ab33824166c9ac3fca3dd6 Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Tue, 27 May 2025 12:41:54 -0700 Subject: [PATCH 023/104] v3.18.5 (#4054) --- .changeset/config.json | 2 +- .changeset/sour-keys-smash.md | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/sour-keys-smash.md diff --git a/.changeset/config.json b/.changeset/config.json index bcd6eefa00..e2acc37662 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -2,7 +2,7 @@ "$schema": "https://unpkg.com/@changesets/config@3.0.4/schema.json", "changelog": "./changelog-config.js", "commit": false, - "fixed": [], + "fixed": [["roo-cline"]], "linked": [], "access": "restricted", "baseBranch": "main", diff --git a/.changeset/sour-keys-smash.md b/.changeset/sour-keys-smash.md new file mode 100644 index 0000000000..95519e409c --- /dev/null +++ b/.changeset/sour-keys-smash.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +v3.18.5 From 343f29b432d9e475278f5d7758a1a5f122e081b4 Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Tue, 27 May 2025 12:56:46 -0700 Subject: [PATCH 024/104] Improve the prompt for the "modes" e2e test (#4055) --- .changeset/fine-eels-find.md | 5 ++++ apps/vscode-e2e/src/suite/modes.test.ts | 36 +++++++------------------ 2 files changed, 15 insertions(+), 26 deletions(-) create mode 100644 .changeset/fine-eels-find.md diff --git a/.changeset/fine-eels-find.md b/.changeset/fine-eels-find.md new file mode 100644 index 0000000000..169eb8c12c --- /dev/null +++ b/.changeset/fine-eels-find.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Improve the prompt for the "modes" e2e test diff --git a/apps/vscode-e2e/src/suite/modes.test.ts b/apps/vscode-e2e/src/suite/modes.test.ts index edc93d4c9d..817d5f71ce 100644 --- a/apps/vscode-e2e/src/suite/modes.test.ts +++ b/apps/vscode-e2e/src/suite/modes.test.ts @@ -1,40 +1,24 @@ import * as assert from "assert" -import type { ClineMessage } from "@roo-code/types" - import { waitUntilCompleted } from "./utils" suite("Roo Code Modes", () => { test("Should handle switching modes correctly", async () => { - const api = globalThis.api + const modes: string[] = [] - const switchModesPrompt = - "For each mode (Architect, Ask, Debug) respond with the mode name and what it specializes in after switching to that mode." + globalThis.api.on("taskModeSwitched", (_taskId, mode) => modes.push(mode)) - const messages: ClineMessage[] = [] - const modeSwitches: string[] = [] - - api.on("taskModeSwitched", (_taskId, mode) => { - console.log("taskModeSwitched", mode) - modeSwitches.push(mode) - }) - - api.on("message", ({ message }) => { - if (message.type === "say" && message.partial === false) { - messages.push(message) - } - }) - - const switchModesTaskId = await api.startNewTask({ + const switchModesTaskId = await globalThis.api.startNewTask({ configuration: { mode: "code", alwaysAllowModeSwitch: true, autoApprovalEnabled: true }, - text: switchModesPrompt, + text: "For each of `architect`, `ask`, and `debug` use the `switch_mode` tool to switch to that mode.", }) - await waitUntilCompleted({ api, taskId: switchModesTaskId }) - await api.cancelCurrentTask() + await waitUntilCompleted({ api: globalThis.api, taskId: switchModesTaskId }) + await globalThis.api.cancelCurrentTask() - assert.ok(modeSwitches.includes("architect")) - assert.ok(modeSwitches.includes("ask")) - assert.ok(modeSwitches.includes("debug")) + assert.ok(modes.includes("architect")) + assert.ok(modes.includes("ask")) + assert.ok(modes.includes("debug")) + assert.ok(modes.length === 3) }) }) From 73d162305a081f31b5fb8d6cf0241ba7b2112ce5 Mon Sep 17 00:00:00 2001 From: slytechnical <139649758+slytechnical@users.noreply.github.com> Date: Tue, 27 May 2025 14:58:20 -0500 Subject: [PATCH 025/104] =?UTF-8?q?Added=20a=20hardcoded=20list=20of=20com?= =?UTF-8?q?puter=20use=20models=20for=20litellm=20as=20a=20fallba=E2=80=A6?= =?UTF-8?q?=20(#4052)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added a hardcoded list of computer use models for litellm as a fallback for older litellm versions --- .../fetchers/__tests__/litellm.test.ts | 199 ++++++++++++++++++ src/api/providers/fetchers/litellm.ts | 17 +- src/shared/api.ts | 33 +++ 3 files changed, 247 insertions(+), 2 deletions(-) diff --git a/src/api/providers/fetchers/__tests__/litellm.test.ts b/src/api/providers/fetchers/__tests__/litellm.test.ts index 4e474cd995..49e928548f 100644 --- a/src/api/providers/fetchers/__tests__/litellm.test.ts +++ b/src/api/providers/fetchers/__tests__/litellm.test.ts @@ -248,4 +248,203 @@ describe("getLiteLLMModels", () => { expect(result).toEqual({}) }) + + it("uses fallback computer use detection when supports_computer_use is not available", async () => { + const mockResponse = { + data: { + data: [ + { + model_name: "claude-3-5-sonnet-latest", + model_info: { + max_tokens: 4096, + max_input_tokens: 200000, + supports_vision: true, + supports_prompt_caching: false, + // Note: no supports_computer_use field + }, + litellm_params: { + model: "anthropic/claude-3-5-sonnet-latest", // This should match the fallback list + }, + }, + { + model_name: "gpt-4-turbo", + model_info: { + max_tokens: 8192, + max_input_tokens: 128000, + supports_vision: false, + supports_prompt_caching: false, + // Note: no supports_computer_use field + }, + litellm_params: { + model: "openai/gpt-4-turbo", // This should NOT match the fallback list + }, + }, + ], + }, + } + + mockedAxios.get.mockResolvedValue(mockResponse) + + const result = await getLiteLLMModels("test-api-key", "http://localhost:4000") + + expect(result["claude-3-5-sonnet-latest"]).toEqual({ + maxTokens: 4096, + contextWindow: 200000, + supportsImages: true, + supportsComputerUse: true, // Should be true due to fallback + supportsPromptCache: false, + inputPrice: undefined, + outputPrice: undefined, + description: "claude-3-5-sonnet-latest via LiteLLM proxy", + }) + + expect(result["gpt-4-turbo"]).toEqual({ + maxTokens: 8192, + contextWindow: 128000, + supportsImages: false, + supportsComputerUse: false, // Should be false as it's not in fallback list + supportsPromptCache: false, + inputPrice: undefined, + outputPrice: undefined, + description: "gpt-4-turbo via LiteLLM proxy", + }) + }) + + it("prioritizes explicit supports_computer_use over fallback detection", async () => { + const mockResponse = { + data: { + data: [ + { + model_name: "claude-3-5-sonnet-latest", + model_info: { + max_tokens: 4096, + max_input_tokens: 200000, + supports_vision: true, + supports_prompt_caching: false, + supports_computer_use: false, // Explicitly set to false + }, + litellm_params: { + model: "anthropic/claude-3-5-sonnet-latest", // This matches fallback list but should be ignored + }, + }, + { + model_name: "custom-model", + model_info: { + max_tokens: 8192, + max_input_tokens: 128000, + supports_vision: false, + supports_prompt_caching: false, + supports_computer_use: true, // Explicitly set to true + }, + litellm_params: { + model: "custom/custom-model", // This would NOT match fallback list + }, + }, + { + model_name: "another-custom-model", + model_info: { + max_tokens: 8192, + max_input_tokens: 128000, + supports_vision: false, + supports_prompt_caching: false, + supports_computer_use: false, // Explicitly set to false + }, + litellm_params: { + model: "custom/another-custom-model", // This would NOT match fallback list + }, + }, + ], + }, + } + + mockedAxios.get.mockResolvedValue(mockResponse) + + const result = await getLiteLLMModels("test-api-key", "http://localhost:4000") + + expect(result["claude-3-5-sonnet-latest"]).toEqual({ + maxTokens: 4096, + contextWindow: 200000, + supportsImages: true, + supportsComputerUse: false, // False because explicitly set to false (fallback ignored) + supportsPromptCache: false, + inputPrice: undefined, + outputPrice: undefined, + description: "claude-3-5-sonnet-latest via LiteLLM proxy", + }) + + expect(result["custom-model"]).toEqual({ + maxTokens: 8192, + contextWindow: 128000, + supportsImages: false, + supportsComputerUse: true, // True because explicitly set to true + supportsPromptCache: false, + inputPrice: undefined, + outputPrice: undefined, + description: "custom-model via LiteLLM proxy", + }) + + expect(result["another-custom-model"]).toEqual({ + maxTokens: 8192, + contextWindow: 128000, + supportsImages: false, + supportsComputerUse: false, // False because explicitly set to false + supportsPromptCache: false, + inputPrice: undefined, + outputPrice: undefined, + description: "another-custom-model via LiteLLM proxy", + }) + }) + + it("handles fallback detection with various model name formats", async () => { + const mockResponse = { + data: { + data: [ + { + model_name: "vertex-claude", + model_info: { + max_tokens: 4096, + max_input_tokens: 200000, + supports_vision: true, + supports_prompt_caching: false, + }, + litellm_params: { + model: "vertex_ai/claude-3-5-sonnet", // Should match fallback list + }, + }, + { + model_name: "openrouter-claude", + model_info: { + max_tokens: 4096, + max_input_tokens: 200000, + supports_vision: true, + supports_prompt_caching: false, + }, + litellm_params: { + model: "openrouter/anthropic/claude-3.5-sonnet", // Should match fallback list + }, + }, + { + model_name: "bedrock-claude", + model_info: { + max_tokens: 4096, + max_input_tokens: 200000, + supports_vision: true, + supports_prompt_caching: false, + }, + litellm_params: { + model: "anthropic.claude-3-5-sonnet-20241022-v2:0", // Should match fallback list + }, + }, + ], + }, + } + + mockedAxios.get.mockResolvedValue(mockResponse) + + const result = await getLiteLLMModels("test-api-key", "http://localhost:4000") + + expect(result["vertex-claude"].supportsComputerUse).toBe(true) + expect(result["openrouter-claude"].supportsComputerUse).toBe(true) + expect(result["bedrock-claude"].supportsComputerUse).toBe(true) + }) }) diff --git a/src/api/providers/fetchers/litellm.ts b/src/api/providers/fetchers/litellm.ts index dbd6ccc73a..093fd85888 100644 --- a/src/api/providers/fetchers/litellm.ts +++ b/src/api/providers/fetchers/litellm.ts @@ -1,6 +1,6 @@ import axios from "axios" -import { ModelRecord } from "../../../shared/api" +import { LITELLM_COMPUTER_USE_MODELS, ModelRecord } from "../../../shared/api" /** * Fetches available models from a LiteLLM server @@ -23,6 +23,8 @@ export async function getLiteLLMModels(apiKey: string, baseUrl: string): Promise const response = await axios.get(`${baseUrl}/v1/model/info`, { headers, timeout: 5000 }) const models: ModelRecord = {} + const computerModels = Array.from(LITELLM_COMPUTER_USE_MODELS) + // Process the model info from the response if (response.data && response.data.data && Array.isArray(response.data.data)) { for (const model of response.data.data) { @@ -32,12 +34,23 @@ export async function getLiteLLMModels(apiKey: string, baseUrl: string): Promise if (!modelName || !modelInfo || !litellmModelName) continue + // Use explicit supports_computer_use if available, otherwise fall back to hardcoded list + let supportsComputerUse: boolean + if (modelInfo.supports_computer_use !== undefined) { + supportsComputerUse = Boolean(modelInfo.supports_computer_use) + } else { + // Fallback for older LiteLLM versions that don't have supports_computer_use field + supportsComputerUse = computerModels.some((computer_model) => + litellmModelName.endsWith(computer_model), + ) + } + models[modelName] = { maxTokens: modelInfo.max_tokens || 8192, contextWindow: modelInfo.max_input_tokens || 200000, supportsImages: Boolean(modelInfo.supports_vision), // litellm_params.model may have a prefix like openrouter/ - supportsComputerUse: Boolean(modelInfo.supports_computer_use), + supportsComputerUse, supportsPromptCache: Boolean(modelInfo.supports_prompt_caching), inputPrice: modelInfo.input_cost_per_token ? modelInfo.input_cost_per_token * 1000000 : undefined, outputPrice: modelInfo.output_cost_per_token diff --git a/src/shared/api.ts b/src/shared/api.ts index 48c397d1b4..76d7b185df 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -1260,6 +1260,39 @@ export const litellmDefaultModelInfo: ModelInfo = { cacheWritesPrice: 3.75, cacheReadsPrice: 0.3, } + +export const LITELLM_COMPUTER_USE_MODELS = new Set([ + "claude-3-5-sonnet-latest", + "claude-opus-4-20250514", + "claude-sonnet-4-20250514", + "claude-3-7-sonnet-latest", + "claude-3-7-sonnet-20250219", + "claude-3-5-sonnet-20241022", + "vertex_ai/claude-3-5-sonnet", + "vertex_ai/claude-3-5-sonnet-v2", + "vertex_ai/claude-3-5-sonnet-v2@20241022", + "vertex_ai/claude-3-7-sonnet@20250219", + "vertex_ai/claude-opus-4@20250514", + "vertex_ai/claude-sonnet-4@20250514", + "openrouter/anthropic/claude-3.5-sonnet", + "openrouter/anthropic/claude-3.5-sonnet:beta", + "openrouter/anthropic/claude-3.7-sonnet", + "openrouter/anthropic/claude-3.7-sonnet:beta", + "anthropic.claude-opus-4-20250514-v1:0", + "anthropic.claude-sonnet-4-20250514-v1:0", + "anthropic.claude-3-7-sonnet-20250219-v1:0", + "anthropic.claude-3-5-sonnet-20241022-v2:0", + "us.anthropic.claude-3-5-sonnet-20241022-v2:0", + "us.anthropic.claude-3-7-sonnet-20250219-v1:0", + "us.anthropic.claude-opus-4-20250514-v1:0", + "us.anthropic.claude-sonnet-4-20250514-v1:0", + "eu.anthropic.claude-3-5-sonnet-20241022-v2:0", + "eu.anthropic.claude-3-7-sonnet-20250219-v1:0", + "eu.anthropic.claude-opus-4-20250514-v1:0", + "eu.anthropic.claude-sonnet-4-20250514-v1:0", + "snowflake/claude-3-5-sonnet", +]) + // xAI // https://docs.x.ai/docs/api-reference export type XAIModelId = keyof typeof xaiModels From ec486374e790fe45b7b16292bc0306c653b8caf1 Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Tue, 27 May 2025 13:16:00 -0700 Subject: [PATCH 026/104] Try adding a version to @roo-code/types (#4056) --- .changeset/puny-beans-join.md | 5 +++++ packages/types/package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/puny-beans-join.md diff --git a/.changeset/puny-beans-join.md b/.changeset/puny-beans-join.md new file mode 100644 index 0000000000..0b132e8701 --- /dev/null +++ b/.changeset/puny-beans-join.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Add version to @roo-code/types diff --git a/packages/types/package.json b/packages/types/package.json index 3cacff9f61..10557c82a4 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,7 +1,7 @@ { "name": "@roo-code/types", "description": "Roo Code foundational types and schemas.", - "private": true, + "version": "0.0.0", "type": "module", "main": "./dist/index.cjs", "exports": { From e1b2a260caca66d211de10f3d2ecb273e816adda Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 13:20:03 -0700 Subject: [PATCH 027/104] Changeset version bump (#4057) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Chris Estreich --- .changeset/fine-eels-find.md | 5 ----- .changeset/new-shoes-flow.md | 5 ----- .changeset/puny-beans-join.md | 5 ----- .changeset/sour-keys-smash.md | 5 ----- .changeset/thirty-cases-fail.md | 5 ----- CHANGELOG.md | 7 +++++++ src/package.json | 2 +- 7 files changed, 8 insertions(+), 26 deletions(-) delete mode 100644 .changeset/fine-eels-find.md delete mode 100644 .changeset/new-shoes-flow.md delete mode 100644 .changeset/puny-beans-join.md delete mode 100644 .changeset/sour-keys-smash.md delete mode 100644 .changeset/thirty-cases-fail.md diff --git a/.changeset/fine-eels-find.md b/.changeset/fine-eels-find.md deleted file mode 100644 index 169eb8c12c..0000000000 --- a/.changeset/fine-eels-find.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": patch ---- - -Improve the prompt for the "modes" e2e test diff --git a/.changeset/new-shoes-flow.md b/.changeset/new-shoes-flow.md deleted file mode 100644 index 9d68182278..0000000000 --- a/.changeset/new-shoes-flow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": patch ---- - -Add thinking controls for Requesty diff --git a/.changeset/puny-beans-join.md b/.changeset/puny-beans-join.md deleted file mode 100644 index 0b132e8701..0000000000 --- a/.changeset/puny-beans-join.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": patch ---- - -Add version to @roo-code/types diff --git a/.changeset/sour-keys-smash.md b/.changeset/sour-keys-smash.md deleted file mode 100644 index 95519e409c..0000000000 --- a/.changeset/sour-keys-smash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": patch ---- - -v3.18.5 diff --git a/.changeset/thirty-cases-fail.md b/.changeset/thirty-cases-fail.md deleted file mode 100644 index cfa78e2102..0000000000 --- a/.changeset/thirty-cases-fail.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"roo-cline": patch ---- - -Fix Posthog by correctly copying .env in the build process diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a2bf1195e..ac7ec18989 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Roo Code Changelog +## [3.18.5] - 2025-05-27 + +- Add thinking controls for Requesty (thanks @dtrugman!) +- Re-enable telemetry +- Improve zh-TW Traditional Chinese locale (thanks @chrarnoldus) +- Improve model metadata for LiteLLM + ## [3.18.4] - 2025-05-25 - Fix codebase indexing settings saving and Ollama indexing (thanks @daniel-lxs!) diff --git a/src/package.json b/src/package.json index 2ad46c4c13..599ff3abad 100644 --- a/src/package.json +++ b/src/package.json @@ -3,7 +3,7 @@ "displayName": "%extension.displayName%", "description": "%extension.description%", "publisher": "RooVeterinaryInc", - "version": "3.18.4", + "version": "3.18.5", "icon": "assets/icons/icon.png", "galleryBanner": { "color": "#617A91", From 6370131364f40b901fc37394e5b7c8d8af551c2a Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Tue, 27 May 2025 19:21:27 -0700 Subject: [PATCH 028/104] Publish @roo-code/types to npm (#4059) --- packages/types/package.json | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/packages/types/package.json b/packages/types/package.json index 10557c82a4..62eb558b59 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,16 +1,35 @@ { "name": "@roo-code/types", - "description": "Roo Code foundational types and schemas.", - "version": "0.0.0", - "type": "module", + "version": "1.15.0", + "description": "TypeScript type definitions for Roo Code.", + "publishConfig": { + "access": "public" + }, + "author": "Roo Code Team", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/RooCodeInc/Roo-Code.git" + }, + "bugs": { + "url": "https://github.com/RooCodeInc/Roo-Code/issues" + }, + "homepage": "https://github.com/RooCodeInc/Roo-Code/tree/main/packages/types", + "keywords": [ + "roo", + "roo-code", + "ai" + ], "main": "./dist/index.cjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", "exports": { ".": { - "types": "./src/index.ts", - "import": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", "require": { - "types": "./dist/index.d.cts", - "default": "./dist/index.cjs" + "types": "./dist/index.d.ts", + "default": "./dist/index.js" } } }, @@ -22,6 +41,9 @@ "check-types": "tsc --noEmit", "test": "vitest --globals --run", "build": "tsup", + "prepublishOnly": "pnpm run build", + "publish:test": "pnpm publish --dry-run", + "publish": "pnpm publish", "clean": "rimraf dist .turbo" }, "dependencies": { From 59f1d4c529af9660357c1b2558fcd60518a756ae Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Tue, 27 May 2025 20:43:59 -0700 Subject: [PATCH 029/104] Add import tests for @roo-code/types, fix the build (#4060) --- packages/types/package.json | 22 +++-- .../types/src/__tests__/cjs-import.test.ts | 64 ++++++++++++++ .../types/src/__tests__/esm-import.test.ts | 35 ++++++++ .../src/__tests__/package-exports.test.ts | 83 +++++++++++++++++++ packages/types/tsup.config.ts | 5 ++ packages/types/vitest.config.ts | 7 ++ 6 files changed, 209 insertions(+), 7 deletions(-) create mode 100644 packages/types/src/__tests__/cjs-import.test.ts create mode 100644 packages/types/src/__tests__/esm-import.test.ts create mode 100644 packages/types/src/__tests__/package-exports.test.ts create mode 100644 packages/types/vitest.config.ts diff --git a/packages/types/package.json b/packages/types/package.json index 62eb558b59..e1accd1282 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,9 +1,19 @@ { "name": "@roo-code/types", - "version": "1.15.0", + "version": "1.16.0", "description": "TypeScript type definitions for Roo Code.", "publishConfig": { - "access": "public" + "access": "public", + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.js" + } + } }, "author": "Roo Code Team", "license": "MIT", @@ -20,13 +30,11 @@ "roo-code", "ai" ], - "main": "./dist/index.cjs", - "module": "./dist/index.mjs", - "types": "./dist/index.d.ts", + "main": "./dist/index.js", "exports": { ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.mjs", + "types": "./src/index.ts", + "import": "./src/index.ts", "require": { "types": "./dist/index.d.ts", "default": "./dist/index.js" diff --git a/packages/types/src/__tests__/cjs-import.test.ts b/packages/types/src/__tests__/cjs-import.test.ts new file mode 100644 index 0000000000..09ca107a44 --- /dev/null +++ b/packages/types/src/__tests__/cjs-import.test.ts @@ -0,0 +1,64 @@ +// npx vitest run src/__tests__/cjs-import.test.ts + +import { resolve } from "path" + +describe("CommonJS Import Tests", () => { + const packageRoot = resolve(__dirname, "../..") + const cjsPath = resolve(packageRoot, "dist", "index.js") + + it("should import types using require() syntax", () => { + // Clear require cache to ensure fresh import. + delete require.cache[cjsPath] + + // Use require to test CJS functionality. + // eslint-disable-next-line @typescript-eslint/no-require-imports + const module = require(cjsPath) + + // Verify that key exports are available + expect(module.GLOBAL_STATE_KEYS).toBeDefined() + expect(Array.isArray(module.GLOBAL_STATE_KEYS)).toBe(true) + expect(module.GLOBAL_STATE_KEYS.length).toBeGreaterThan(0) + }) + + it("should import specific exports using destructuring", () => { + // Clear require cache. + delete require.cache[cjsPath] + + // Test destructured require. + // eslint-disable-next-line @typescript-eslint/no-require-imports + const { GLOBAL_STATE_KEYS, SECRET_STATE_KEYS } = require(cjsPath) + + expect(GLOBAL_STATE_KEYS).toBeDefined() + expect(SECRET_STATE_KEYS).toBeDefined() + expect(Array.isArray(GLOBAL_STATE_KEYS)).toBe(true) + expect(Array.isArray(SECRET_STATE_KEYS)).toBe(true) + }) + + it("should have default export available", () => { + // Clear require cache + delete require.cache[cjsPath] + + // eslint-disable-next-line @typescript-eslint/no-require-imports + const module = require(cjsPath) + + // Check if module has expected structure + expect(typeof module).toBe("object") + expect(module).not.toBeNull() + }) + + it("should maintain consistency between multiple require calls", () => { + // Clear require cache first. + delete require.cache[cjsPath] + + // Multiple require calls should return the same cached module. + // eslint-disable-next-line @typescript-eslint/no-require-imports + const firstRequire = require(cjsPath) + + // eslint-disable-next-line @typescript-eslint/no-require-imports + const secondRequire = require(cjsPath) + + // Should be the exact same object (cached). + expect(firstRequire).toBe(secondRequire) + expect(firstRequire.GLOBAL_STATE_KEYS).toBe(secondRequire.GLOBAL_STATE_KEYS) + }) +}) diff --git a/packages/types/src/__tests__/esm-import.test.ts b/packages/types/src/__tests__/esm-import.test.ts new file mode 100644 index 0000000000..786ae62e8c --- /dev/null +++ b/packages/types/src/__tests__/esm-import.test.ts @@ -0,0 +1,35 @@ +// npx vitest run src/__tests__/esm-import.test.ts + +describe("ESM Import Tests", () => { + it("should import types using ESM syntax", async () => { + // Dynamic import to test ESM functionality. + const module = await import("../index.js") + + // Verify that key exports are available. + expect(module.GLOBAL_STATE_KEYS).toBeDefined() + expect(Array.isArray(module.GLOBAL_STATE_KEYS)).toBe(true) + expect(module.GLOBAL_STATE_KEYS.length).toBeGreaterThan(0) + }) + + it("should import specific exports using ESM syntax", async () => { + // Test named imports. + const { GLOBAL_STATE_KEYS, SECRET_STATE_KEYS } = await import("../index.js") + + expect(GLOBAL_STATE_KEYS).toBeDefined() + expect(SECRET_STATE_KEYS).toBeDefined() + expect(Array.isArray(GLOBAL_STATE_KEYS)).toBe(true) + expect(Array.isArray(SECRET_STATE_KEYS)).toBe(true) + }) + + it("should have consistent exports between static and dynamic imports", async () => { + // Static import. + const staticImport = await import("../index.js") + + // Dynamic import. + const dynamicImport = await import("../index.js") + + // Both should have the same exports. + expect(Object.keys(staticImport)).toEqual(Object.keys(dynamicImport)) + expect(staticImport.GLOBAL_STATE_KEYS).toEqual(dynamicImport.GLOBAL_STATE_KEYS) + }) +}) diff --git a/packages/types/src/__tests__/package-exports.test.ts b/packages/types/src/__tests__/package-exports.test.ts new file mode 100644 index 0000000000..d6c09d42dd --- /dev/null +++ b/packages/types/src/__tests__/package-exports.test.ts @@ -0,0 +1,83 @@ +// npx vitest run src/__tests__/package-exports.test.ts + +import { resolve } from "path" + +describe("Package Exports Integration Tests", () => { + const packageRoot = resolve(__dirname, "../..") + const distPath = resolve(packageRoot, "dist") + + it("should import from built ESM file", async () => { + const esmPath = resolve(distPath, "index.mjs") + + // Dynamic import of the built ESM file + const module = await import(esmPath) + + expect(module.GLOBAL_STATE_KEYS).toBeDefined() + expect(Array.isArray(module.GLOBAL_STATE_KEYS)).toBe(true) + expect(module.GLOBAL_STATE_KEYS.length).toBeGreaterThan(0) + }) + + it("should import from built CJS file", () => { + const cjsPath = resolve(distPath, "index.js") + + // Clear require cache to ensure fresh import + delete require.cache[cjsPath] + + // Require the built CJS file + // eslint-disable-next-line @typescript-eslint/no-require-imports + const module = require(cjsPath) + + expect(module.GLOBAL_STATE_KEYS).toBeDefined() + expect(Array.isArray(module.GLOBAL_STATE_KEYS)).toBe(true) + expect(module.GLOBAL_STATE_KEYS.length).toBeGreaterThan(0) + }) + + it("should have consistent exports between ESM and CJS builds", async () => { + const esmPath = resolve(distPath, "index.mjs") + const cjsPath = resolve(distPath, "index.js") + + // Clear require cache. + delete require.cache[cjsPath] + + // Import both versions. + const esmModule = await import(esmPath) + // eslint-disable-next-line @typescript-eslint/no-require-imports + const cjsModule = require(cjsPath) + + // Compare key exports. + expect(esmModule.GLOBAL_STATE_KEYS).toEqual(cjsModule.GLOBAL_STATE_KEYS) + expect(esmModule.SECRET_STATE_KEYS).toEqual(cjsModule.SECRET_STATE_KEYS) + + // Ensure both have the same export keys. + const esmKeys = Object.keys(esmModule).sort() + const cjsKeys = Object.keys(cjsModule).sort() + expect(esmKeys).toEqual(cjsKeys) + }) + + it("should import using package name resolution (simulated)", async () => { + // This simulates how the package would be imported by consumers. + // We test the source files since we can't easily test the published package. + const module = await import("../index.js") + + // Verify the main exports that consumers would use. + expect(module.GLOBAL_STATE_KEYS).toBeDefined() + expect(module.SECRET_STATE_KEYS).toBeDefined() + + // Test some common type exports exist. + expect(typeof module.GLOBAL_STATE_KEYS).toBe("object") + expect(typeof module.SECRET_STATE_KEYS).toBe("object") + }) + + it("should have TypeScript definitions available", () => { + const dtsPath = resolve(distPath, "index.d.ts") + // eslint-disable-next-line @typescript-eslint/no-require-imports + const fs = require("fs") + + // Check that the .d.ts file exists and has content. + expect(fs.existsSync(dtsPath)).toBe(true) + + const dtsContent = fs.readFileSync(dtsPath, "utf8") + expect(dtsContent.length).toBeGreaterThan(0) + expect(dtsContent).toContain("export") + }) +}) diff --git a/packages/types/tsup.config.ts b/packages/types/tsup.config.ts index 9c96eb1901..fccbcc170a 100644 --- a/packages/types/tsup.config.ts +++ b/packages/types/tsup.config.ts @@ -8,4 +8,9 @@ export default defineConfig({ splitting: false, sourcemap: true, outDir: "dist", + outExtension({ format }) { + return { + js: format === "cjs" ? ".js" : ".mjs", + } + }, }) diff --git a/packages/types/vitest.config.ts b/packages/types/vitest.config.ts new file mode 100644 index 0000000000..aa04bc59b7 --- /dev/null +++ b/packages/types/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "vitest/config" + +export default defineConfig({ + test: { + globals: true, + }, +}) From 39d0f6865921289afba5a36f080336f4f96eef65 Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Tue, 27 May 2025 22:10:41 -0700 Subject: [PATCH 030/104] Improve configuration for npm publish of @roo-code/types (#4062) --- .github/workflows/code-qa.yml | 4 +- MONOREPO.md | 15 +++- packages/types/{ => npm}/README.md | 0 packages/types/npm/package.json | 40 +++++++++ packages/types/package.json | 48 ++--------- .../types/src/__tests__/cjs-import.test.ts | 64 -------------- .../types/src/__tests__/esm-import.test.ts | 35 -------- packages/types/src/__tests__/index.test.ts | 2 +- .../src/__tests__/package-exports.test.ts | 83 ------------------- packages/types/tsup.config.ts | 5 -- 10 files changed, 65 insertions(+), 231 deletions(-) rename packages/types/{ => npm}/README.md (100%) create mode 100644 packages/types/npm/package.json delete mode 100644 packages/types/src/__tests__/cjs-import.test.ts delete mode 100644 packages/types/src/__tests__/esm-import.test.ts delete mode 100644 packages/types/src/__tests__/package-exports.test.ts diff --git a/.github/workflows/code-qa.yml b/.github/workflows/code-qa.yml index 0fbd581fe7..271ecc1f28 100644 --- a/.github/workflows/code-qa.yml +++ b/.github/workflows/code-qa.yml @@ -134,8 +134,8 @@ jobs: working-directory: apps/vscode-e2e run: xvfb-run -a pnpm test:ci - unit-test: - needs: [platform-unit-test] # [platform-unit-test, integration-test] + qa: + needs: [check-translations, knip, compile, platform-unit-test, integration-test] runs-on: ubuntu-latest steps: - name: NO-OP diff --git a/MONOREPO.md b/MONOREPO.md index 65c21d8b1e..f436b116eb 100644 --- a/MONOREPO.md +++ b/MONOREPO.md @@ -24,6 +24,19 @@ pnpm install If things are in good working order then you should be able to build a vsix and install it in VSCode: ```sh -pnpm build --out ../bin/roo-code-main.vsix && \ +pnpm build -- --out ../bin/roo-code-main.vsix && \ code --install-extension bin/roo-code-main.vsix ``` + +To fully stress the monorepo setup, run the following: + +```sh +pnpm clean && pnpm lint +pnpm clean && pnpm check-types +pnpm clean && pnpm test +pnpm clean && pnpm bundle +pnpm clean && pnpm build +pnpm clean && pnpm npx turbo watch:bundle +pnpm clean && pnpm npx turbo watch:tsc +cd apps/vscode-e2e && pnpm test:ci +``` diff --git a/packages/types/README.md b/packages/types/npm/README.md similarity index 100% rename from packages/types/README.md rename to packages/types/npm/README.md diff --git a/packages/types/npm/package.json b/packages/types/npm/package.json new file mode 100644 index 0000000000..a1e46de317 --- /dev/null +++ b/packages/types/npm/package.json @@ -0,0 +1,40 @@ +{ + "name": "@roo-code/types", + "version": "1.19.0", + "description": "TypeScript type definitions for Roo Code.", + "publishConfig": { + "access": "public", + "name": "@roo-code/types" + }, + "author": "Roo Code Team", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/RooCodeInc/Roo-Code.git" + }, + "bugs": { + "url": "https://github.com/RooCodeInc/Roo-Code/issues" + }, + "homepage": "https://github.com/RooCodeInc/Roo-Code/tree/main/packages/types", + "keywords": [ + "roo", + "roo-code", + "ai" + ], + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + } + }, + "files": [ + "dist" + ] +} diff --git a/packages/types/package.json b/packages/types/package.json index e1accd1282..db05e3921b 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,58 +1,26 @@ { "name": "@roo-code/types", - "version": "1.16.0", - "description": "TypeScript type definitions for Roo Code.", - "publishConfig": { - "access": "public", - "main": "./dist/index.js", - "module": "./dist/index.mjs", - "types": "./dist/index.d.ts", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.mjs", - "require": "./dist/index.js" - } - } - }, - "author": "Roo Code Team", - "license": "MIT", - "repository": { - "type": "git", - "url": "git+https://github.com/RooCodeInc/Roo-Code.git" - }, - "bugs": { - "url": "https://github.com/RooCodeInc/Roo-Code/issues" - }, - "homepage": "https://github.com/RooCodeInc/Roo-Code/tree/main/packages/types", - "keywords": [ - "roo", - "roo-code", - "ai" - ], - "main": "./dist/index.js", + "version": "0.0.0", + "type": "module", + "main": "./dist/index.cjs", "exports": { ".": { "types": "./src/index.ts", "import": "./src/index.ts", "require": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" } } }, - "files": [ - "dist" - ], "scripts": { "lint": "eslint src --ext=ts --max-warnings=0", "check-types": "tsc --noEmit", "test": "vitest --globals --run", "build": "tsup", - "prepublishOnly": "pnpm run build", - "publish:test": "pnpm publish --dry-run", - "publish": "pnpm publish", - "clean": "rimraf dist .turbo" + "npm:publish:test": "tsup --outDir npm/dist && cd npm && npm publish --dry-run", + "npm:publish": "tsup --outDir npm/dist && cd npm && npm publish", + "clean": "rimraf dist npm/dist .turbo" }, "dependencies": { "zod": "^3.24.2" diff --git a/packages/types/src/__tests__/cjs-import.test.ts b/packages/types/src/__tests__/cjs-import.test.ts deleted file mode 100644 index 09ca107a44..0000000000 --- a/packages/types/src/__tests__/cjs-import.test.ts +++ /dev/null @@ -1,64 +0,0 @@ -// npx vitest run src/__tests__/cjs-import.test.ts - -import { resolve } from "path" - -describe("CommonJS Import Tests", () => { - const packageRoot = resolve(__dirname, "../..") - const cjsPath = resolve(packageRoot, "dist", "index.js") - - it("should import types using require() syntax", () => { - // Clear require cache to ensure fresh import. - delete require.cache[cjsPath] - - // Use require to test CJS functionality. - // eslint-disable-next-line @typescript-eslint/no-require-imports - const module = require(cjsPath) - - // Verify that key exports are available - expect(module.GLOBAL_STATE_KEYS).toBeDefined() - expect(Array.isArray(module.GLOBAL_STATE_KEYS)).toBe(true) - expect(module.GLOBAL_STATE_KEYS.length).toBeGreaterThan(0) - }) - - it("should import specific exports using destructuring", () => { - // Clear require cache. - delete require.cache[cjsPath] - - // Test destructured require. - // eslint-disable-next-line @typescript-eslint/no-require-imports - const { GLOBAL_STATE_KEYS, SECRET_STATE_KEYS } = require(cjsPath) - - expect(GLOBAL_STATE_KEYS).toBeDefined() - expect(SECRET_STATE_KEYS).toBeDefined() - expect(Array.isArray(GLOBAL_STATE_KEYS)).toBe(true) - expect(Array.isArray(SECRET_STATE_KEYS)).toBe(true) - }) - - it("should have default export available", () => { - // Clear require cache - delete require.cache[cjsPath] - - // eslint-disable-next-line @typescript-eslint/no-require-imports - const module = require(cjsPath) - - // Check if module has expected structure - expect(typeof module).toBe("object") - expect(module).not.toBeNull() - }) - - it("should maintain consistency between multiple require calls", () => { - // Clear require cache first. - delete require.cache[cjsPath] - - // Multiple require calls should return the same cached module. - // eslint-disable-next-line @typescript-eslint/no-require-imports - const firstRequire = require(cjsPath) - - // eslint-disable-next-line @typescript-eslint/no-require-imports - const secondRequire = require(cjsPath) - - // Should be the exact same object (cached). - expect(firstRequire).toBe(secondRequire) - expect(firstRequire.GLOBAL_STATE_KEYS).toBe(secondRequire.GLOBAL_STATE_KEYS) - }) -}) diff --git a/packages/types/src/__tests__/esm-import.test.ts b/packages/types/src/__tests__/esm-import.test.ts deleted file mode 100644 index 786ae62e8c..0000000000 --- a/packages/types/src/__tests__/esm-import.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -// npx vitest run src/__tests__/esm-import.test.ts - -describe("ESM Import Tests", () => { - it("should import types using ESM syntax", async () => { - // Dynamic import to test ESM functionality. - const module = await import("../index.js") - - // Verify that key exports are available. - expect(module.GLOBAL_STATE_KEYS).toBeDefined() - expect(Array.isArray(module.GLOBAL_STATE_KEYS)).toBe(true) - expect(module.GLOBAL_STATE_KEYS.length).toBeGreaterThan(0) - }) - - it("should import specific exports using ESM syntax", async () => { - // Test named imports. - const { GLOBAL_STATE_KEYS, SECRET_STATE_KEYS } = await import("../index.js") - - expect(GLOBAL_STATE_KEYS).toBeDefined() - expect(SECRET_STATE_KEYS).toBeDefined() - expect(Array.isArray(GLOBAL_STATE_KEYS)).toBe(true) - expect(Array.isArray(SECRET_STATE_KEYS)).toBe(true) - }) - - it("should have consistent exports between static and dynamic imports", async () => { - // Static import. - const staticImport = await import("../index.js") - - // Dynamic import. - const dynamicImport = await import("../index.js") - - // Both should have the same exports. - expect(Object.keys(staticImport)).toEqual(Object.keys(dynamicImport)) - expect(staticImport.GLOBAL_STATE_KEYS).toEqual(dynamicImport.GLOBAL_STATE_KEYS) - }) -}) diff --git a/packages/types/src/__tests__/index.test.ts b/packages/types/src/__tests__/index.test.ts index fd1e9e1c88..c3df37fa97 100644 --- a/packages/types/src/__tests__/index.test.ts +++ b/packages/types/src/__tests__/index.test.ts @@ -1,4 +1,4 @@ -// npx vitest run --globals src/__tests__/index.test.ts +// npx vitest run src/__tests__/index.test.ts import { GLOBAL_STATE_KEYS } from "../index.js" diff --git a/packages/types/src/__tests__/package-exports.test.ts b/packages/types/src/__tests__/package-exports.test.ts deleted file mode 100644 index d6c09d42dd..0000000000 --- a/packages/types/src/__tests__/package-exports.test.ts +++ /dev/null @@ -1,83 +0,0 @@ -// npx vitest run src/__tests__/package-exports.test.ts - -import { resolve } from "path" - -describe("Package Exports Integration Tests", () => { - const packageRoot = resolve(__dirname, "../..") - const distPath = resolve(packageRoot, "dist") - - it("should import from built ESM file", async () => { - const esmPath = resolve(distPath, "index.mjs") - - // Dynamic import of the built ESM file - const module = await import(esmPath) - - expect(module.GLOBAL_STATE_KEYS).toBeDefined() - expect(Array.isArray(module.GLOBAL_STATE_KEYS)).toBe(true) - expect(module.GLOBAL_STATE_KEYS.length).toBeGreaterThan(0) - }) - - it("should import from built CJS file", () => { - const cjsPath = resolve(distPath, "index.js") - - // Clear require cache to ensure fresh import - delete require.cache[cjsPath] - - // Require the built CJS file - // eslint-disable-next-line @typescript-eslint/no-require-imports - const module = require(cjsPath) - - expect(module.GLOBAL_STATE_KEYS).toBeDefined() - expect(Array.isArray(module.GLOBAL_STATE_KEYS)).toBe(true) - expect(module.GLOBAL_STATE_KEYS.length).toBeGreaterThan(0) - }) - - it("should have consistent exports between ESM and CJS builds", async () => { - const esmPath = resolve(distPath, "index.mjs") - const cjsPath = resolve(distPath, "index.js") - - // Clear require cache. - delete require.cache[cjsPath] - - // Import both versions. - const esmModule = await import(esmPath) - // eslint-disable-next-line @typescript-eslint/no-require-imports - const cjsModule = require(cjsPath) - - // Compare key exports. - expect(esmModule.GLOBAL_STATE_KEYS).toEqual(cjsModule.GLOBAL_STATE_KEYS) - expect(esmModule.SECRET_STATE_KEYS).toEqual(cjsModule.SECRET_STATE_KEYS) - - // Ensure both have the same export keys. - const esmKeys = Object.keys(esmModule).sort() - const cjsKeys = Object.keys(cjsModule).sort() - expect(esmKeys).toEqual(cjsKeys) - }) - - it("should import using package name resolution (simulated)", async () => { - // This simulates how the package would be imported by consumers. - // We test the source files since we can't easily test the published package. - const module = await import("../index.js") - - // Verify the main exports that consumers would use. - expect(module.GLOBAL_STATE_KEYS).toBeDefined() - expect(module.SECRET_STATE_KEYS).toBeDefined() - - // Test some common type exports exist. - expect(typeof module.GLOBAL_STATE_KEYS).toBe("object") - expect(typeof module.SECRET_STATE_KEYS).toBe("object") - }) - - it("should have TypeScript definitions available", () => { - const dtsPath = resolve(distPath, "index.d.ts") - // eslint-disable-next-line @typescript-eslint/no-require-imports - const fs = require("fs") - - // Check that the .d.ts file exists and has content. - expect(fs.existsSync(dtsPath)).toBe(true) - - const dtsContent = fs.readFileSync(dtsPath, "utf8") - expect(dtsContent.length).toBeGreaterThan(0) - expect(dtsContent).toContain("export") - }) -}) diff --git a/packages/types/tsup.config.ts b/packages/types/tsup.config.ts index fccbcc170a..9c96eb1901 100644 --- a/packages/types/tsup.config.ts +++ b/packages/types/tsup.config.ts @@ -8,9 +8,4 @@ export default defineConfig({ splitting: false, sourcemap: true, outDir: "dist", - outExtension({ format }) { - return { - js: format === "cjs" ? ".js" : ".mjs", - } - }, }) From 3bfe2a38731e4d2ea60214cd2c0d0f0b12bf0c6f Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Wed, 28 May 2025 11:24:06 -0400 Subject: [PATCH 031/104] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac7ec18989..4a690d9622 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ - Add thinking controls for Requesty (thanks @dtrugman!) - Re-enable telemetry -- Improve zh-TW Traditional Chinese locale (thanks @chrarnoldus) +- Improve zh-TW Traditional Chinese locale (thanks @PeterDaveHello and @chrarnoldus!) - Improve model metadata for LiteLLM ## [3.18.4] - 2025-05-25 From 13658004741b7b35de49512ae6bbca75c6a318df Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Wed, 28 May 2025 11:55:00 -0400 Subject: [PATCH 032/104] Fix centering of the about text (#4080) --- webview-ui/src/components/chat/ChatView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index 6eaceb1374..7ebdc468ce 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -1321,7 +1321,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction} {/* Show the task history preview if expanded and tasks exist */} {taskHistory.length > 0 && isExpanded && } -

+

Date: Thu, 29 May 2025 00:10:40 +0700 Subject: [PATCH 033/104] feat(McpHub): inject env vars on whole mcp config (#3970) * types(utils/config): improve return type infer * feat(McpHub): inject env variable on whole mcp config * fix: check for undefined configInjected * refactor: improve type assertion --------- Co-authored-by: Daniel <57051444+daniel-lxs@users.noreply.github.com> --- src/services/mcp/McpHub.ts | 23 +++++++++++++---------- src/utils/config.ts | 6 +++--- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/services/mcp/McpHub.ts b/src/services/mcp/McpHub.ts index a77fc8013f..1e8aabf1f5 100644 --- a/src/services/mcp/McpHub.ts +++ b/src/services/mcp/McpHub.ts @@ -443,13 +443,16 @@ export class McpHub { let transport: StdioClientTransport | SSEClientTransport - if (config.type === "stdio") { + // Inject environment variables to the config + const configInjected = (await injectEnv(config)) as typeof config + + if (configInjected.type === "stdio") { transport = new StdioClientTransport({ - command: config.command, - args: config.args, - cwd: config.cwd, + command: configInjected.command, + args: configInjected.args, + cwd: configInjected.cwd, env: { - ...(config.env ? await injectEnv(config.env) : {}), + ...(configInjected.env || {}), ...(process.env.PATH ? { PATH: process.env.PATH } : {}), ...(process.env.HOME ? { HOME: process.env.HOME } : {}), }, @@ -508,16 +511,16 @@ export class McpHub { // SSE connection const sseOptions = { requestInit: { - headers: config.headers, + headers: configInjected.headers, }, } // Configure ReconnectingEventSource options const reconnectingEventSourceOptions = { max_retry_time: 5000, // Maximum retry time in milliseconds - withCredentials: config.headers?.["Authorization"] ? true : false, // Enable credentials if Authorization header exists + withCredentials: configInjected.headers?.["Authorization"] ? true : false, // Enable credentials if Authorization header exists } global.EventSource = ReconnectingEventSource - transport = new SSEClientTransport(new URL(config.url), { + transport = new SSEClientTransport(new URL(configInjected.url), { ...sseOptions, eventSourceInit: reconnectingEventSourceOptions, }) @@ -537,9 +540,9 @@ export class McpHub { const connection: McpConnection = { server: { name, - config: JSON.stringify(config), + config: JSON.stringify(configInjected), status: "connecting", - disabled: config.disabled, + disabled: configInjected.disabled, source, projectPath: source === "project" ? vscode.workspace.workspaceFolders?.[0]?.uri.fsPath : undefined, errorHistory: [], diff --git a/src/utils/config.ts b/src/utils/config.ts index da746be204..50dc7d6386 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -5,11 +5,11 @@ * * Does not mutate original object */ -export async function injectEnv(config: string | Record, notFoundValue: any = "") { +export async function injectEnv>(config: C, 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 + let _config: string = isObject ? JSON.stringify(config) : config _config = _config.replace(/\$\{env:([\w]+)\}/g, (_, name) => { // Check if null or undefined @@ -21,5 +21,5 @@ export async function injectEnv(config: string | Record, notFo return process.env[name] ?? notFoundValue }) - return isObject ? JSON.parse(_config) : _config + return (isObject ? JSON.parse(_config) : _config) as C extends string ? string : C } From acd51c5120d7c5147d37b2b0f578d8fcebd36b15 Mon Sep 17 00:00:00 2001 From: Sam Hoang Van Date: Thu, 29 May 2025 00:11:38 +0700 Subject: [PATCH 034/104] fix(webview): resolve memory leak in ChatView by stabilizing callback props (#3926) * fix(webview): resolve memory leak in ChatView by stabilizing callback props - Stabilize handleSendMessage using clineAskRef to prevent frequent re-creation - Stabilize toggleRowExpansion by extracting handleSetExpandedRow and managing dependencies - Re-integrate scrolling logic into useEffect hook to avoid destabilizing callbacks - Add everVisibleMessagesTsRef to reduce unnecessary ChatRow remounts by Virtuoso - Update onToggleExpand signature to accept timestamp parameter for better stability - Remove diagnostic console.log statements used for debugging callback changes These changes address detached DOM elements memory leak caused by frequent callback re-creation triggering unnecessary component re-renders and preventing proper garbage collection of chat message DOM nodes. * comment correct TTL --------- Co-authored-by: Daniel <57051444+daniel-lxs@users.noreply.github.com> --- pnpm-lock.yaml | 3 + webview-ui/package.json | 3 +- webview-ui/src/components/chat/ChatRow.tsx | 37 ++-- webview-ui/src/components/chat/ChatView.tsx | 214 ++++++++++++-------- 4 files changed, 151 insertions(+), 106 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d7fc259cab..9340d50d2f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -577,6 +577,9 @@ importers: knuth-shuffle-seeded: specifier: ^1.0.6 version: 1.0.6 + lru-cache: + specifier: ^11.1.0 + version: 11.1.0 lucide-react: specifier: ^0.510.0 version: 0.510.0(react@18.3.1) diff --git a/webview-ui/package.json b/webview-ui/package.json index 269b8e6e49..9a31a423f0 100644 --- a/webview-ui/package.json +++ b/webview-ui/package.json @@ -47,6 +47,7 @@ "i18next": "^24.2.2", "i18next-http-backend": "^3.0.2", "knuth-shuffle-seeded": "^1.0.6", + "lru-cache": "^11.1.0", "lucide-react": "^0.510.0", "mermaid": "^11.4.1", "posthog-js": "^1.227.2", @@ -70,8 +71,8 @@ "tailwindcss": "^4.0.0", "tailwindcss-animate": "^1.0.7", "unist-util-visit": "^5.0.0", - "vscode-material-icons": "^0.1.1", "use-sound": "^5.0.0", + "vscode-material-icons": "^0.1.1", "vscrui": "^0.2.2", "zod": "^3.24.2" }, diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index e6b8bb601a..eaabb77e70 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -1,4 +1,4 @@ -import React, { memo, useEffect, useMemo, useRef, useState } from "react" +import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from "react" import { useSize } from "react-use" import { useTranslation, Trans } from "react-i18next" import deepEqual from "fast-deep-equal" @@ -44,7 +44,7 @@ interface ChatRowProps { isExpanded: boolean isLast: boolean isStreaming: boolean - onToggleExpand: () => void + onToggleExpand: (ts: number) => void onHeightChange: (isTaller: boolean) => void onSuggestionClick?: (answer: string, event?: React.MouseEvent) => void } @@ -103,6 +103,11 @@ export const ChatRowContent = ({ const [showCopySuccess, setShowCopySuccess] = useState(false) const { copyWithFeedback } = useCopyToClipboard() + // Memoized callback to prevent re-renders caused by inline arrow functions + const handleToggleExpand = useCallback(() => { + onToggleExpand(message.ts) + }, [onToggleExpand, message.ts]) + const [cost, apiReqCancelReason, apiReqStreamingFailedMessage] = useMemo(() => { if (message.text !== null && message.text !== undefined && message.say === "api_req_started") { const info = safeJsonParse(message.text) @@ -302,7 +307,7 @@ export const ChatRowContent = ({ progressStatus={message.progressStatus} isLoading={message.partial} isExpanded={isExpanded} - onToggleExpand={onToggleExpand} + onToggleExpand={handleToggleExpand} /> ) @@ -328,7 +333,7 @@ export const ChatRowContent = ({ progressStatus={message.progressStatus} isLoading={message.partial} isExpanded={isExpanded} - onToggleExpand={onToggleExpand} + onToggleExpand={handleToggleExpand} /> ) @@ -350,7 +355,7 @@ export const ChatRowContent = ({ progressStatus={message.progressStatus} isLoading={message.partial} isExpanded={isExpanded} - onToggleExpand={onToggleExpand} + onToggleExpand={handleToggleExpand} /> ) @@ -389,7 +394,7 @@ export const ChatRowContent = ({ language={getLanguageFromPath(tool.path || "") || "log"} isLoading={message.partial} isExpanded={isExpanded} - onToggleExpand={onToggleExpand} + onToggleExpand={handleToggleExpand} /> ) @@ -435,7 +440,7 @@ export const ChatRowContent = ({ language="markdown" isLoading={message.partial} isExpanded={isExpanded} - onToggleExpand={onToggleExpand} + onToggleExpand={handleToggleExpand} /> ) @@ -455,7 +460,7 @@ export const ChatRowContent = ({ code={tool.content} language="shell-session" isExpanded={isExpanded} - onToggleExpand={onToggleExpand} + onToggleExpand={handleToggleExpand} /> ) @@ -475,7 +480,7 @@ export const ChatRowContent = ({ code={tool.content} language="shellsession" isExpanded={isExpanded} - onToggleExpand={onToggleExpand} + onToggleExpand={handleToggleExpand} /> ) @@ -495,7 +500,7 @@ export const ChatRowContent = ({ code={tool.content} language="markdown" isExpanded={isExpanded} - onToggleExpand={onToggleExpand} + onToggleExpand={handleToggleExpand} /> ) @@ -525,7 +530,7 @@ export const ChatRowContent = ({ code={tool.content} language="shellsession" isExpanded={isExpanded} - onToggleExpand={onToggleExpand} + onToggleExpand={handleToggleExpand} /> ) @@ -813,7 +818,7 @@ export const ChatRowContent = ({ MozUserSelect: "none", msUserSelect: "none", }} - onClick={onToggleExpand}> + onClick={handleToggleExpand}>

{icon} {title} @@ -852,7 +857,7 @@ export const ChatRowContent = ({ code={safeJsonParse(message.text)?.request} language="markdown" isExpanded={true} - onToggleExpand={onToggleExpand} + onToggleExpand={handleToggleExpand} />
)} @@ -898,7 +903,7 @@ export const ChatRowContent = ({ language="diff" isFeedback={true} isExpanded={isExpanded} - onToggleExpand={onToggleExpand} + onToggleExpand={handleToggleExpand} /> ) @@ -945,7 +950,7 @@ export const ChatRowContent = ({ code={message.text} language="json" isExpanded={true} - onToggleExpand={onToggleExpand} + onToggleExpand={handleToggleExpand} /> @@ -1105,7 +1110,7 @@ export const ChatRowContent = ({ code={useMcpServer.arguments} language="json" isExpanded={true} - onToggleExpand={onToggleExpand} + onToggleExpand={handleToggleExpand} /> )} diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index 7ebdc468ce..ef80bf18cb 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -38,6 +38,7 @@ import TaskHeader from "./TaskHeader" import AutoApproveMenu from "./AutoApproveMenu" import SystemPromptWarning from "./SystemPromptWarning" import { CheckpointWarning } from "./CheckpointWarning" +import { LRUCache } from "lru-cache" export interface ChatViewProps { isHidden: boolean @@ -91,6 +92,11 @@ const ChatViewComponent: React.ForwardRefRenderFunction { + messagesRef.current = messages + }, [messages]) + const { tasks } = useTaskSearch() // Initialize expanded state based on the persisted setting (default to expanded if undefined) @@ -128,6 +134,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction(null) const [expandedRows, setExpandedRows] = useState>({}) + const prevExpandedRowsRef = useRef>() const scrollContainerRef = useRef(null) const disableAutoScrollRef = useRef(false) const [showScrollToBottom, setShowScrollToBottom] = useState(false) @@ -136,6 +143,17 @@ const ChatViewComponent: React.ForwardRefRenderFunction(false) const [showCheckpointWarning, setShowCheckpointWarning] = useState(false) const [isCondensing, setIsCondensing] = useState(false) + const everVisibleMessagesTsRef = useRef>( + new LRUCache({ + max: 250, + ttl: 1000 * 60 * 15, // 15 minutes TTL for long-running tasks + }), + ) + + const clineAskRef = useRef(clineAsk) + useEffect(() => { + clineAskRef.current = clineAsk + }, [clineAsk]) // UI layout depends on the last 2 messages // (since it relies on the content of these messages, we are deep comparing. i.e. the button state after hitting button sets enableButtons to false, and this effect otherwise would have to true again even if messages didn't change @@ -367,7 +385,32 @@ const ChatViewComponent: React.ForwardRefRenderFunction setExpandedRows({}), [task?.ts]) + useEffect(() => { + setExpandedRows({}) + everVisibleMessagesTsRef.current.clear() // Clear for new task + }, [task?.ts]) + + useEffect(() => () => everVisibleMessagesTsRef.current.clear(), []) + + useEffect(() => { + const prev = prevExpandedRowsRef.current + let wasAnyRowExpandedByUser = false + if (prev) { + // Check if any row transitioned from false/undefined to true + for (const [tsKey, isExpanded] of Object.entries(expandedRows)) { + const ts = Number(tsKey) + if (isExpanded && !(prev[ts] ?? false)) { + wasAnyRowExpandedByUser = true + break + } + } + } + + if (wasAnyRowExpandedByUser) { + disableAutoScrollRef.current = true + } + prevExpandedRowsRef.current = expandedRows // Store current state for next comparison + }, [expandedRows]) const isStreaming = useMemo(() => { // Checking clineAsk isn't enough since messages effect may be called @@ -428,10 +471,13 @@ const ChatViewComponent: React.ForwardRefRenderFunction 0) { - if (messages.length === 0) { + if (messagesRef.current.length === 0) { vscode.postMessage({ type: "newTask", text, images }) - } else if (clineAsk) { - switch (clineAsk) { + } else if (clineAskRef.current) { + // Use clineAskRef.current + switch ( + clineAskRef.current // Use clineAskRef.current + ) { case "followup": case "tool": case "browser_action_launch": @@ -451,7 +497,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction { - return modifiedMessages.filter((message) => { + const newVisibleMessages = modifiedMessages.filter((message) => { + if (everVisibleMessagesTsRef.current.has(message.ts)) { + // If it was ever visible, and it's not one of the types that should always be hidden once processed, keep it. + // This helps prevent flickering for messages like 'api_req_retry_delayed' if they are no longer the absolute last. + const alwaysHiddenOnceProcessedAsk: ClineAsk[] = [ + "api_req_failed", + "resume_task", + "resume_completed_task", + ] + const alwaysHiddenOnceProcessedSay = [ + "api_req_finished", + "api_req_retried", + "api_req_deleted", + "mcp_server_request_started", + ] + if (message.ask && alwaysHiddenOnceProcessedAsk.includes(message.ask)) return false + if (message.say && alwaysHiddenOnceProcessedSay.includes(message.say)) return false + // Also, re-evaluate empty text messages if they were previously visible but now empty (e.g. partial stream ended) + if (message.say === "text" && (message.text ?? "") === "" && (message.images?.length ?? 0) === 0) { + return false + } + return true + } + + // Original filter logic switch (message.ask) { case "completion_result": - // Don't show a chat row for a completion_result ask without - // text. This specific type of message only occurs if cline - // wants to execute a command as part of its completion - // result, in which case we interject the completion_result - // tool with the execute_command tool. - if (message.text === "") { - return false - } + if (message.text === "") return false break - case "api_req_failed": // This message is used to update the latest `api_req_started` that the request failed. + case "api_req_failed": case "resume_task": case "resume_completed_task": return false } switch (message.say) { - case "api_req_finished": // `combineApiRequests` removes this from `modifiedMessages` anyways. - case "api_req_retried": // This message is used to update the latest `api_req_started` that the request was retried. - case "api_req_deleted": // Aggregated `api_req` metrics from deleted messages. + case "api_req_finished": + case "api_req_retried": + case "api_req_deleted": return false case "api_req_retry_delayed": - // Only show the retry message if it's the last message or - // the last messages is api_req_retry_delayed+resume_task. const last1 = modifiedMessages.at(-1) const last2 = modifiedMessages.at(-2) if (last1?.ask === "resume_task" && last2 === message) { - return true - } - return message === last1 - case "text": - // Sometimes cline returns an empty text message, we don't - // want to render these. (We also use a say text for user - // messages, so in case they just sent images we still - // render that.) - if ((message.text ?? "") === "" && (message.images?.length ?? 0) === 0) { + // This specific sequence should be visible + } else if (message !== last1) { + // If not the specific sequence above, and not the last message, hide it. return false } break + case "text": + if ((message.text ?? "") === "" && (message.images?.length ?? 0) === 0) return false + break case "mcp_server_request_started": return false } return true }) + + // Update the set of ever-visible messages (LRUCache automatically handles cleanup) + newVisibleMessages.forEach((msg) => everVisibleMessagesTsRef.current.set(msg.ts, true)) + + return newVisibleMessages }, [modifiedMessages]) const isReadOnlyToolAction = useCallback((message: ClineMessage | undefined) => { @@ -1006,54 +1069,21 @@ const ChatViewComponent: React.ForwardRefRenderFunction { + setExpandedRows((prev) => ({ ...prev, [ts]: expand === undefined ? !prev[ts] : expand })) + }, + [setExpandedRows], // setExpandedRows is stable + ) + // Scroll when user toggles certain rows. const toggleRowExpansion = useCallback( (ts: number) => { - const isCollapsing = expandedRows[ts] ?? false - const lastGroup = groupedMessages.at(-1) - const isLast = Array.isArray(lastGroup) ? lastGroup[0].ts === ts : lastGroup?.ts === ts - const secondToLastGroup = groupedMessages.at(-2) - const isSecondToLast = Array.isArray(secondToLastGroup) - ? secondToLastGroup[0].ts === ts - : secondToLastGroup?.ts === ts - - const isLastCollapsedApiReq = - isLast && - !Array.isArray(lastGroup) && // Make sure it's not a browser session group - lastGroup?.say === "api_req_started" && - !expandedRows[lastGroup.ts] - - setExpandedRows((prev) => ({ ...prev, [ts]: !prev[ts] })) - - // Disable auto scroll when user expands row - if (!isCollapsing) { - disableAutoScrollRef.current = true - } - - if (isCollapsing && isAtBottom) { - const timer = setTimeout(() => scrollToBottomAuto(), 0) - return () => clearTimeout(timer) - } else if (isLast || isSecondToLast) { - if (isCollapsing) { - if (isSecondToLast && !isLastCollapsedApiReq) { - return - } - - const timer = setTimeout(() => scrollToBottomAuto(), 0) - return () => clearTimeout(timer) - } else { - const timer = setTimeout(() => { - virtuosoRef.current?.scrollToIndex({ - index: groupedMessages.length - (isLast ? 1 : 2), - align: "start", - }) - }, 0) - - return () => clearTimeout(timer) - } - } + handleSetExpandedRow(ts) + // The logic to set disableAutoScrollRef.current = true on expansion + // is now handled by the useEffect hook that observes expandedRows. }, - [groupedMessages, expandedRows, scrollToBottomAuto, isAtBottom], + [handleSetExpandedRow], ) const handleRowHeightChange = useCallback( @@ -1111,6 +1141,20 @@ const ChatViewComponent: React.ForwardRefRenderFunction { + if (event?.shiftKey) { + // Always append to existing text, don't overwrite + setInputValue((currentValue) => { + return currentValue !== "" ? `${currentValue} \n${answer}` : answer + }) + } else { + handleSendMessage(answer, []) + } + }, + [handleSendMessage, setInputValue], // setInputValue is stable, handleSendMessage depends on clineAsk + ) + const itemContent = useCallback( (index: number, messageOrGroup: ClineMessage | ClineMessage[]) => { // browser session group @@ -1122,7 +1166,6 @@ const ChatViewComponent: React.ForwardRefRenderFunction expandedRows[messageTs] ?? false} onToggleExpand={(messageTs: number) => { setExpandedRows((prev) => ({ @@ -1140,32 +1183,25 @@ const ChatViewComponent: React.ForwardRefRenderFunction toggleRowExpansion(messageOrGroup.ts)} - lastModifiedMessage={modifiedMessages.at(-1)} - isLast={index === groupedMessages.length - 1} + onToggleExpand={toggleRowExpansion} // This was already stabilized + lastModifiedMessage={modifiedMessages.at(-1)} // Original direct access + isLast={index === groupedMessages.length - 1} // Original direct access onHeightChange={handleRowHeightChange} isStreaming={isStreaming} - onSuggestionClick={(answer: string, event?: React.MouseEvent) => { - if (event?.shiftKey) { - // Always append to existing text, don't overwrite - setInputValue((currentValue) => { - return currentValue !== "" ? `${currentValue} \n${answer}` : answer - }) - } else { - handleSendMessage(answer, []) - } - }} + onSuggestionClick={handleSuggestionClickInRow} // This was already stabilized /> ) }, [ + // Original broader dependencies expandedRows, + groupedMessages, modifiedMessages, - groupedMessages.length, handleRowHeightChange, isStreaming, toggleRowExpansion, - handleSendMessage, + handleSuggestionClickInRow, + setExpandedRows, // For the inline onToggleExpand in BrowserSessionRow ], ) From e8b4dda922a66154ddb95242e762b3873f9503bd Mon Sep 17 00:00:00 2001 From: Sam Hoang Van Date: Thu, 29 May 2025 00:13:42 +0700 Subject: [PATCH 035/104] feat: Make checkpoint on new task (#3834) * feat: Make checkpoint on new task Ensures that invoking the `newTaskTool` always creates a checkpoint, even if no files have changed. This provides a consistent state snapshot before a sub-task is initiated. * refactor: remove delay --------- Co-authored-by: Daniel <57051444+daniel-lxs@users.noreply.github.com> --- src/core/checkpoints/index.ts | 4 +- src/core/task/Task.ts | 4 +- src/core/tools/newTaskTool.ts | 4 + .../checkpoints/ShadowCheckpointService.ts | 12 +- .../__tests__/ShadowCheckpointService.test.ts | 175 ++++++++++++++++++ 5 files changed, 192 insertions(+), 7 deletions(-) diff --git a/src/core/checkpoints/index.ts b/src/core/checkpoints/index.ts index 3a5c2dde45..68b25b1256 100644 --- a/src/core/checkpoints/index.ts +++ b/src/core/checkpoints/index.ts @@ -152,7 +152,7 @@ async function getInitializedCheckpointService( } } -export async function checkpointSave(cline: Task) { +export async function checkpointSave(cline: Task, force = false) { const service = getCheckpointService(cline) if (!service) { @@ -169,7 +169,7 @@ export async function checkpointSave(cline: Task) { telemetryService.captureCheckpointCreated(cline.taskId) // Start the checkpoint process in the background. - return service.saveCheckpoint(`Task: ${cline.taskId}, Time: ${Date.now()}`).catch((err) => { + return service.saveCheckpoint(`Task: ${cline.taskId}, Time: ${Date.now()}`, { allowEmpty: force }).catch((err) => { console.error("[Cline#checkpointSave] caught unexpected error, disabling checkpoints", err) cline.enableCheckpoints = false }) diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index c9f2b4a100..6223f0fdfc 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -1736,8 +1736,8 @@ export class Task extends EventEmitter { // Checkpoints - public async checkpointSave() { - return checkpointSave(this) + public async checkpointSave(force: boolean = false) { + return checkpointSave(this, force) } public async checkpointRestore(options: CheckpointRestoreOptions) { diff --git a/src/core/tools/newTaskTool.ts b/src/core/tools/newTaskTool.ts index 38b4cbf302..bdb6d9a009 100644 --- a/src/core/tools/newTaskTool.ts +++ b/src/core/tools/newTaskTool.ts @@ -69,6 +69,10 @@ export async function newTaskTool( return } + if (cline.enableCheckpoints) { + cline.checkpointSave(true) + } + // Preserve the current mode so we can resume with it later. cline.pausedModeSlug = (await provider.getState()).mode ?? defaultModeSlug diff --git a/src/services/checkpoints/ShadowCheckpointService.ts b/src/services/checkpoints/ShadowCheckpointService.ts index d6e53980cb..8ec82f77ec 100644 --- a/src/services/checkpoints/ShadowCheckpointService.ts +++ b/src/services/checkpoints/ShadowCheckpointService.ts @@ -214,9 +214,14 @@ export abstract class ShadowCheckpointService extends EventEmitter { return this.shadowGitConfigWorktree } - public async saveCheckpoint(message: string): Promise { + public async saveCheckpoint( + message: string, + options?: { allowEmpty?: boolean }, + ): Promise { try { - this.log(`[${this.constructor.name}#saveCheckpoint] starting checkpoint save`) + this.log( + `[${this.constructor.name}#saveCheckpoint] starting checkpoint save (allowEmpty: ${options?.allowEmpty ?? false})`, + ) if (!this.git) { throw new Error("Shadow git repo not initialized") @@ -224,7 +229,8 @@ export abstract class ShadowCheckpointService extends EventEmitter { const startTime = Date.now() await this.stageAll(this.git) - const result = await this.git.commit(message) + const commitArgs = options?.allowEmpty ? { "--allow-empty": null } : undefined + const result = await this.git.commit(message, commitArgs) const isFirst = this._checkpoints.length === 0 const fromHash = this._checkpoints[this._checkpoints.length - 1] ?? this.baseHash! const toHash = result.commit || fromHash diff --git a/src/services/checkpoints/__tests__/ShadowCheckpointService.test.ts b/src/services/checkpoints/__tests__/ShadowCheckpointService.test.ts index 84589c5fd2..ad155b36c3 100644 --- a/src/services/checkpoints/__tests__/ShadowCheckpointService.test.ts +++ b/src/services/checkpoints/__tests__/ShadowCheckpointService.test.ts @@ -632,5 +632,180 @@ describe.each([[RepoPerTaskCheckpointService, "RepoPerTaskCheckpointService"]])( expect(checkpointHandler).not.toHaveBeenCalled() }) }) + + describe(`${klass.name}#saveCheckpoint with allowEmpty option`, () => { + it("creates checkpoint with allowEmpty=true even when no changes", async () => { + // No changes made, but force checkpoint creation + const result = await service.saveCheckpoint("Empty checkpoint", { allowEmpty: true }) + + expect(result).toBeDefined() + expect(result?.commit).toBeTruthy() + expect(typeof result?.commit).toBe("string") + }) + + it("does not create checkpoint with allowEmpty=false when no changes", async () => { + const result = await service.saveCheckpoint("No changes checkpoint", { allowEmpty: false }) + + expect(result).toBeUndefined() + }) + + it("does not create checkpoint by default when no changes", async () => { + const result = await service.saveCheckpoint("Default behavior checkpoint") + + expect(result).toBeUndefined() + }) + + it("creates checkpoint with changes regardless of allowEmpty setting", async () => { + await fs.writeFile(testFile, "Modified content for allowEmpty test") + + const resultWithAllowEmpty = await service.saveCheckpoint("With changes and allowEmpty", { allowEmpty: true }) + expect(resultWithAllowEmpty?.commit).toBeTruthy() + + await fs.writeFile(testFile, "Another modification for allowEmpty test") + + const resultWithoutAllowEmpty = await service.saveCheckpoint("With changes, no allowEmpty") + expect(resultWithoutAllowEmpty?.commit).toBeTruthy() + }) + + it("emits checkpoint event for empty commits when allowEmpty=true", async () => { + const checkpointHandler = jest.fn() + service.on("checkpoint", checkpointHandler) + + const result = await service.saveCheckpoint("Empty checkpoint event test", { allowEmpty: true }) + + expect(checkpointHandler).toHaveBeenCalledTimes(1) + const eventData = checkpointHandler.mock.calls[0][0] + expect(eventData.type).toBe("checkpoint") + expect(eventData.toHash).toBe(result?.commit) + expect(typeof eventData.duration).toBe("number") + expect(typeof eventData.isFirst).toBe("boolean") // Can be true or false depending on checkpoint history + }) + + it("does not emit checkpoint event when no changes and allowEmpty=false", async () => { + // First, create a checkpoint to ensure we're not in the initial state + await fs.writeFile(testFile, "Setup content") + await service.saveCheckpoint("Setup checkpoint") + + // Reset the file to original state + await fs.writeFile(testFile, "Hello, world!") + await service.saveCheckpoint("Reset to original") + + // Now test with no changes and allowEmpty=false + const checkpointHandler = jest.fn() + service.on("checkpoint", checkpointHandler) + + const result = await service.saveCheckpoint("No changes, no event", { allowEmpty: false }) + + expect(result).toBeUndefined() + expect(checkpointHandler).not.toHaveBeenCalled() + }) + + it("handles multiple empty checkpoints correctly", async () => { + const commit1 = await service.saveCheckpoint("First empty checkpoint", { allowEmpty: true }) + expect(commit1?.commit).toBeTruthy() + + const commit2 = await service.saveCheckpoint("Second empty checkpoint", { allowEmpty: true }) + expect(commit2?.commit).toBeTruthy() + + // Commits should be different + expect(commit1?.commit).not.toBe(commit2?.commit) + }) + + it("logs correct message for allowEmpty option", async () => { + const logMessages: string[] = [] + const testService = await klass.create({ + taskId: "log-test", + shadowDir: path.join(tmpDir, `log-test-${Date.now()}`), + workspaceDir: service.workspaceDir, + log: (message: string) => logMessages.push(message), + }) + await testService.initShadowGit() + + await testService.saveCheckpoint("Test logging with allowEmpty", { allowEmpty: true }) + + const saveCheckpointLogs = logMessages.filter(msg => + msg.includes("starting checkpoint save") && msg.includes("allowEmpty: true") + ) + expect(saveCheckpointLogs).toHaveLength(1) + + await testService.saveCheckpoint("Test logging without allowEmpty") + + const defaultLogs = logMessages.filter(msg => + msg.includes("starting checkpoint save") && msg.includes("allowEmpty: false") + ) + expect(defaultLogs).toHaveLength(1) + }) + + it("maintains checkpoint history with empty commits", async () => { + // Create a regular checkpoint + await fs.writeFile(testFile, "Regular change") + const regularCommit = await service.saveCheckpoint("Regular checkpoint") + expect(regularCommit?.commit).toBeTruthy() + + // Create an empty checkpoint + const emptyCommit = await service.saveCheckpoint("Empty checkpoint", { allowEmpty: true }) + expect(emptyCommit?.commit).toBeTruthy() + + // Create another regular checkpoint + await fs.writeFile(testFile, "Another regular change") + const anotherCommit = await service.saveCheckpoint("Another regular checkpoint") + expect(anotherCommit?.commit).toBeTruthy() + + // Verify we can restore to the empty checkpoint + await service.restoreCheckpoint(emptyCommit!.commit) + expect(await fs.readFile(testFile, "utf-8")).toBe("Regular change") + + // Verify we can restore to other checkpoints + await service.restoreCheckpoint(regularCommit!.commit) + expect(await fs.readFile(testFile, "utf-8")).toBe("Regular change") + + await service.restoreCheckpoint(anotherCommit!.commit) + expect(await fs.readFile(testFile, "utf-8")).toBe("Another regular change") + }) + + it("handles getDiff correctly with empty commits", async () => { + // Create a regular checkpoint + await fs.writeFile(testFile, "Content before empty") + const beforeEmpty = await service.saveCheckpoint("Before empty") + expect(beforeEmpty?.commit).toBeTruthy() + + // Create an empty checkpoint + const emptyCommit = await service.saveCheckpoint("Empty checkpoint", { allowEmpty: true }) + expect(emptyCommit?.commit).toBeTruthy() + + // Get diff between regular commit and empty commit + const diff = await service.getDiff({ + from: beforeEmpty!.commit, + to: emptyCommit!.commit + }) + + // Should have no differences since empty commit doesn't change anything + expect(diff).toHaveLength(0) + }) + + it("works correctly in integration with new task workflow", async () => { + // Simulate the new task workflow where we force a checkpoint even with no changes + // This tests the specific use case mentioned in the git commit + + // Start with a clean state (no pending changes) + const initialState = await service.saveCheckpoint("Check initial state") + expect(initialState).toBeUndefined() // No changes, so no commit + + // Force a checkpoint for new task (this is the new functionality) + const newTaskCheckpoint = await service.saveCheckpoint("New task checkpoint", { allowEmpty: true }) + expect(newTaskCheckpoint?.commit).toBeTruthy() + + // Verify the checkpoint was created and can be restored + await fs.writeFile(testFile, "Work done in new task") + const workCommit = await service.saveCheckpoint("Work in new task") + expect(workCommit?.commit).toBeTruthy() + + // Restore to the new task checkpoint + await service.restoreCheckpoint(newTaskCheckpoint!.commit) + + // File should be back to original state + expect(await fs.readFile(testFile, "utf-8")).toBe("Hello, world!") + }) + }) }, ) From 171307c2dc2656ae7a9bfa4e615d4b7dbe0e98bf Mon Sep 17 00:00:00 2001 From: ChuKhaLi <15166543+ChuKhaLi@users.noreply.github.com> Date: Thu, 29 May 2025 00:23:49 +0700 Subject: [PATCH 036/104] fix: ensure correct precedence for roleDefinition and customInstructions when generating system prompt (#3791) * fix: ensure correct precedence for roleDefinition and customInstructions * Refactors mode selection logic for custom modes Refactors the mode selection logic to prioritize custom modes --- src/core/prompts/system.ts | 17 +- src/shared/__tests__/modes.test.ts | 210 +++++++++++++++++- src/shared/modes.ts | 38 +++- .../src/components/prompts/PromptsView.tsx | 13 +- 4 files changed, 265 insertions(+), 13 deletions(-) diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index b5471cea9a..a8edeea83e 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -3,7 +3,7 @@ import * as os from "os" import type { ModeConfig, PromptComponent, CustomModePrompts } from "@roo-code/types" -import { Mode, modes, defaultModeSlug, getModeBySlug, getGroupName } from "../../shared/modes" +import { Mode, modes, defaultModeSlug, getModeBySlug, getGroupName, getModeSelection } from "../../shared/modes" import { DiffStrategy } from "../../shared/tools" import { formatLanguage } from "../../shared/language" @@ -51,9 +51,9 @@ async function generatePrompt( // If diff is disabled, don't pass the diffStrategy const effectiveDiffStrategy = diffEnabled ? diffStrategy : undefined - // Get the full mode config to ensure we have the role definition + // Get the full mode config to ensure we have the role definition (used for groups, etc.) const modeConfig = getModeBySlug(mode, customModeConfigs) || modes.find((m) => m.slug === mode) || modes[0] - const roleDefinition = promptComponent?.roleDefinition || modeConfig.roleDefinition + const { roleDefinition, baseInstructions } = getModeSelection(mode, promptComponent, customModeConfigs) const [modesSection, mcpServersSection] = await Promise.all([ getModesSection(context), @@ -97,7 +97,7 @@ ${getSystemInfoSection(cwd)} ${getObjectiveSection()} -${await addCustomInstructions(promptComponent?.customInstructions || modeConfig.customInstructions || "", globalCustomInstructions || "", cwd, mode, { language: language ?? formatLanguage(vscode.env.language), rooIgnoreInstructions })}` +${await addCustomInstructions(baseInstructions, globalCustomInstructions || "", cwd, mode, { language: language ?? formatLanguage(vscode.env.language), rooIgnoreInstructions })}` return basePrompt } @@ -149,9 +149,14 @@ export const SYSTEM_PROMPT = async ( // If a file-based custom system prompt exists, use it if (fileCustomSystemPrompt) { - const roleDefinition = promptComponent?.roleDefinition || currentMode.roleDefinition + const { roleDefinition, baseInstructions: baseInstructionsForFile } = getModeSelection( + mode, + promptComponent, + customModes, + ) + const customInstructions = await addCustomInstructions( - promptComponent?.customInstructions || currentMode.customInstructions || "", + baseInstructionsForFile, globalCustomInstructions || "", cwd, mode, diff --git a/src/shared/__tests__/modes.test.ts b/src/shared/__tests__/modes.test.ts index e45417c741..f5de88cb9e 100644 --- a/src/shared/__tests__/modes.test.ts +++ b/src/shared/__tests__/modes.test.ts @@ -1,6 +1,6 @@ // npx jest src/shared/__tests__/modes.test.ts -import type { ModeConfig } from "@roo-code/types" +import type { ModeConfig, PromptComponent } from "@roo-code/types" // Mock setup must come before imports jest.mock("vscode") @@ -11,7 +11,7 @@ jest.mock("../../core/prompts/sections/custom-instructions", () => ({ addCustomInstructions: mockAddCustomInstructions, })) -import { isToolAllowedForMode, FileRestrictionError, getFullModeDetails, modes } from "../modes" +import { isToolAllowedForMode, FileRestrictionError, getFullModeDetails, modes, getModeSelection } from "../modes" import { addCustomInstructions } from "../../core/prompts/sections/custom-instructions" describe("isToolAllowedForMode", () => { @@ -371,3 +371,209 @@ describe("FileRestrictionError", () => { expect(error.name).toBe("FileRestrictionError") }) }) + +describe("getModeSelection", () => { + const builtInAskMode = modes.find((m) => m.slug === "ask")! + const customModesList: ModeConfig[] = [ + { + slug: "code", // Override + name: "Custom Code Mode", + roleDefinition: "Custom Code Role", + customInstructions: "Custom Code Instructions", + groups: ["read"], + }, + { + slug: "new-custom", + name: "New Custom Mode", + roleDefinition: "New Custom Role", + customInstructions: "New Custom Instructions", + groups: ["edit"], + }, + ] + + const promptComponentCode: PromptComponent = { + roleDefinition: "Prompt Component Code Role", + customInstructions: "Prompt Component Code Instructions", + } + + const promptComponentAsk: PromptComponent = { + roleDefinition: "Prompt Component Ask Role", + customInstructions: "Prompt Component Ask Instructions", + } + + test("should return built-in mode details if no overrides", () => { + const selection = getModeSelection("ask") + expect(selection.roleDefinition).toBe(builtInAskMode.roleDefinition) + expect(selection.baseInstructions).toBe(builtInAskMode.customInstructions || "") + }) + + test("should prioritize promptComponent for built-in mode if no custom mode exists for that slug", () => { + const selection = getModeSelection("ask", promptComponentAsk) // "ask" is not in customModesList + expect(selection.roleDefinition).toBe(promptComponentAsk.roleDefinition) + expect(selection.baseInstructions).toBe(promptComponentAsk.customInstructions) + }) + + test("should prioritize customMode over built-in mode", () => { + const selection = getModeSelection("code", undefined, customModesList) + const customCode = customModesList.find((m) => m.slug === "code")! + expect(selection.roleDefinition).toBe(customCode.roleDefinition) + expect(selection.baseInstructions).toBe(customCode.customInstructions) + }) + + test("should prioritize customMode over promptComponent and built-in mode", () => { + const selection = getModeSelection("code", promptComponentCode, customModesList) + const customCode = customModesList.find((m) => m.slug === "code")! + expect(selection.roleDefinition).toBe(customCode.roleDefinition) + expect(selection.baseInstructions).toBe(customCode.customInstructions) + }) + + test("should return new custom mode details if it exists", () => { + const selection = getModeSelection("new-custom", undefined, customModesList) + const newCustom = customModesList.find((m) => m.slug === "new-custom")! + expect(selection.roleDefinition).toBe(newCustom.roleDefinition) + expect(selection.baseInstructions).toBe(newCustom.customInstructions) + }) + + test("customMode takes precedence for a new custom mode even if promptComponent is provided", () => { + const promptComponentNew: PromptComponent = { + roleDefinition: "Prompt New Custom Role", + customInstructions: "Prompt New Custom Instructions", + } + const selection = getModeSelection("new-custom", promptComponentNew, customModesList) + const newCustomMode = customModesList.find((m) => m.slug === "new-custom")! + expect(selection.roleDefinition).toBe(newCustomMode.roleDefinition) + expect(selection.baseInstructions).toBe(newCustomMode.customInstructions) + }) + + test("should return empty strings if slug does not exist in custom, prompt, or built-in modes", () => { + const selection = getModeSelection("non-existent-mode", undefined, customModesList) + expect(selection.roleDefinition).toBe("") + expect(selection.baseInstructions).toBe("") + }) + + test("customMode's properties are used if customMode exists, ignoring promptComponent's properties", () => { + const selection = getModeSelection( + "code", + { roleDefinition: "Prompt Role Only", customInstructions: "Prompt Instructions Only" }, + customModesList, + ) + const customCodeMode = customModesList.find((m) => m.slug === "code")! + expect(selection.roleDefinition).toBe(customCodeMode.roleDefinition) // Takes from customCodeMode + expect(selection.baseInstructions).toBe(customCodeMode.customInstructions) // Takes from customCodeMode + }) + + test("handles undefined customInstructions in customMode gracefully", () => { + const modesWithoutCustomInstructions: ModeConfig[] = [ + { + slug: "no-instr", + name: "No Instructions Mode", + roleDefinition: "Role for no instructions", + groups: ["read"], + // customInstructions is undefined + }, + ] + const selection = getModeSelection("no-instr", undefined, modesWithoutCustomInstructions) + expect(selection.roleDefinition).toBe("Role for no instructions") + expect(selection.baseInstructions).toBe("") // Defaults to empty string + }) + + test("handles empty or undefined roleDefinition in customMode gracefully", () => { + const modesWithEmptyRoleDef: ModeConfig[] = [ + { + slug: "empty-role", + name: "Empty Role Mode", + roleDefinition: "", + customInstructions: "Instructions for empty role", + groups: ["read"], + }, + ] + const selection = getModeSelection("empty-role", undefined, modesWithEmptyRoleDef) + expect(selection.roleDefinition).toBe("") + expect(selection.baseInstructions).toBe("Instructions for empty role") + + const modesWithUndefinedRoleDef: ModeConfig[] = [ + { + slug: "undefined-role", + name: "Undefined Role Mode", + roleDefinition: "", // Test undefined explicitly by using an empty string + customInstructions: "Instructions for undefined role", + groups: ["read"], + }, + ] + const selection2 = getModeSelection("undefined-role", undefined, modesWithUndefinedRoleDef) + expect(selection2.roleDefinition).toBe("") + expect(selection2.baseInstructions).toBe("Instructions for undefined role") + }) + + test("customMode's defined properties take precedence, undefined ones in customMode result in ''", () => { + const customModeRoleOnlyList: ModeConfig[] = [ + // Renamed for clarity + { + slug: "role-custom", + name: "Role Custom", + roleDefinition: "Custom Role Only", + groups: ["read"] /* customInstructions undefined */, + }, + ] + const promptComponentInstrOnly: PromptComponent = { customInstructions: "Prompt Instructions Only" } + // "role-custom" exists in customModeRoleOnlyList + const selection = getModeSelection("role-custom", promptComponentInstrOnly, customModeRoleOnlyList) + // customMode is chosen. + expect(selection.roleDefinition).toBe("Custom Role Only") // From customMode + expect(selection.baseInstructions).toBe("") // From customMode (undefined || '' -> '') + }) + + test("customMode's defined properties take precedence, empty string ones in customMode are used", () => { + const customModeInstrOnlyList: ModeConfig[] = [ + // Renamed for clarity + { + slug: "instr-custom", + name: "Instr Custom", + roleDefinition: "", // Explicitly empty + customInstructions: "Custom Instructions Only", + groups: ["read"], + }, + ] + const promptComponentRoleOnly: PromptComponent = { roleDefinition: "Prompt Role Only" } + // "instr-custom" exists in customModeInstrOnlyList + const selection = getModeSelection("instr-custom", promptComponentRoleOnly, customModeInstrOnlyList) + // customMode is chosen + expect(selection.roleDefinition).toBe("") // From customMode ( "" || '' -> "") + expect(selection.baseInstructions).toBe("Custom Instructions Only") // From customMode + }) + + test("customMode with empty/undefined fields takes precedence over promptComponent and builtInMode", () => { + const customModeMinimal: ModeConfig[] = [ + { slug: "ask", name: "Custom Ask Minimal", roleDefinition: "", groups: ["read"] }, // roleDef empty, customInstr undefined + ] + const promptComponentMinimal: PromptComponent = { + roleDefinition: "Prompt Min Role", + customInstructions: "Prompt Min Instr", + } + // "ask" is in customModeMinimal + const selection = getModeSelection("ask", promptComponentMinimal, customModeMinimal) + // customMode is chosen + expect(selection.roleDefinition).toBe("") // From customModeMinimal + expect(selection.baseInstructions).toBe("") // From customModeMinimal + }) + + test("promptComponent is used if customMode for slug does not exist, even if customModesList is provided", () => { + // 'ask' is not in customModesList, but 'code' and 'new-custom' are. + const selection = getModeSelection("ask", promptComponentAsk, customModesList) + expect(selection.roleDefinition).toBe(promptComponentAsk.roleDefinition) + expect(selection.baseInstructions).toBe(promptComponentAsk.customInstructions) + }) + + test("builtInMode is used if customMode for slug does not exist and promptComponent is not provided", () => { + // 'ask' is not in customModesList + const selection = getModeSelection("ask", undefined, customModesList) + expect(selection.roleDefinition).toBe(builtInAskMode.roleDefinition) + expect(selection.baseInstructions).toBe(builtInAskMode.customInstructions || "") + }) + + test("promptComponent is used if customMode is not provided (undefined customModesList)", () => { + const selection = getModeSelection("ask", promptComponentAsk, undefined) + expect(selection.roleDefinition).toBe(promptComponentAsk.roleDefinition) + expect(selection.baseInstructions).toBe(promptComponentAsk.customInstructions) + }) +}) diff --git a/src/shared/modes.ts b/src/shared/modes.ts index 686c0437d3..c735118f66 100644 --- a/src/shared/modes.ts +++ b/src/shared/modes.ts @@ -1,6 +1,14 @@ import * as vscode from "vscode" -import type { GroupOptions, GroupEntry, ModeConfig, CustomModePrompts, ExperimentId, ToolGroup } from "@roo-code/types" +import type { + GroupOptions, + GroupEntry, + ModeConfig, + CustomModePrompts, + ExperimentId, + ToolGroup, + PromptComponent, +} from "@roo-code/types" import { addCustomInstructions } from "../core/prompts/sections/custom-instructions" @@ -149,6 +157,34 @@ export function isCustomMode(slug: string, customModes?: ModeConfig[]): boolean return !!customModes?.some((mode) => mode.slug === slug) } +/** + * Find a mode by its slug, don't fall back to built-in modes + */ +export function findModeBySlug(slug: string, modes: readonly ModeConfig[] | undefined): ModeConfig | undefined { + return modes?.find((mode) => mode.slug === slug) +} + +/** + * Get the mode selection based on the provided mode slug, prompt component, and custom modes. + * If a custom mode is found, it takes precedence over the built-in modes. + * If no custom mode is found, the built-in mode is used. + * If neither is found, the default mode is used. + */ +export function getModeSelection(mode: string, promptComponent?: PromptComponent, customModes?: ModeConfig[]) { + const customMode = findModeBySlug(mode, customModes) + const builtInMode = findModeBySlug(mode, modes) + + const modeToUse = customMode || promptComponent || builtInMode + + const roleDefinition = modeToUse?.roleDefinition || "" + const baseInstructions = modeToUse?.customInstructions || "" + + return { + roleDefinition, + baseInstructions, + } +} + // Custom error class for file restrictions export class FileRestrictionError extends Error { constructor(mode: string, pattern: string, description: string | undefined, filePath: string) { diff --git a/webview-ui/src/components/prompts/PromptsView.tsx b/webview-ui/src/components/prompts/PromptsView.tsx index 1f175154e1..8ee63f3dfb 100644 --- a/webview-ui/src/components/prompts/PromptsView.tsx +++ b/webview-ui/src/components/prompts/PromptsView.tsx @@ -11,7 +11,14 @@ import { ChevronsUpDown, X } from "lucide-react" import { ModeConfig, GroupEntry, PromptComponent, ToolGroup, modeConfigSchema } from "@roo-code/types" -import { Mode, getRoleDefinition, getWhenToUse, getCustomInstructions, getAllModes } from "@roo/modes" +import { + Mode, + getRoleDefinition, + getWhenToUse, + getCustomInstructions, + getAllModes, + findModeBySlug as findCustomModeBySlug, +} from "@roo/modes" import { supportPrompt, SupportPromptType } from "@roo/support-prompt" import { TOOL_GROUPS } from "@roo/tools" @@ -133,9 +140,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { // Helper function to find a mode by slug const findModeBySlug = useCallback( (searchSlug: string, modes: readonly ModeConfig[] | undefined): ModeConfig | undefined => { - if (!modes) return undefined - const isModeWithSlug = (mode: ModeConfig): mode is ModeConfig => mode.slug === searchSlug - return modes.find(isModeWithSlug) + return findCustomModeBySlug(searchSlug, modes) }, [], ) From 5d33d4c3ffb70d9f68bee56a4fc98e618f547c82 Mon Sep 17 00:00:00 2001 From: Adrian Belmans Date: Wed, 28 May 2025 19:27:01 +0200 Subject: [PATCH 037/104] Mcp server instructions update fix (#3699) * Refactor weather server example implementation inside of prompt to use new MCP SDK features * update NPM install instructions as well * docs: minor improvements * refactor: improve readability on mcpHub check * fix: add missing bracket * refactor: add interfaces --------- Co-authored-by: Daniel <57051444+daniel-lxs@users.noreply.github.com> --- .../prompts/instructions/create-mcp-server.ts | 424 +++++++----------- 1 file changed, 170 insertions(+), 254 deletions(-) diff --git a/src/core/prompts/instructions/create-mcp-server.ts b/src/core/prompts/instructions/create-mcp-server.ts index 71982528ef..3d1d2a20cf 100644 --- a/src/core/prompts/instructions/create-mcp-server.ts +++ b/src/core/prompts/instructions/create-mcp-server.ts @@ -64,7 +64,7 @@ cd ${await mcpHub.getMcpServersPath()} npx @modelcontextprotocol/create-server weather-server cd weather-server # Install dependencies -npm install axios +npm install axios zod @modelcontextprotocol/sdk \`\`\` This will create a new project with the following structure: @@ -83,271 +83,185 @@ weather-server/ } ├── tsconfig.json └── src/ - └── weather-server/ - └── index.ts # Main server implementation + └── index.ts # Main server implementation \`\`\` 2. Replace \`src/index.ts\` with the following: \`\`\`typescript #!/usr/bin/env node -import { Server } from '@modelcontextprotocol/sdk/server/index.js'; -import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; -import { - CallToolRequestSchema, - ErrorCode, - ListResourcesRequestSchema, - ListResourceTemplatesRequestSchema, - ListToolsRequestSchema, - McpError, - ReadResourceRequestSchema, -} from '@modelcontextprotocol/sdk/types.js'; +import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { z } from "zod"; import axios from 'axios'; const API_KEY = process.env.OPENWEATHER_API_KEY; // provided by MCP config if (!API_KEY) { - throw new Error('OPENWEATHER_API_KEY environment variable is required'); + throw new Error('OPENWEATHER_API_KEY environment variable is required'); } -interface OpenWeatherResponse { - main: { - temp: number; - humidity: number; - }; - weather: [{ description: string }]; - wind: { speed: number }; - dt_txt?: string; +// Define types for OpenWeather API responses +interface WeatherData { + main: { + temp: number; + humidity: number; + }; + weather: Array<{ + description: string; + }>; + wind: { + speed: number; + }; } -const isValidForecastArgs = ( - args: any -): args is { city: string; days?: number } => - typeof args === 'object' && - args !== null && - typeof args.city === 'string' && - (args.days === undefined || typeof args.days === 'number'); - -class WeatherServer { - private server: Server; - private axiosInstance; - - constructor() { - this.server = new Server( - { - name: 'example-weather-server', - version: '0.1.0', - }, - { - capabilities: { - resources: {}, - tools: {}, - }, - } - ); - - this.axiosInstance = axios.create({ - baseURL: 'http://api.openweathermap.org/data/2.5', - params: { - appid: API_KEY, - units: 'metric', - }, - }); - - this.setupResourceHandlers(); - this.setupToolHandlers(); - - // Error handling - this.server.onerror = (error) => console.error('[MCP Error]', error); - process.on('SIGINT', async () => { - await this.server.close(); - process.exit(0); - }); - } - - // MCP Resources represent any kind of UTF-8 encoded data that an MCP server wants to make available to clients, such as database records, API responses, log files, and more. Servers define direct resources with a static URI or dynamic resources with a URI template that follows the format \`[protocol]://[host]/[path]\`. - private setupResourceHandlers() { - // For static resources, servers can expose a list of resources: - this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({ - resources: [ - // This is a poor example since you could use the resource template to get the same information but this demonstrates how to define a static resource - { - uri: \`weather://San Francisco/current\`, // Unique identifier for San Francisco weather resource - name: \`Current weather in San Francisco\`, // Human-readable name - mimeType: 'application/json', // Optional MIME type - // Optional description - description: - 'Real-time weather data for San Francisco including temperature, conditions, humidity, and wind speed', - }, - ], - })); - - // For dynamic resources, servers can expose resource templates: - this.server.setRequestHandler( - ListResourceTemplatesRequestSchema, - async () => ({ - resourceTemplates: [ - { - uriTemplate: 'weather://{city}/current', // URI template (RFC 6570) - name: 'Current weather for a given city', // Human-readable name - mimeType: 'application/json', // Optional MIME type - description: 'Real-time weather data for a specified city', // Optional description - }, - ], - }) - ); - - // ReadResourceRequestSchema is used for both static resources and dynamic resource templates - this.server.setRequestHandler( - ReadResourceRequestSchema, - async (request) => { - const match = request.params.uri.match( - /^weather:\/\/([^/]+)\/current$/ - ); - if (!match) { - throw new McpError( - ErrorCode.InvalidRequest, - \`Invalid URI format: \${request.params.uri}\` - ); - } - const city = decodeURIComponent(match[1]); - - try { - const response = await this.axiosInstance.get( - 'weather', // current weather - { - params: { q: city }, - } - ); - - return { - contents: [ - { - uri: request.params.uri, - mimeType: 'application/json', - text: JSON.stringify( - { - temperature: response.data.main.temp, - conditions: response.data.weather[0].description, - humidity: response.data.main.humidity, - wind_speed: response.data.wind.speed, - timestamp: new Date().toISOString(), - }, - null, - 2 - ), - }, - ], - }; - } catch (error) { - if (axios.isAxiosError(error)) { - throw new McpError( - ErrorCode.InternalError, - \`Weather API error: \${ - error.response?.data.message ?? error.message - }\` - ); - } - throw error; - } - } - ); - } - - /* MCP Tools enable servers to expose executable functionality to the system. Through these tools, you can interact with external systems, perform computations, and take actions in the real world. - * - Like resources, tools are identified by unique names and can include descriptions to guide their usage. However, unlike resources, tools represent dynamic operations that can modify state or interact with external systems. - * - While resources and tools are similar, you should prefer to create tools over resources when possible as they provide more flexibility. - */ - private setupToolHandlers() { - this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ - tools: [ - { - name: 'get_forecast', // Unique identifier - description: 'Get weather forecast for a city', // Human-readable description - inputSchema: { - // JSON Schema for parameters - type: 'object', - properties: { - city: { - type: 'string', - description: 'City name', - }, - days: { - type: 'number', - description: 'Number of days (1-5)', - minimum: 1, - maximum: 5, - }, - }, - required: ['city'], // Array of required property names - }, - }, - ], - })); - - this.server.setRequestHandler(CallToolRequestSchema, async (request) => { - if (request.params.name !== 'get_forecast') { - throw new McpError( - ErrorCode.MethodNotFound, - \`Unknown tool: \${request.params.name}\` - ); - } - - if (!isValidForecastArgs(request.params.arguments)) { - throw new McpError( - ErrorCode.InvalidParams, - 'Invalid forecast arguments' - ); - } - - const city = request.params.arguments.city; - const days = Math.min(request.params.arguments.days || 3, 5); - - try { - const response = await this.axiosInstance.get<{ - list: OpenWeatherResponse[]; - }>('forecast', { - params: { - q: city, - cnt: days * 8, - }, - }); - - return { - content: [ - { - type: 'text', - text: JSON.stringify(response.data.list, null, 2), - }, - ], - }; - } catch (error) { - if (axios.isAxiosError(error)) { - return { - content: [ - { - type: 'text', - text: \`Weather API error: \${ - error.response?.data.message ?? error.message - }\`, - }, - ], - isError: true, - }; - } - throw error; - } - }); - } - - async run() { - const transport = new StdioServerTransport(); - await this.server.connect(transport); - console.error('Weather MCP server running on stdio'); - } +interface ForecastData { + list: Array; } -const server = new WeatherServer(); -server.run().catch(console.error); +// Create an MCP server +const server = new McpServer({ + name: "weather-server", + version: "0.1.0" +}); + +// Create axios instance for OpenWeather API +const weatherApi = axios.create({ + baseURL: 'http://api.openweathermap.org/data/2.5', + params: { + appid: API_KEY, + units: 'metric', + }, +}); + +// Add a tool for getting weather forecasts +server.tool( + "get_forecast", + { + city: z.string().describe("City name"), + days: z.number().min(1).max(5).optional().describe("Number of days (1-5)"), + }, + async ({ city, days = 3 }) => { + try { + const response = await weatherApi.get('forecast', { + params: { + q: city, + cnt: Math.min(days, 5) * 8, + }, + }); + + return { + content: [ + { + type: "text", + text: JSON.stringify(response.data.list, null, 2), + }, + ], + }; + } catch (error) { + if (axios.isAxiosError(error)) { + return { + content: [ + { + type: "text", + text: \`Weather API error: \${ + error.response?.data.message ?? error.message + }\`, + }, + ], + isError: true, + }; + } + throw error; + } + } +); + +// Add a resource for current weather in San Francisco +server.resource( + "sf_weather", + { uri: "weather://San Francisco/current", list: true }, + async (uri) => { + try { + const response = weatherApi.get('weather', { + params: { q: "San Francisco" }, + }); + + return { + contents: [ + { + uri: uri.href, + mimeType: "application/json", + text: JSON.stringify( + { + temperature: response.data.main.temp, + conditions: response.data.weather[0].description, + humidity: response.data.main.humidity, + wind_speed: response.data.wind.speed, + timestamp: new Date().toISOString(), + }, + null, + 2 + ), + }, + ], + }; + } catch (error) { + if (axios.isAxiosError(error)) { + throw new Error(\`Weather API error: \${ + error.response?.data.message ?? error.message + }\`); + } + throw error; + } + } +); + +// Add a dynamic resource template for current weather by city +server.resource( + "current_weather", + new ResourceTemplate("weather://{city}/current", { list: true }), + async (uri, { city }) => { + try { + const response = await weatherApi.get('weather', { + params: { q: city }, + }); + + return { + contents: [ + { + uri: uri.href, + mimeType: "application/json", + text: JSON.stringify( + { + temperature: response.data.main.temp, + conditions: response.data.weather[0].description, + humidity: response.data.main.humidity, + wind_speed: response.data.wind.speed, + timestamp: new Date().toISOString(), + }, + null, + 2 + ), + }, + ], + }; + } catch (error) { + if (axios.isAxiosError(error)) { + throw new Error(\`Weather API error: \${ + error.response?.data.message ?? error.message + }\`); + } + throw error; + } + } +); + +// Start receiving messages on stdin and sending messages on stdout +const transport = new StdioServerTransport(); +await server.connect(transport); +console.error('Weather MCP server running on stdio'); \`\`\` (Remember: This is just an example–you may use different dependencies, break the implementation up into multiple files, etc.) @@ -387,12 +301,14 @@ IMPORTANT: Regardless of what else you see in the MCP settings file, you must de ## Editing MCP Servers -The user may ask to add tools or resources that may make sense to add to an existing MCP server (listed under 'Connected MCP Servers' above: ${ - mcpHub +The user may ask to add tools or resources that may make sense to add to an existing MCP server (listed under 'Connected MCP Servers' above: ${(() => { + if (!mcpHub) return "(None running currently)" + const servers = mcpHub .getServers() .map((server) => server.name) - .join(", ") || "(None running currently)" - }, e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use write_to_file${diffStrategy ? " or apply_diff" : ""} to make changes to the files. + .join(", ") + return servers || "(None running currently)" + })()}, e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use write_to_file${diffStrategy ? " or apply_diff" : ""} to make changes to the files. However some MCP servers may be running from installed packages rather than a local repository, in which case it may make more sense to create a new MCP server. From 145ce6e7b5b1f3d6d59e8f2a1678ef4d3993fa52 Mon Sep 17 00:00:00 2001 From: Sacha Sayan Date: Wed, 28 May 2025 13:45:52 -0400 Subject: [PATCH 038/104] Lock mermaid diagram sizes. (#3514) --- webview-ui/src/components/common/MermaidBlock.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/webview-ui/src/components/common/MermaidBlock.tsx b/webview-ui/src/components/common/MermaidBlock.tsx index d44d51b5f9..4d8d9e6272 100644 --- a/webview-ui/src/components/common/MermaidBlock.tsx +++ b/webview-ui/src/components/common/MermaidBlock.tsx @@ -306,4 +306,12 @@ const SvgContainer = styled.div` cursor: pointer; display: flex; justify-content: center; + max-height: 400px; + + /* Ensure the SVG scales within the container */ + & > svg { + display: block; /* Ensure block layout */ + width: 100%; + max-height: 100%; /* Respect container's max-height */ + } ` From 2c58caeb59ccd3a9659ee3e62b3e7cc6b4f37485 Mon Sep 17 00:00:00 2001 From: xyOz Date: Wed, 28 May 2025 19:59:02 +0100 Subject: [PATCH 039/104] Chat input clearing during running task (#4084) Fixes bug --- webview-ui/src/components/chat/ChatView.tsx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index ef80bf18cb..fd5972f8c2 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -345,14 +345,6 @@ const ChatViewComponent: React.ForwardRefRenderFunction Date: Thu, 29 May 2025 00:35:00 +0530 Subject: [PATCH 040/104] bugfix: Update PAGER env for Windows compatibility in Terminal (#3986) Update PAGER environment variable for Windows compatibility in Terminal class --- src/integrations/terminal/Terminal.ts | 2 +- .../terminal/__tests__/TerminalRegistry.test.ts | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/integrations/terminal/Terminal.ts b/src/integrations/terminal/Terminal.ts index 4b35e92bbf..8bf2072f3d 100644 --- a/src/integrations/terminal/Terminal.ts +++ b/src/integrations/terminal/Terminal.ts @@ -152,7 +152,7 @@ export class Terminal extends BaseTerminal { public static getEnv(): Record { const env: Record = { - PAGER: "cat", + PAGER: process.platform === "win32" ? "" : "cat", // VTE must be disabled because it prevents the prompt command from executing // See https://wiki.gnome.org/Apps/Terminal/VTE diff --git a/src/integrations/terminal/__tests__/TerminalRegistry.test.ts b/src/integrations/terminal/__tests__/TerminalRegistry.test.ts index 3d691df945..283e5b73c4 100644 --- a/src/integrations/terminal/__tests__/TerminalRegistry.test.ts +++ b/src/integrations/terminal/__tests__/TerminalRegistry.test.ts @@ -3,6 +3,8 @@ import { Terminal } from "../Terminal" import { TerminalRegistry } from "../TerminalRegistry" +const PAGER = process.platform === "win32" ? "" : "cat" + // Mock vscode.window.createTerminal const mockCreateTerminal = jest.fn() @@ -29,7 +31,7 @@ describe("TerminalRegistry", () => { }) describe("createTerminal", () => { - it("creates terminal with PAGER set to cat", () => { + it("creates terminal with PAGER set appropriately for platform", () => { TerminalRegistry.createTerminal("/test/path", "vscode") expect(mockCreateTerminal).toHaveBeenCalledWith({ @@ -37,7 +39,7 @@ describe("TerminalRegistry", () => { name: "Roo Code", iconPath: expect.any(Object), env: { - PAGER: "cat", + PAGER, VTE_VERSION: "0", PROMPT_EOL_MARK: "", }, @@ -57,7 +59,7 @@ describe("TerminalRegistry", () => { name: "Roo Code", iconPath: expect.any(Object), env: { - PAGER: "cat", + PAGER, PROMPT_COMMAND: "sleep 0.05", VTE_VERSION: "0", PROMPT_EOL_MARK: "", @@ -79,7 +81,7 @@ describe("TerminalRegistry", () => { name: "Roo Code", iconPath: expect.any(Object), env: { - PAGER: "cat", + PAGER, VTE_VERSION: "0", PROMPT_EOL_MARK: "", ITERM_SHELL_INTEGRATION_INSTALLED: "Yes", @@ -100,7 +102,7 @@ describe("TerminalRegistry", () => { name: "Roo Code", iconPath: expect.any(Object), env: { - PAGER: "cat", + PAGER, VTE_VERSION: "0", PROMPT_EOL_MARK: "", POWERLEVEL9K_TERM_SHELL_INTEGRATION: "true", From 5fa2cd2055e8335514f6b5b51484d9124a08aa8c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 28 May 2025 15:07:17 -0400 Subject: [PATCH 041/104] Update contributors list (#4044) docs: update contributors list [skip ci] Co-authored-by: mrubens --- README.md | 60 ++++++++++++++++++++--------------------- locales/ca/README.md | 44 +++++++++++++++--------------- locales/de/README.md | 44 +++++++++++++++--------------- locales/es/README.md | 44 +++++++++++++++--------------- locales/fr/README.md | 44 +++++++++++++++--------------- locales/hi/README.md | 44 +++++++++++++++--------------- locales/it/README.md | 44 +++++++++++++++--------------- locales/ja/README.md | 44 +++++++++++++++--------------- locales/ko/README.md | 44 +++++++++++++++--------------- locales/nl/README.md | 44 +++++++++++++++--------------- locales/pl/README.md | 44 +++++++++++++++--------------- locales/pt-BR/README.md | 44 +++++++++++++++--------------- locales/ru/README.md | 44 +++++++++++++++--------------- locales/tr/README.md | 44 +++++++++++++++--------------- locales/vi/README.md | 44 +++++++++++++++--------------- locales/zh-CN/README.md | 44 +++++++++++++++--------------- locales/zh-TW/README.md | 44 +++++++++++++++--------------- 17 files changed, 382 insertions(+), 382 deletions(-) diff --git a/README.md b/README.md index f03668c692..e20f7dfbce 100644 --- a/README.md +++ b/README.md @@ -176,36 +176,36 @@ 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
| hannesrudolph
hannesrudolph
| KJ7LNW
KJ7LNW
| stea9499
stea9499
| canrobins13
canrobins13
| joemanley201
joemanley201
| -| System233
System233
| nissa-seru
nissa-seru
| jquanton
jquanton
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| -| punkpeye
punkpeye
| wkordalski
wkordalski
| monotykamary
monotykamary
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| elianiva
elianiva
| cannuri
cannuri
| -| feifei325
feifei325
| zhangtony239
zhangtony239
| qdaxb
qdaxb
| vigneshsubbiah16
vigneshsubbiah16
| shariqriazz
shariqriazz
| lloydchang
lloydchang
| -| sachasayan
sachasayan
| Szpadel
Szpadel
| dtrugman
dtrugman
| diarmidmackenzie
diarmidmackenzie
| psv2522
psv2522
| Premshay
Premshay
| -| lupuletic
lupuletic
| xyOz-dev
xyOz-dev
| pugazhendhi-m
pugazhendhi-m
| aheizi
aheizi
| PeterDaveHello
PeterDaveHello
| olweraltuve
olweraltuve
| -| jr
jr
| nbihan-mediware
nbihan-mediware
| RaySinner
RaySinner
| afshawnlotfi
afshawnlotfi
| emshvac
emshvac
| kyle-apex
kyle-apex
| -| pdecat
pdecat
| Lunchb0ne
Lunchb0ne
| vagadiya
vagadiya
| arthurauffray
arthurauffray
| upamune
upamune
| StevenTCramer
StevenTCramer
| -| sammcj
sammcj
| p12tic
p12tic
| noritaka1166
noritaka1166
| gtaylor
gtaylor
| ChuKhaLi
ChuKhaLi
| aitoroses
aitoroses
| -| ross
ross
| heyseth
heyseth
| taisukeoe
taisukeoe
| avtc
avtc
| dlab-anton
dlab-anton
| eonghk
eonghk
| -| ronyblum
ronyblum
| teddyOOXX
teddyOOXX
| vincentsong
vincentsong
| yongjer
yongjer
| SmartManoj
SmartManoj
| ashktn
ashktn
| -| franekp
franekp
| yt3trees
yt3trees
| benzntech
benzntech
| axkirillov
axkirillov
| anton-otee
anton-otee
| bramburn
bramburn
| -| hassoncs
hassoncs
| snoyiatk
snoyiatk
| GitlyHallows
GitlyHallows
| jcbdev
jcbdev
| Chenjiayuan195
Chenjiayuan195
| julionav
julionav
| -| SplittyDev
SplittyDev
| mdp
mdp
| napter
napter
| philfung
philfung
| GOODBOY008
GOODBOY008
| hatsu38
hatsu38
| -| hongzio
hongzio
| im47cn
im47cn
| shoopapa
shoopapa
| jwcraig
jwcraig
| kinandan
kinandan
| kohii
kohii
| -| lightrabbit
lightrabbit
| nevermorec
nevermorec
| dqroid
dqroid
| dairui1
dairui1
| bannzai
bannzai
| axmo
axmo
| -| asychin
asychin
| amittell
amittell
| Yoshino-Yukitaro
Yoshino-Yukitaro
| Yikai-Liao
Yikai-Liao
| zxdvd
zxdvd
| vladstudio
vladstudio
| -| NamesMT
NamesMT
| PretzelVector
PretzelVector
| zetaloop
zetaloop
| zeozeozeo
zeozeozeo
| cdlliuy
cdlliuy
| student20880
student20880
| -| slytechnical
slytechnical
| shohei-ihaya
shohei-ihaya
| shaybc
shaybc
| seedlord
seedlord
| samir-nimbly
samir-nimbly
| robertheadley
robertheadley
| -| refactorthis
refactorthis
| pokutuna
pokutuna
| philipnext
philipnext
| oprstchn
oprstchn
| nobu007
nobu007
| mosleyit
mosleyit
| -| moqimoqidea
moqimoqidea
| mlopezr
mlopezr
| mecab
mecab
| olup
olup
| DeXtroTip
DeXtroTip
| pfitz
pfitz
| -| celestial-vault
celestial-vault
| linegel
linegel
| dbasclpy
dbasclpy
| Deon588
Deon588
| dleen
dleen
| devxpain
devxpain
| -| chadgauth
chadgauth
| olearycrew
olearycrew
| bogdan0083
bogdan0083
| Atlogit
Atlogit
| atlasgong
atlasgong
| andreastempsch
andreastempsch
| -| alasano
alasano
| QuinsZouls
QuinsZouls
| HadesArchitect
HadesArchitect
| alarno
alarno
| adilhafeez
adilhafeez
| adamwlarson
adamwlarson
| -| AMHesch
AMHesch
| tmsjngx0
tmsjngx0
| tgfjt
tgfjt
| maekawataiki
maekawataiki
| SannidhyaSah
SannidhyaSah
| samsilveira
samsilveira
| -| mr-ryan-james
mr-ryan-james
| Ruakij
Ruakij
| 01Rian
01Rian
| RSO
RSO
| R-omk
R-omk
| Sarke
Sarke
| -| kvokka
kvokka
| ecmasx
ecmasx
| marvijo-code
marvijo-code
| mamertofabian
mamertofabian
| monkeyDluffy6017
monkeyDluffy6017
| libertyteeth
libertyteeth
| -| shtse8
shtse8
| ksze
ksze
| Jdo300
Jdo300
| hesara
hesara
| | | +| mrubens
mrubens
| saoudrizwan
saoudrizwan
| cte
cte
| samhvw8
samhvw8
| daniel-lxs
daniel-lxs
| a8trejo
a8trejo
| +| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| ColemanRoo
ColemanRoo
| hannesrudolph
hannesrudolph
| KJ7LNW
KJ7LNW
| stea9499
stea9499
| canrobins13
canrobins13
| joemanley201
joemanley201
| +| System233
System233
| nissa-seru
nissa-seru
| jquanton
jquanton
| NyxJae
NyxJae
| MuriloFP
MuriloFP
| d-oit
d-oit
| +| punkpeye
punkpeye
| wkordalski
wkordalski
| monotykamary
monotykamary
| Smartsheet-JB-Brown
Smartsheet-JB-Brown
| elianiva
elianiva
| sachasayan
sachasayan
| +| cannuri
cannuri
| feifei325
feifei325
| zhangtony239
zhangtony239
| qdaxb
qdaxb
| vigneshsubbiah16
vigneshsubbiah16
| shariqriazz
shariqriazz
| +| lloydchang
lloydchang
| Szpadel
Szpadel
| dtrugman
dtrugman
| diarmidmackenzie
diarmidmackenzie
| psv2522
psv2522
| Premshay
Premshay
| +| lupuletic
lupuletic
| xyOz-dev
xyOz-dev
| pugazhendhi-m
pugazhendhi-m
| aheizi
aheizi
| PeterDaveHello
PeterDaveHello
| olweraltuve
olweraltuve
| +| jr
jr
| nbihan-mediware
nbihan-mediware
| RaySinner
RaySinner
| afshawnlotfi
afshawnlotfi
| pdecat
pdecat
| kyle-apex
kyle-apex
| +| emshvac
emshvac
| ChuKhaLi
ChuKhaLi
| Lunchb0ne
Lunchb0ne
| vagadiya
vagadiya
| slytechnical
slytechnical
| arthurauffray
arthurauffray
| +| upamune
upamune
| StevenTCramer
StevenTCramer
| sammcj
sammcj
| p12tic
p12tic
| noritaka1166
noritaka1166
| gtaylor
gtaylor
| +| aitoroses
aitoroses
| anton-otee
anton-otee
| heyseth
heyseth
| taisukeoe
taisukeoe
| NamesMT
NamesMT
| avtc
avtc
| +| dlab-anton
dlab-anton
| eonghk
eonghk
| ronyblum
ronyblum
| teddyOOXX
teddyOOXX
| vincentsong
vincentsong
| yongjer
yongjer
| +| SmartManoj
SmartManoj
| ashktn
ashktn
| franekp
franekp
| yt3trees
yt3trees
| benzntech
benzntech
| axkirillov
axkirillov
| +| bramburn
bramburn
| hassoncs
hassoncs
| snoyiatk
snoyiatk
| GitlyHallows
GitlyHallows
| ross
ross
| philfung
philfung
| +| napter
napter
| mdp
mdp
| SplittyDev
SplittyDev
| jcbdev
jcbdev
| julionav
julionav
| Chenjiayuan195
Chenjiayuan195
| +| nevermorec
nevermorec
| kohii
kohii
| kinandan
kinandan
| jwcraig
jwcraig
| shoopapa
shoopapa
| im47cn
im47cn
| +| hongzio
hongzio
| hatsu38
hatsu38
| GOODBOY008
GOODBOY008
| dqroid
dqroid
| dairui1
dairui1
| bannzai
bannzai
| +| axmo
axmo
| asychin
asychin
| amittell
amittell
| Yoshino-Yukitaro
Yoshino-Yukitaro
| Yikai-Liao
Yikai-Liao
| zxdvd
zxdvd
| +| vladstudio
vladstudio
| tmsjngx0
tmsjngx0
| tgfjt
tgfjt
| PretzelVector
PretzelVector
| zetaloop
zetaloop
| zeozeozeo
zeozeozeo
| +| cdlliuy
cdlliuy
| student20880
student20880
| shohei-ihaya
shohei-ihaya
| shaybc
shaybc
| seedlord
seedlord
| samir-nimbly
samir-nimbly
| +| robertheadley
robertheadley
| refactorthis
refactorthis
| pokutuna
pokutuna
| philipnext
philipnext
| oprstchn
oprstchn
| nobu007
nobu007
| +| mosleyit
mosleyit
| moqimoqidea
moqimoqidea
| mlopezr
mlopezr
| mecab
mecab
| olup
olup
| lightrabbit
lightrabbit
| +| celestial-vault
celestial-vault
| linegel
linegel
| dbasclpy
dbasclpy
| Deon588
Deon588
| dleen
dleen
| devxpain
devxpain
| +| chrarnoldus
chrarnoldus
| chadgauth
chadgauth
| olearycrew
olearycrew
| bogdan0083
bogdan0083
| Atlogit
Atlogit
| atlasgong
atlasgong
| +| andreastempsch
andreastempsch
| alasano
alasano
| QuinsZouls
QuinsZouls
| HadesArchitect
HadesArchitect
| alarno
alarno
| nexon33
nexon33
| +| adilhafeez
adilhafeez
| adamwlarson
adamwlarson
| AMHesch
AMHesch
| maekawataiki
maekawataiki
| SannidhyaSah
SannidhyaSah
| samsilveira
samsilveira
| +| mr-ryan-james
mr-ryan-james
| Ruakij
Ruakij
| 01Rian
01Rian
| RSO
RSO
| R-omk
R-omk
| Sarke
Sarke
| +| kvokka
kvokka
| ecmasx
ecmasx
| marvijo-code
marvijo-code
| mamertofabian
mamertofabian
| monkeyDluffy6017
monkeyDluffy6017
| libertyteeth
libertyteeth
| +| shtse8
shtse8
| ksze
ksze
| Jdo300
Jdo300
| hesara
hesara
| DeXtroTip
DeXtroTip
| pfitz
pfitz
| diff --git a/locales/ca/README.md b/locales/ca/README.md index 9603cb2fec..9e946638f4 100644 --- a/locales/ca/README.md +++ b/locales/ca/README.md @@ -185,32 +185,32 @@ Gràcies a tots els nostres col·laboradors que han ajudat a millorar Roo Code! |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| -|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|sachasayan
sachasayan
| +|cannuri
cannuri
|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
| +|lloydchang
lloydchang
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| |lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| -|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| -|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| -|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| -|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| -|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| -|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| -|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| -|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| -|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| -|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| -|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|pdecat
pdecat
|kyle-apex
kyle-apex
| +|emshvac
emshvac
|ChuKhaLi
ChuKhaLi
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|slytechnical
slytechnical
|arthurauffray
arthurauffray
| +|upamune
upamune
|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
| +|aitoroses
aitoroses
|anton-otee
anton-otee
|heyseth
heyseth
|taisukeoe
taisukeoe
|NamesMT
NamesMT
|avtc
avtc
| +|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
| +|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
| +|bramburn
bramburn
|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|ross
ross
|philfung
philfung
| +|napter
napter
|mdp
mdp
|SplittyDev
SplittyDev
|jcbdev
jcbdev
|julionav
julionav
|Chenjiayuan195
Chenjiayuan195
| +|nevermorec
nevermorec
|kohii
kohii
|kinandan
kinandan
|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
| +|hongzio
hongzio
|hatsu38
hatsu38
|GOODBOY008
GOODBOY008
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
| +|axmo
axmo
|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
| +|vladstudio
vladstudio
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
| +|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
| +|robertheadley
robertheadley
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
| +|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
| |celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| -|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| -|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| -|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|chrarnoldus
chrarnoldus
|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
| +|andreastempsch
andreastempsch
|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|nexon33
nexon33
| +|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| |mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| |kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| -|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| ## Llicència diff --git a/locales/de/README.md b/locales/de/README.md index 93d1d040b2..f29f7a644b 100644 --- a/locales/de/README.md +++ b/locales/de/README.md @@ -185,32 +185,32 @@ Danke an alle unsere Mitwirkenden, die geholfen haben, Roo Code zu verbessern! |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| -|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|sachasayan
sachasayan
| +|cannuri
cannuri
|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
| +|lloydchang
lloydchang
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| |lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| -|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| -|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| -|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| -|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| -|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| -|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| -|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| -|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| -|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| -|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| -|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|pdecat
pdecat
|kyle-apex
kyle-apex
| +|emshvac
emshvac
|ChuKhaLi
ChuKhaLi
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|slytechnical
slytechnical
|arthurauffray
arthurauffray
| +|upamune
upamune
|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
| +|aitoroses
aitoroses
|anton-otee
anton-otee
|heyseth
heyseth
|taisukeoe
taisukeoe
|NamesMT
NamesMT
|avtc
avtc
| +|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
| +|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
| +|bramburn
bramburn
|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|ross
ross
|philfung
philfung
| +|napter
napter
|mdp
mdp
|SplittyDev
SplittyDev
|jcbdev
jcbdev
|julionav
julionav
|Chenjiayuan195
Chenjiayuan195
| +|nevermorec
nevermorec
|kohii
kohii
|kinandan
kinandan
|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
| +|hongzio
hongzio
|hatsu38
hatsu38
|GOODBOY008
GOODBOY008
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
| +|axmo
axmo
|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
| +|vladstudio
vladstudio
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
| +|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
| +|robertheadley
robertheadley
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
| +|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
| |celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| -|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| -|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| -|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|chrarnoldus
chrarnoldus
|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
| +|andreastempsch
andreastempsch
|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|nexon33
nexon33
| +|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| |mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| |kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| -|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| ## Lizenz diff --git a/locales/es/README.md b/locales/es/README.md index e3f0794935..b43ae9f317 100644 --- a/locales/es/README.md +++ b/locales/es/README.md @@ -185,32 +185,32 @@ Usamos [changesets](https://github.com/changesets/changesets) para versionar y p |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| -|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|sachasayan
sachasayan
| +|cannuri
cannuri
|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
| +|lloydchang
lloydchang
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| |lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| -|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| -|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| -|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| -|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| -|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| -|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| -|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| -|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| -|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| -|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| -|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|pdecat
pdecat
|kyle-apex
kyle-apex
| +|emshvac
emshvac
|ChuKhaLi
ChuKhaLi
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|slytechnical
slytechnical
|arthurauffray
arthurauffray
| +|upamune
upamune
|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
| +|aitoroses
aitoroses
|anton-otee
anton-otee
|heyseth
heyseth
|taisukeoe
taisukeoe
|NamesMT
NamesMT
|avtc
avtc
| +|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
| +|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
| +|bramburn
bramburn
|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|ross
ross
|philfung
philfung
| +|napter
napter
|mdp
mdp
|SplittyDev
SplittyDev
|jcbdev
jcbdev
|julionav
julionav
|Chenjiayuan195
Chenjiayuan195
| +|nevermorec
nevermorec
|kohii
kohii
|kinandan
kinandan
|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
| +|hongzio
hongzio
|hatsu38
hatsu38
|GOODBOY008
GOODBOY008
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
| +|axmo
axmo
|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
| +|vladstudio
vladstudio
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
| +|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
| +|robertheadley
robertheadley
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
| +|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
| |celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| -|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| -|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| -|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|chrarnoldus
chrarnoldus
|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
| +|andreastempsch
andreastempsch
|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|nexon33
nexon33
| +|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| |mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| |kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| -|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| ## Licencia diff --git a/locales/fr/README.md b/locales/fr/README.md index d62225c4fd..64d5143882 100644 --- a/locales/fr/README.md +++ b/locales/fr/README.md @@ -185,32 +185,32 @@ Merci à tous nos contributeurs qui ont aidé à améliorer Roo Code ! |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| -|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|sachasayan
sachasayan
| +|cannuri
cannuri
|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
| +|lloydchang
lloydchang
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| |lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| -|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| -|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| -|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| -|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| -|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| -|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| -|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| -|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| -|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| -|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| -|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|pdecat
pdecat
|kyle-apex
kyle-apex
| +|emshvac
emshvac
|ChuKhaLi
ChuKhaLi
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|slytechnical
slytechnical
|arthurauffray
arthurauffray
| +|upamune
upamune
|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
| +|aitoroses
aitoroses
|anton-otee
anton-otee
|heyseth
heyseth
|taisukeoe
taisukeoe
|NamesMT
NamesMT
|avtc
avtc
| +|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
| +|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
| +|bramburn
bramburn
|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|ross
ross
|philfung
philfung
| +|napter
napter
|mdp
mdp
|SplittyDev
SplittyDev
|jcbdev
jcbdev
|julionav
julionav
|Chenjiayuan195
Chenjiayuan195
| +|nevermorec
nevermorec
|kohii
kohii
|kinandan
kinandan
|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
| +|hongzio
hongzio
|hatsu38
hatsu38
|GOODBOY008
GOODBOY008
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
| +|axmo
axmo
|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
| +|vladstudio
vladstudio
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
| +|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
| +|robertheadley
robertheadley
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
| +|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
| |celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| -|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| -|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| -|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|chrarnoldus
chrarnoldus
|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
| +|andreastempsch
andreastempsch
|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|nexon33
nexon33
| +|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| |mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| |kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| -|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| ## Licence diff --git a/locales/hi/README.md b/locales/hi/README.md index 78cf479229..7260c5273a 100644 --- a/locales/hi/README.md +++ b/locales/hi/README.md @@ -185,32 +185,32 @@ Roo Code को बेहतर बनाने में मदद करने |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| -|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|sachasayan
sachasayan
| +|cannuri
cannuri
|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
| +|lloydchang
lloydchang
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| |lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| -|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| -|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| -|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| -|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| -|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| -|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| -|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| -|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| -|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| -|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| -|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|pdecat
pdecat
|kyle-apex
kyle-apex
| +|emshvac
emshvac
|ChuKhaLi
ChuKhaLi
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|slytechnical
slytechnical
|arthurauffray
arthurauffray
| +|upamune
upamune
|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
| +|aitoroses
aitoroses
|anton-otee
anton-otee
|heyseth
heyseth
|taisukeoe
taisukeoe
|NamesMT
NamesMT
|avtc
avtc
| +|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
| +|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
| +|bramburn
bramburn
|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|ross
ross
|philfung
philfung
| +|napter
napter
|mdp
mdp
|SplittyDev
SplittyDev
|jcbdev
jcbdev
|julionav
julionav
|Chenjiayuan195
Chenjiayuan195
| +|nevermorec
nevermorec
|kohii
kohii
|kinandan
kinandan
|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
| +|hongzio
hongzio
|hatsu38
hatsu38
|GOODBOY008
GOODBOY008
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
| +|axmo
axmo
|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
| +|vladstudio
vladstudio
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
| +|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
| +|robertheadley
robertheadley
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
| +|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
| |celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| -|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| -|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| -|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|chrarnoldus
chrarnoldus
|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
| +|andreastempsch
andreastempsch
|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|nexon33
nexon33
| +|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| |mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| |kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| -|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| ## लाइसेंस diff --git a/locales/it/README.md b/locales/it/README.md index 49d0380c75..29897d7e73 100644 --- a/locales/it/README.md +++ b/locales/it/README.md @@ -185,32 +185,32 @@ Grazie a tutti i nostri contributori che hanno aiutato a migliorare Roo Code! |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| -|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|sachasayan
sachasayan
| +|cannuri
cannuri
|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
| +|lloydchang
lloydchang
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| |lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| -|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| -|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| -|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| -|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| -|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| -|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| -|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| -|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| -|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| -|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| -|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|pdecat
pdecat
|kyle-apex
kyle-apex
| +|emshvac
emshvac
|ChuKhaLi
ChuKhaLi
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|slytechnical
slytechnical
|arthurauffray
arthurauffray
| +|upamune
upamune
|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
| +|aitoroses
aitoroses
|anton-otee
anton-otee
|heyseth
heyseth
|taisukeoe
taisukeoe
|NamesMT
NamesMT
|avtc
avtc
| +|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
| +|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
| +|bramburn
bramburn
|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|ross
ross
|philfung
philfung
| +|napter
napter
|mdp
mdp
|SplittyDev
SplittyDev
|jcbdev
jcbdev
|julionav
julionav
|Chenjiayuan195
Chenjiayuan195
| +|nevermorec
nevermorec
|kohii
kohii
|kinandan
kinandan
|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
| +|hongzio
hongzio
|hatsu38
hatsu38
|GOODBOY008
GOODBOY008
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
| +|axmo
axmo
|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
| +|vladstudio
vladstudio
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
| +|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
| +|robertheadley
robertheadley
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
| +|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
| |celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| -|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| -|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| -|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|chrarnoldus
chrarnoldus
|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
| +|andreastempsch
andreastempsch
|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|nexon33
nexon33
| +|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| |mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| |kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| -|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| ## Licenza diff --git a/locales/ja/README.md b/locales/ja/README.md index d49ef2eafa..d9305d54f0 100644 --- a/locales/ja/README.md +++ b/locales/ja/README.md @@ -185,32 +185,32 @@ Roo Codeの改善に貢献してくれたすべての貢献者に感謝します |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| -|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|sachasayan
sachasayan
| +|cannuri
cannuri
|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
| +|lloydchang
lloydchang
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| |lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| -|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| -|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| -|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| -|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| -|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| -|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| -|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| -|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| -|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| -|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| -|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|pdecat
pdecat
|kyle-apex
kyle-apex
| +|emshvac
emshvac
|ChuKhaLi
ChuKhaLi
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|slytechnical
slytechnical
|arthurauffray
arthurauffray
| +|upamune
upamune
|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
| +|aitoroses
aitoroses
|anton-otee
anton-otee
|heyseth
heyseth
|taisukeoe
taisukeoe
|NamesMT
NamesMT
|avtc
avtc
| +|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
| +|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
| +|bramburn
bramburn
|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|ross
ross
|philfung
philfung
| +|napter
napter
|mdp
mdp
|SplittyDev
SplittyDev
|jcbdev
jcbdev
|julionav
julionav
|Chenjiayuan195
Chenjiayuan195
| +|nevermorec
nevermorec
|kohii
kohii
|kinandan
kinandan
|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
| +|hongzio
hongzio
|hatsu38
hatsu38
|GOODBOY008
GOODBOY008
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
| +|axmo
axmo
|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
| +|vladstudio
vladstudio
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
| +|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
| +|robertheadley
robertheadley
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
| +|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
| |celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| -|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| -|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| -|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|chrarnoldus
chrarnoldus
|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
| +|andreastempsch
andreastempsch
|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|nexon33
nexon33
| +|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| |mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| |kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| -|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| ## ライセンス diff --git a/locales/ko/README.md b/locales/ko/README.md index f3de3e1b78..1cb6f8c10a 100644 --- a/locales/ko/README.md +++ b/locales/ko/README.md @@ -185,32 +185,32 @@ Roo Code를 더 좋게 만드는 데 도움을 준 모든 기여자에게 감사 |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| -|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|sachasayan
sachasayan
| +|cannuri
cannuri
|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
| +|lloydchang
lloydchang
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| |lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| -|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| -|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| -|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| -|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| -|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| -|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| -|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| -|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| -|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| -|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| -|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|pdecat
pdecat
|kyle-apex
kyle-apex
| +|emshvac
emshvac
|ChuKhaLi
ChuKhaLi
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|slytechnical
slytechnical
|arthurauffray
arthurauffray
| +|upamune
upamune
|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
| +|aitoroses
aitoroses
|anton-otee
anton-otee
|heyseth
heyseth
|taisukeoe
taisukeoe
|NamesMT
NamesMT
|avtc
avtc
| +|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
| +|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
| +|bramburn
bramburn
|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|ross
ross
|philfung
philfung
| +|napter
napter
|mdp
mdp
|SplittyDev
SplittyDev
|jcbdev
jcbdev
|julionav
julionav
|Chenjiayuan195
Chenjiayuan195
| +|nevermorec
nevermorec
|kohii
kohii
|kinandan
kinandan
|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
| +|hongzio
hongzio
|hatsu38
hatsu38
|GOODBOY008
GOODBOY008
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
| +|axmo
axmo
|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
| +|vladstudio
vladstudio
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
| +|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
| +|robertheadley
robertheadley
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
| +|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
| |celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| -|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| -|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| -|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|chrarnoldus
chrarnoldus
|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
| +|andreastempsch
andreastempsch
|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|nexon33
nexon33
| +|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| |mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| |kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| -|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| ## 라이선스 diff --git a/locales/nl/README.md b/locales/nl/README.md index 88a023c1c4..603e62583c 100644 --- a/locales/nl/README.md +++ b/locales/nl/README.md @@ -186,32 +186,32 @@ Dank aan alle bijdragers die Roo Code beter hebben gemaakt! |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| -|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|sachasayan
sachasayan
| +|cannuri
cannuri
|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
| +|lloydchang
lloydchang
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| |lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| -|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| -|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| -|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| -|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| -|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| -|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| -|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| -|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| -|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| -|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| -|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|pdecat
pdecat
|kyle-apex
kyle-apex
| +|emshvac
emshvac
|ChuKhaLi
ChuKhaLi
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|slytechnical
slytechnical
|arthurauffray
arthurauffray
| +|upamune
upamune
|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
| +|aitoroses
aitoroses
|anton-otee
anton-otee
|heyseth
heyseth
|taisukeoe
taisukeoe
|NamesMT
NamesMT
|avtc
avtc
| +|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
| +|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
| +|bramburn
bramburn
|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|ross
ross
|philfung
philfung
| +|napter
napter
|mdp
mdp
|SplittyDev
SplittyDev
|jcbdev
jcbdev
|julionav
julionav
|Chenjiayuan195
Chenjiayuan195
| +|nevermorec
nevermorec
|kohii
kohii
|kinandan
kinandan
|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
| +|hongzio
hongzio
|hatsu38
hatsu38
|GOODBOY008
GOODBOY008
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
| +|axmo
axmo
|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
| +|vladstudio
vladstudio
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
| +|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
| +|robertheadley
robertheadley
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
| +|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
| |celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| -|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| -|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| -|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|chrarnoldus
chrarnoldus
|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
| +|andreastempsch
andreastempsch
|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|nexon33
nexon33
| +|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| |mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| |kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| -|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| ## Licentie diff --git a/locales/pl/README.md b/locales/pl/README.md index 7250673fe4..013ca3982e 100644 --- a/locales/pl/README.md +++ b/locales/pl/README.md @@ -185,32 +185,32 @@ Dziękujemy wszystkim naszym współtwórcom, którzy pomogli ulepszyć Roo Code |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| -|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|sachasayan
sachasayan
| +|cannuri
cannuri
|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
| +|lloydchang
lloydchang
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| |lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| -|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| -|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| -|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| -|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| -|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| -|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| -|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| -|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| -|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| -|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| -|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|pdecat
pdecat
|kyle-apex
kyle-apex
| +|emshvac
emshvac
|ChuKhaLi
ChuKhaLi
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|slytechnical
slytechnical
|arthurauffray
arthurauffray
| +|upamune
upamune
|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
| +|aitoroses
aitoroses
|anton-otee
anton-otee
|heyseth
heyseth
|taisukeoe
taisukeoe
|NamesMT
NamesMT
|avtc
avtc
| +|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
| +|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
| +|bramburn
bramburn
|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|ross
ross
|philfung
philfung
| +|napter
napter
|mdp
mdp
|SplittyDev
SplittyDev
|jcbdev
jcbdev
|julionav
julionav
|Chenjiayuan195
Chenjiayuan195
| +|nevermorec
nevermorec
|kohii
kohii
|kinandan
kinandan
|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
| +|hongzio
hongzio
|hatsu38
hatsu38
|GOODBOY008
GOODBOY008
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
| +|axmo
axmo
|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
| +|vladstudio
vladstudio
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
| +|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
| +|robertheadley
robertheadley
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
| +|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
| |celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| -|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| -|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| -|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|chrarnoldus
chrarnoldus
|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
| +|andreastempsch
andreastempsch
|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|nexon33
nexon33
| +|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| |mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| |kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| -|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| ## Licencja diff --git a/locales/pt-BR/README.md b/locales/pt-BR/README.md index b135f69cab..2c55f1d31b 100644 --- a/locales/pt-BR/README.md +++ b/locales/pt-BR/README.md @@ -185,32 +185,32 @@ Obrigado a todos os nossos contribuidores que ajudaram a tornar o Roo Code melho |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| -|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|sachasayan
sachasayan
| +|cannuri
cannuri
|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
| +|lloydchang
lloydchang
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| |lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| -|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| -|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| -|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| -|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| -|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| -|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| -|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| -|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| -|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| -|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| -|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|pdecat
pdecat
|kyle-apex
kyle-apex
| +|emshvac
emshvac
|ChuKhaLi
ChuKhaLi
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|slytechnical
slytechnical
|arthurauffray
arthurauffray
| +|upamune
upamune
|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
| +|aitoroses
aitoroses
|anton-otee
anton-otee
|heyseth
heyseth
|taisukeoe
taisukeoe
|NamesMT
NamesMT
|avtc
avtc
| +|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
| +|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
| +|bramburn
bramburn
|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|ross
ross
|philfung
philfung
| +|napter
napter
|mdp
mdp
|SplittyDev
SplittyDev
|jcbdev
jcbdev
|julionav
julionav
|Chenjiayuan195
Chenjiayuan195
| +|nevermorec
nevermorec
|kohii
kohii
|kinandan
kinandan
|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
| +|hongzio
hongzio
|hatsu38
hatsu38
|GOODBOY008
GOODBOY008
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
| +|axmo
axmo
|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
| +|vladstudio
vladstudio
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
| +|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
| +|robertheadley
robertheadley
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
| +|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
| |celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| -|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| -|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| -|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|chrarnoldus
chrarnoldus
|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
| +|andreastempsch
andreastempsch
|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|nexon33
nexon33
| +|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| |mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| |kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| -|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| ## Licença diff --git a/locales/ru/README.md b/locales/ru/README.md index 3faabf6c8c..8ea26b92d3 100644 --- a/locales/ru/README.md +++ b/locales/ru/README.md @@ -187,32 +187,32 @@ code --install-extension bin/roo-cline-.vsix |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| -|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|sachasayan
sachasayan
| +|cannuri
cannuri
|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
| +|lloydchang
lloydchang
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| |lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| -|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| -|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| -|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| -|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| -|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| -|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| -|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| -|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| -|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| -|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| -|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|pdecat
pdecat
|kyle-apex
kyle-apex
| +|emshvac
emshvac
|ChuKhaLi
ChuKhaLi
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|slytechnical
slytechnical
|arthurauffray
arthurauffray
| +|upamune
upamune
|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
| +|aitoroses
aitoroses
|anton-otee
anton-otee
|heyseth
heyseth
|taisukeoe
taisukeoe
|NamesMT
NamesMT
|avtc
avtc
| +|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
| +|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
| +|bramburn
bramburn
|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|ross
ross
|philfung
philfung
| +|napter
napter
|mdp
mdp
|SplittyDev
SplittyDev
|jcbdev
jcbdev
|julionav
julionav
|Chenjiayuan195
Chenjiayuan195
| +|nevermorec
nevermorec
|kohii
kohii
|kinandan
kinandan
|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
| +|hongzio
hongzio
|hatsu38
hatsu38
|GOODBOY008
GOODBOY008
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
| +|axmo
axmo
|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
| +|vladstudio
vladstudio
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
| +|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
| +|robertheadley
robertheadley
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
| +|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
| |celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| -|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| -|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| -|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|chrarnoldus
chrarnoldus
|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
| +|andreastempsch
andreastempsch
|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|nexon33
nexon33
| +|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| |mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| |kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| -|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| ## Лицензия diff --git a/locales/tr/README.md b/locales/tr/README.md index 73ef665214..2d61df38bb 100644 --- a/locales/tr/README.md +++ b/locales/tr/README.md @@ -185,32 +185,32 @@ Roo Code'u daha iyi hale getirmeye yardımcı olan tüm katkıda bulunanlara te |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| -|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|sachasayan
sachasayan
| +|cannuri
cannuri
|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
| +|lloydchang
lloydchang
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| |lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| -|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| -|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| -|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| -|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| -|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| -|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| -|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| -|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| -|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| -|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| -|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|pdecat
pdecat
|kyle-apex
kyle-apex
| +|emshvac
emshvac
|ChuKhaLi
ChuKhaLi
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|slytechnical
slytechnical
|arthurauffray
arthurauffray
| +|upamune
upamune
|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
| +|aitoroses
aitoroses
|anton-otee
anton-otee
|heyseth
heyseth
|taisukeoe
taisukeoe
|NamesMT
NamesMT
|avtc
avtc
| +|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
| +|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
| +|bramburn
bramburn
|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|ross
ross
|philfung
philfung
| +|napter
napter
|mdp
mdp
|SplittyDev
SplittyDev
|jcbdev
jcbdev
|julionav
julionav
|Chenjiayuan195
Chenjiayuan195
| +|nevermorec
nevermorec
|kohii
kohii
|kinandan
kinandan
|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
| +|hongzio
hongzio
|hatsu38
hatsu38
|GOODBOY008
GOODBOY008
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
| +|axmo
axmo
|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
| +|vladstudio
vladstudio
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
| +|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
| +|robertheadley
robertheadley
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
| +|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
| |celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| -|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| -|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| -|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|chrarnoldus
chrarnoldus
|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
| +|andreastempsch
andreastempsch
|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|nexon33
nexon33
| +|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| |mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| |kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| -|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| ## Lisans diff --git a/locales/vi/README.md b/locales/vi/README.md index 9dc142992b..772724a43e 100644 --- a/locales/vi/README.md +++ b/locales/vi/README.md @@ -185,32 +185,32 @@ Cảm ơn tất cả những người đóng góp đã giúp cải thiện Roo C |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| -|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|sachasayan
sachasayan
| +|cannuri
cannuri
|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
| +|lloydchang
lloydchang
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| |lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| -|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| -|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| -|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| -|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| -|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| -|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| -|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| -|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| -|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| -|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| -|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|pdecat
pdecat
|kyle-apex
kyle-apex
| +|emshvac
emshvac
|ChuKhaLi
ChuKhaLi
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|slytechnical
slytechnical
|arthurauffray
arthurauffray
| +|upamune
upamune
|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
| +|aitoroses
aitoroses
|anton-otee
anton-otee
|heyseth
heyseth
|taisukeoe
taisukeoe
|NamesMT
NamesMT
|avtc
avtc
| +|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
| +|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
| +|bramburn
bramburn
|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|ross
ross
|philfung
philfung
| +|napter
napter
|mdp
mdp
|SplittyDev
SplittyDev
|jcbdev
jcbdev
|julionav
julionav
|Chenjiayuan195
Chenjiayuan195
| +|nevermorec
nevermorec
|kohii
kohii
|kinandan
kinandan
|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
| +|hongzio
hongzio
|hatsu38
hatsu38
|GOODBOY008
GOODBOY008
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
| +|axmo
axmo
|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
| +|vladstudio
vladstudio
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
| +|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
| +|robertheadley
robertheadley
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
| +|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
| |celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| -|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| -|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| -|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|chrarnoldus
chrarnoldus
|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
| +|andreastempsch
andreastempsch
|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|nexon33
nexon33
| +|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| |mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| |kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| -|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| ## Giấy Phép diff --git a/locales/zh-CN/README.md b/locales/zh-CN/README.md index 58cb5e3536..f6d02b1e4f 100644 --- a/locales/zh-CN/README.md +++ b/locales/zh-CN/README.md @@ -185,32 +185,32 @@ code --install-extension bin/roo-cline-.vsix |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| -|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|sachasayan
sachasayan
| +|cannuri
cannuri
|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
| +|lloydchang
lloydchang
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| |lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| -|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| -|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| -|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| -|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| -|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| -|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| -|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| -|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| -|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| -|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| -|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|pdecat
pdecat
|kyle-apex
kyle-apex
| +|emshvac
emshvac
|ChuKhaLi
ChuKhaLi
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|slytechnical
slytechnical
|arthurauffray
arthurauffray
| +|upamune
upamune
|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
| +|aitoroses
aitoroses
|anton-otee
anton-otee
|heyseth
heyseth
|taisukeoe
taisukeoe
|NamesMT
NamesMT
|avtc
avtc
| +|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
| +|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
| +|bramburn
bramburn
|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|ross
ross
|philfung
philfung
| +|napter
napter
|mdp
mdp
|SplittyDev
SplittyDev
|jcbdev
jcbdev
|julionav
julionav
|Chenjiayuan195
Chenjiayuan195
| +|nevermorec
nevermorec
|kohii
kohii
|kinandan
kinandan
|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
| +|hongzio
hongzio
|hatsu38
hatsu38
|GOODBOY008
GOODBOY008
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
| +|axmo
axmo
|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
| +|vladstudio
vladstudio
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
| +|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
| +|robertheadley
robertheadley
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
| +|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
| |celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| -|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| -|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| -|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|chrarnoldus
chrarnoldus
|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
| +|andreastempsch
andreastempsch
|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|nexon33
nexon33
| +|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| |mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| |kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| -|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| ## 许可证 diff --git a/locales/zh-TW/README.md b/locales/zh-TW/README.md index 55e2541f02..f005b7b7f8 100644 --- a/locales/zh-TW/README.md +++ b/locales/zh-TW/README.md @@ -186,32 +186,32 @@ code --install-extension bin/roo-cline-.vsix |:---:|:---:|:---:|:---:|:---:|:---:| |ColemanRoo
ColemanRoo
|hannesrudolph
hannesrudolph
|KJ7LNW
KJ7LNW
|stea9499
stea9499
|canrobins13
canrobins13
|joemanley201
joemanley201
| |System233
System233
|nissa-seru
nissa-seru
|jquanton
jquanton
|NyxJae
NyxJae
|MuriloFP
MuriloFP
|d-oit
d-oit
| -|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|cannuri
cannuri
| -|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
|lloydchang
lloydchang
| -|sachasayan
sachasayan
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| +|punkpeye
punkpeye
|wkordalski
wkordalski
|monotykamary
monotykamary
|Smartsheet-JB-Brown
Smartsheet-JB-Brown
|elianiva
elianiva
|sachasayan
sachasayan
| +|cannuri
cannuri
|feifei325
feifei325
|zhangtony239
zhangtony239
|qdaxb
qdaxb
|vigneshsubbiah16
vigneshsubbiah16
|shariqriazz
shariqriazz
| +|lloydchang
lloydchang
|Szpadel
Szpadel
|dtrugman
dtrugman
|diarmidmackenzie
diarmidmackenzie
|psv2522
psv2522
|Premshay
Premshay
| |lupuletic
lupuletic
|xyOz-dev
xyOz-dev
|pugazhendhi-m
pugazhendhi-m
|aheizi
aheizi
|PeterDaveHello
PeterDaveHello
|olweraltuve
olweraltuve
| -|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|emshvac
emshvac
|kyle-apex
kyle-apex
| -|pdecat
pdecat
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|arthurauffray
arthurauffray
|upamune
upamune
|StevenTCramer
StevenTCramer
| -|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
|ChuKhaLi
ChuKhaLi
|aitoroses
aitoroses
| -|ross
ross
|heyseth
heyseth
|taisukeoe
taisukeoe
|avtc
avtc
|dlab-anton
dlab-anton
|eonghk
eonghk
| -|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
|SmartManoj
SmartManoj
|ashktn
ashktn
| -|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
|anton-otee
anton-otee
|bramburn
bramburn
| -|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|jcbdev
jcbdev
|Chenjiayuan195
Chenjiayuan195
|julionav
julionav
| -|SplittyDev
SplittyDev
|mdp
mdp
|napter
napter
|philfung
philfung
|GOODBOY008
GOODBOY008
|hatsu38
hatsu38
| -|hongzio
hongzio
|im47cn
im47cn
|shoopapa
shoopapa
|jwcraig
jwcraig
|kinandan
kinandan
|kohii
kohii
| -|lightrabbit
lightrabbit
|nevermorec
nevermorec
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
|axmo
axmo
| -|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
|vladstudio
vladstudio
| -|NamesMT
NamesMT
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
|cdlliuy
cdlliuy
|student20880
student20880
| -|slytechnical
slytechnical
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
|robertheadley
robertheadley
| -|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
|mosleyit
mosleyit
| -|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| +|jr
jr
|nbihan-mediware
nbihan-mediware
|RaySinner
RaySinner
|afshawnlotfi
afshawnlotfi
|pdecat
pdecat
|kyle-apex
kyle-apex
| +|emshvac
emshvac
|ChuKhaLi
ChuKhaLi
|Lunchb0ne
Lunchb0ne
|vagadiya
vagadiya
|slytechnical
slytechnical
|arthurauffray
arthurauffray
| +|upamune
upamune
|StevenTCramer
StevenTCramer
|sammcj
sammcj
|p12tic
p12tic
|noritaka1166
noritaka1166
|gtaylor
gtaylor
| +|aitoroses
aitoroses
|anton-otee
anton-otee
|heyseth
heyseth
|taisukeoe
taisukeoe
|NamesMT
NamesMT
|avtc
avtc
| +|dlab-anton
dlab-anton
|eonghk
eonghk
|ronyblum
ronyblum
|teddyOOXX
teddyOOXX
|vincentsong
vincentsong
|yongjer
yongjer
| +|SmartManoj
SmartManoj
|ashktn
ashktn
|franekp
franekp
|yt3trees
yt3trees
|benzntech
benzntech
|axkirillov
axkirillov
| +|bramburn
bramburn
|hassoncs
hassoncs
|snoyiatk
snoyiatk
|GitlyHallows
GitlyHallows
|ross
ross
|philfung
philfung
| +|napter
napter
|mdp
mdp
|SplittyDev
SplittyDev
|jcbdev
jcbdev
|julionav
julionav
|Chenjiayuan195
Chenjiayuan195
| +|nevermorec
nevermorec
|kohii
kohii
|kinandan
kinandan
|jwcraig
jwcraig
|shoopapa
shoopapa
|im47cn
im47cn
| +|hongzio
hongzio
|hatsu38
hatsu38
|GOODBOY008
GOODBOY008
|dqroid
dqroid
|dairui1
dairui1
|bannzai
bannzai
| +|axmo
axmo
|asychin
asychin
|amittell
amittell
|Yoshino-Yukitaro
Yoshino-Yukitaro
|Yikai-Liao
Yikai-Liao
|zxdvd
zxdvd
| +|vladstudio
vladstudio
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|PretzelVector
PretzelVector
|zetaloop
zetaloop
|zeozeozeo
zeozeozeo
| +|cdlliuy
cdlliuy
|student20880
student20880
|shohei-ihaya
shohei-ihaya
|shaybc
shaybc
|seedlord
seedlord
|samir-nimbly
samir-nimbly
| +|robertheadley
robertheadley
|refactorthis
refactorthis
|pokutuna
pokutuna
|philipnext
philipnext
|oprstchn
oprstchn
|nobu007
nobu007
| +|mosleyit
mosleyit
|moqimoqidea
moqimoqidea
|mlopezr
mlopezr
|mecab
mecab
|olup
olup
|lightrabbit
lightrabbit
| |celestial-vault
celestial-vault
|linegel
linegel
|dbasclpy
dbasclpy
|Deon588
Deon588
|dleen
dleen
|devxpain
devxpain
| -|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
|andreastempsch
andreastempsch
| -|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
| -|AMHesch
AMHesch
|tmsjngx0
tmsjngx0
|tgfjt
tgfjt
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| +|chrarnoldus
chrarnoldus
|chadgauth
chadgauth
|olearycrew
olearycrew
|bogdan0083
bogdan0083
|Atlogit
Atlogit
|atlasgong
atlasgong
| +|andreastempsch
andreastempsch
|alasano
alasano
|QuinsZouls
QuinsZouls
|HadesArchitect
HadesArchitect
|alarno
alarno
|nexon33
nexon33
| +|adilhafeez
adilhafeez
|adamwlarson
adamwlarson
|AMHesch
AMHesch
|maekawataiki
maekawataiki
|SannidhyaSah
SannidhyaSah
|samsilveira
samsilveira
| |mr-ryan-james
mr-ryan-james
|Ruakij
Ruakij
|01Rian
01Rian
|RSO
RSO
|R-omk
R-omk
|Sarke
Sarke
| |kvokka
kvokka
|ecmasx
ecmasx
|marvijo-code
marvijo-code
|mamertofabian
mamertofabian
|monkeyDluffy6017
monkeyDluffy6017
|libertyteeth
libertyteeth
| -|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
| | | +|shtse8
shtse8
|ksze
ksze
|Jdo300
Jdo300
|hesara
hesara
|DeXtroTip
DeXtroTip
|pfitz
pfitz
| ## 授權 From 2e5a1a8e1d97812f318496a384ebbe2ed8b100b1 Mon Sep 17 00:00:00 2001 From: Canyon Robins Date: Wed, 28 May 2025 12:55:07 -0700 Subject: [PATCH 042/104] [Condense] Skip condense and show error if the context grows (#4061) * [Condense] Skip condense and show error if the context grows * update tests * changeset * nit: error should be nonempty * update translations * add more errors * add more test cases * update translations * pipe error back from truncate code * add condense_context_error ClineMessage * fixes * translations --- .changeset/spotty-steaks-brake.md | 5 + packages/types/src/message.ts | 1 + src/core/condense/__tests__/index.test.ts | 255 ++++++++++++++++-- src/core/condense/index.ts | 29 +- .../__tests__/sliding-window.test.ts | 7 +- src/core/sliding-window/index.ts | 12 +- src/core/task/Task.ts | 21 +- src/i18n/locales/ca/common.json | 7 +- src/i18n/locales/de/common.json | 7 +- src/i18n/locales/en/common.json | 7 +- src/i18n/locales/es/common.json | 7 +- src/i18n/locales/fr/common.json | 7 +- src/i18n/locales/hi/common.json | 7 +- src/i18n/locales/it/common.json | 7 +- src/i18n/locales/ja/common.json | 7 +- src/i18n/locales/ko/common.json | 7 +- src/i18n/locales/nl/common.json | 7 +- src/i18n/locales/pl/common.json | 7 +- src/i18n/locales/pt-BR/common.json | 7 +- src/i18n/locales/ru/common.json | 7 +- src/i18n/locales/tr/common.json | 7 +- src/i18n/locales/vi/common.json | 7 +- src/i18n/locales/zh-CN/common.json | 7 +- src/i18n/locales/zh-TW/common.json | 7 +- webview-ui/src/components/chat/ChatRow.tsx | 4 +- .../components/chat/ContextCondenseRow.tsx | 13 + webview-ui/src/i18n/locales/ca/chat.json | 1 + webview-ui/src/i18n/locales/de/chat.json | 1 + webview-ui/src/i18n/locales/en/chat.json | 1 + webview-ui/src/i18n/locales/es/chat.json | 1 + webview-ui/src/i18n/locales/fr/chat.json | 1 + webview-ui/src/i18n/locales/hi/chat.json | 1 + webview-ui/src/i18n/locales/it/chat.json | 1 + webview-ui/src/i18n/locales/ja/chat.json | 1 + webview-ui/src/i18n/locales/ko/chat.json | 1 + webview-ui/src/i18n/locales/nl/chat.json | 1 + webview-ui/src/i18n/locales/pl/chat.json | 1 + webview-ui/src/i18n/locales/pt-BR/chat.json | 1 + webview-ui/src/i18n/locales/ru/chat.json | 1 + webview-ui/src/i18n/locales/tr/chat.json | 1 + webview-ui/src/i18n/locales/vi/chat.json | 1 + webview-ui/src/i18n/locales/zh-CN/chat.json | 1 + webview-ui/src/i18n/locales/zh-TW/chat.json | 1 + 43 files changed, 430 insertions(+), 53 deletions(-) create mode 100644 .changeset/spotty-steaks-brake.md diff --git a/.changeset/spotty-steaks-brake.md b/.changeset/spotty-steaks-brake.md new file mode 100644 index 0000000000..47fa9afc09 --- /dev/null +++ b/.changeset/spotty-steaks-brake.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Skips condense operations if the context size grows & shows an error diff --git a/packages/types/src/message.ts b/packages/types/src/message.ts index e870e8d707..33c2b7a108 100644 --- a/packages/types/src/message.ts +++ b/packages/types/src/message.ts @@ -50,6 +50,7 @@ export const clineSays = [ "rooignore_error", "diff_error", "condense_context", + "condense_context_error", "codebase_search_result", ] as const diff --git a/src/core/condense/__tests__/index.test.ts b/src/core/condense/__tests__/index.test.ts index e1003dcdaf..e3b613f903 100644 --- a/src/core/condense/__tests__/index.test.ts +++ b/src/core/condense/__tests__/index.test.ts @@ -17,6 +17,7 @@ jest.mock("../../../services/telemetry/TelemetryService", () => ({ })) const taskId = "test-task-id" +const DEFAULT_PREV_CONTEXT_TOKENS = 1000 describe("getMessagesSinceLastSummary", () => { it("should return all messages when there is no summary", () => { @@ -115,11 +116,18 @@ describe("summarizeConversation", () => { { role: "assistant", content: "Hi there", ts: 2 }, ] - const result = await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt, taskId) + const result = await summarizeConversation( + messages, + mockApiHandler, + defaultSystemPrompt, + taskId, + DEFAULT_PREV_CONTEXT_TOKENS, + ) expect(result.messages).toEqual(messages) expect(result.cost).toBe(0) expect(result.summary).toBe("") expect(result.newContextTokens).toBeUndefined() + expect(result.error).toBeTruthy() // Error should be set for not enough messages expect(mockApiHandler.createMessage).not.toHaveBeenCalled() }) @@ -134,11 +142,18 @@ describe("summarizeConversation", () => { { role: "user", content: "Tell me more", ts: 7 }, ] - const result = await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt, taskId) + const result = await summarizeConversation( + messages, + mockApiHandler, + defaultSystemPrompt, + taskId, + DEFAULT_PREV_CONTEXT_TOKENS, + ) expect(result.messages).toEqual(messages) expect(result.cost).toBe(0) expect(result.summary).toBe("") expect(result.newContextTokens).toBeUndefined() + expect(result.error).toBeTruthy() // Error should be set for recent summary expect(mockApiHandler.createMessage).not.toHaveBeenCalled() }) @@ -153,7 +168,13 @@ describe("summarizeConversation", () => { { role: "user", content: "Tell me more", ts: 7 }, ] - const result = await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt, taskId) + const result = await summarizeConversation( + messages, + mockApiHandler, + defaultSystemPrompt, + taskId, + DEFAULT_PREV_CONTEXT_TOKENS, + ) // Check that the API was called correctly expect(mockApiHandler.createMessage).toHaveBeenCalled() @@ -177,9 +198,10 @@ describe("summarizeConversation", () => { expect(result.cost).toBe(0.05) expect(result.summary).toBe("This is a summary") expect(result.newContextTokens).toBe(250) // 150 output tokens + 100 from countTokens + expect(result.error).toBeUndefined() }) - it("should handle empty summary response", async () => { + it("should handle empty summary response and return error", async () => { // We need enough messages to trigger summarization const messages: ApiMessage[] = [ { role: "user", content: "Hello", ts: 1 }, @@ -191,11 +213,6 @@ describe("summarizeConversation", () => { { role: "user", content: "Tell me more", ts: 7 }, ] - // Mock console.warn before we call the function - const originalWarn = console.warn - const mockWarn = jest.fn() - console.warn = mockWarn - // Setup empty summary response with usage information const emptyStream = (async function* () { yield { type: "text" as const, text: "" } @@ -211,16 +228,20 @@ describe("summarizeConversation", () => { return messages.map(({ role, content }: { role: string; content: any }) => ({ role, content })) }) - const result = await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt, taskId) + const result = await summarizeConversation( + messages, + mockApiHandler, + defaultSystemPrompt, + taskId, + DEFAULT_PREV_CONTEXT_TOKENS, + ) // Should return original messages when summary is empty expect(result.messages).toEqual(messages) expect(result.cost).toBe(0.02) expect(result.summary).toBe("") - expect(mockWarn).toHaveBeenCalledWith("Received empty summary from API") - - // Restore console.warn - console.warn = originalWarn + expect(result.error).toBeTruthy() // Error should be set + expect(result.newContextTokens).toBeUndefined() }) it("should correctly format the request to the API", async () => { @@ -234,7 +255,7 @@ describe("summarizeConversation", () => { { role: "user", content: "Tell me more", ts: 7 }, ] - await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt, taskId) + await summarizeConversation(messages, mockApiHandler, defaultSystemPrompt, taskId, DEFAULT_PREV_CONTEXT_TOKENS) // Verify the final request message const expectedFinalMessage = { @@ -275,7 +296,13 @@ describe("summarizeConversation", () => { // Override the mock for this test mockApiHandler.createMessage = jest.fn().mockReturnValue(streamWithUsage) as any - const result = await summarizeConversation(messages, mockApiHandler, systemPrompt, taskId) + const result = await summarizeConversation( + messages, + mockApiHandler, + systemPrompt, + taskId, + DEFAULT_PREV_CONTEXT_TOKENS, + ) // Verify that countTokens was called with the correct messages including system prompt expect(mockApiHandler.countTokens).toHaveBeenCalled() @@ -284,6 +311,193 @@ describe("summarizeConversation", () => { expect(result.newContextTokens).toBe(300) // 200 output tokens + 100 from countTokens expect(result.cost).toBe(0.06) expect(result.summary).toBe("This is a summary with system prompt") + expect(result.error).toBeUndefined() + }) + + it("should return error when new context tokens >= previous context tokens", async () => { + const messages: ApiMessage[] = [ + { role: "user", content: "Hello", ts: 1 }, + { role: "assistant", content: "Hi there", ts: 2 }, + { role: "user", content: "How are you?", ts: 3 }, + { role: "assistant", content: "I'm good", ts: 4 }, + { role: "user", content: "What's new?", ts: 5 }, + { role: "assistant", content: "Not much", ts: 6 }, + { role: "user", content: "Tell me more", ts: 7 }, + ] + + // Create a stream that produces a summary + const streamWithLargeTokens = (async function* () { + yield { type: "text" as const, text: "This is a very long summary that uses many tokens" } + yield { type: "usage" as const, totalCost: 0.08, outputTokens: 500 } + })() + + // Override the mock for this test + mockApiHandler.createMessage = jest.fn().mockReturnValue(streamWithLargeTokens) as any + + // Mock countTokens to return a high value that when added to outputTokens (500) + // will be >= prevContextTokens (600) + mockApiHandler.countTokens = jest.fn().mockImplementation(() => Promise.resolve(200)) as any + + const prevContextTokens = 600 + const result = await summarizeConversation( + messages, + mockApiHandler, + defaultSystemPrompt, + taskId, + prevContextTokens, + ) + + // Should return original messages when context would grow + expect(result.messages).toEqual(messages) + expect(result.cost).toBe(0.08) + expect(result.summary).toBe("") + expect(result.error).toBeTruthy() // Error should be set + expect(result.newContextTokens).toBeUndefined() + }) + + it("should successfully summarize when new context tokens < previous context tokens", async () => { + const messages: ApiMessage[] = [ + { role: "user", content: "Hello", ts: 1 }, + { role: "assistant", content: "Hi there", ts: 2 }, + { role: "user", content: "How are you?", ts: 3 }, + { role: "assistant", content: "I'm good", ts: 4 }, + { role: "user", content: "What's new?", ts: 5 }, + { role: "assistant", content: "Not much", ts: 6 }, + { role: "user", content: "Tell me more", ts: 7 }, + ] + + // Create a stream that produces a summary with reasonable token count + const streamWithSmallTokens = (async function* () { + yield { type: "text" as const, text: "Concise summary" } + yield { type: "usage" as const, totalCost: 0.03, outputTokens: 50 } + })() + + // Override the mock for this test + mockApiHandler.createMessage = jest.fn().mockReturnValue(streamWithSmallTokens) as any + + // Mock countTokens to return a small value so total is < prevContextTokens + mockApiHandler.countTokens = jest.fn().mockImplementation(() => Promise.resolve(30)) as any + + const prevContextTokens = 200 + const result = await summarizeConversation( + messages, + mockApiHandler, + defaultSystemPrompt, + taskId, + prevContextTokens, + ) + + // Should successfully summarize + expect(result.messages.length).toBe(messages.length + 1) // Original + summary + expect(result.cost).toBe(0.03) + expect(result.summary).toBe("Concise summary") + expect(result.error).toBeUndefined() + expect(result.newContextTokens).toBe(80) // 50 output tokens + 30 from countTokens + expect(result.newContextTokens).toBeLessThan(prevContextTokens) + }) + + it("should return error when not enough messages to summarize", async () => { + const messages: ApiMessage[] = [{ role: "user", content: "Hello", ts: 1 }] + + const result = await summarizeConversation( + messages, + mockApiHandler, + defaultSystemPrompt, + taskId, + DEFAULT_PREV_CONTEXT_TOKENS, + ) + + // Should return original messages when not enough to summarize + expect(result.messages).toEqual(messages) + expect(result.cost).toBe(0) + expect(result.summary).toBe("") + expect(result.error).toBeTruthy() // Error should be set + expect(result.newContextTokens).toBeUndefined() + expect(mockApiHandler.createMessage).not.toHaveBeenCalled() + }) + + it("should return error when recent summary exists in kept messages", async () => { + const messages: ApiMessage[] = [ + { role: "user", content: "Hello", ts: 1 }, + { role: "assistant", content: "Hi there", ts: 2 }, + { role: "user", content: "How are you?", ts: 3 }, + { role: "assistant", content: "I'm good", ts: 4 }, + { role: "user", content: "What's new?", ts: 5 }, + { role: "assistant", content: "Recent summary", ts: 6, isSummary: true }, // Summary in last 3 messages + { role: "user", content: "Tell me more", ts: 7 }, + ] + + const result = await summarizeConversation( + messages, + mockApiHandler, + defaultSystemPrompt, + taskId, + DEFAULT_PREV_CONTEXT_TOKENS, + ) + + // Should return original messages when recent summary exists + expect(result.messages).toEqual(messages) + expect(result.cost).toBe(0) + expect(result.summary).toBe("") + expect(result.error).toBeTruthy() // Error should be set + expect(result.newContextTokens).toBeUndefined() + expect(mockApiHandler.createMessage).not.toHaveBeenCalled() + }) + + it("should return error when both condensing and main API handlers are invalid", async () => { + const messages: ApiMessage[] = [ + { role: "user", content: "Hello", ts: 1 }, + { role: "assistant", content: "Hi there", ts: 2 }, + { role: "user", content: "How are you?", ts: 3 }, + { role: "assistant", content: "I'm good", ts: 4 }, + { role: "user", content: "What's new?", ts: 5 }, + { role: "assistant", content: "Not much", ts: 6 }, + { role: "user", content: "Tell me more", ts: 7 }, + ] + + // Create invalid handlers (missing createMessage) + const invalidMainHandler = { + countTokens: jest.fn(), + getModel: jest.fn(), + // createMessage is missing + } as unknown as ApiHandler + + const invalidCondensingHandler = { + countTokens: jest.fn(), + getModel: jest.fn(), + // createMessage is missing + } as unknown as ApiHandler + + // Mock console.error to verify error message + const originalError = console.error + const mockError = jest.fn() + console.error = mockError + + const result = await summarizeConversation( + messages, + invalidMainHandler, + defaultSystemPrompt, + taskId, + DEFAULT_PREV_CONTEXT_TOKENS, + false, + undefined, + invalidCondensingHandler, + ) + + // Should return original messages when both handlers are invalid + expect(result.messages).toEqual(messages) + expect(result.cost).toBe(0) + expect(result.summary).toBe("") + expect(result.error).toBeTruthy() // Error should be set + expect(result.newContextTokens).toBeUndefined() + + // Verify error was logged + expect(mockError).toHaveBeenCalledWith( + expect.stringContaining("Main API handler is also invalid for condensing"), + ) + + // Restore console.error + console.error = originalError }) }) @@ -373,6 +587,7 @@ describe("summarizeConversation with custom settings", () => { mockMainApiHandler, defaultSystemPrompt, taskId, + DEFAULT_PREV_CONTEXT_TOKENS, false, customPrompt, ) @@ -393,6 +608,7 @@ describe("summarizeConversation with custom settings", () => { mockMainApiHandler, defaultSystemPrompt, taskId, + DEFAULT_PREV_CONTEXT_TOKENS, false, " ", // Empty custom prompt ) @@ -409,6 +625,7 @@ describe("summarizeConversation with custom settings", () => { mockMainApiHandler, defaultSystemPrompt, taskId, + DEFAULT_PREV_CONTEXT_TOKENS, false, undefined, // No custom prompt ) @@ -428,6 +645,7 @@ describe("summarizeConversation with custom settings", () => { mockMainApiHandler, defaultSystemPrompt, taskId, + DEFAULT_PREV_CONTEXT_TOKENS, false, undefined, mockCondensingApiHandler, @@ -447,6 +665,7 @@ describe("summarizeConversation with custom settings", () => { mockMainApiHandler, defaultSystemPrompt, taskId, + DEFAULT_PREV_CONTEXT_TOKENS, false, undefined, undefined, @@ -477,6 +696,7 @@ describe("summarizeConversation with custom settings", () => { mockMainApiHandler, defaultSystemPrompt, taskId, + DEFAULT_PREV_CONTEXT_TOKENS, false, undefined, invalidHandler, @@ -503,6 +723,7 @@ describe("summarizeConversation with custom settings", () => { mockMainApiHandler, defaultSystemPrompt, taskId, + DEFAULT_PREV_CONTEXT_TOKENS, false, "Custom prompt", ) @@ -525,6 +746,7 @@ describe("summarizeConversation with custom settings", () => { mockMainApiHandler, defaultSystemPrompt, taskId, + DEFAULT_PREV_CONTEXT_TOKENS, false, undefined, mockCondensingApiHandler, @@ -548,6 +770,7 @@ describe("summarizeConversation with custom settings", () => { mockMainApiHandler, defaultSystemPrompt, taskId, + DEFAULT_PREV_CONTEXT_TOKENS, true, // isAutomaticTrigger "Custom prompt", mockCondensingApiHandler, diff --git a/src/core/condense/index.ts b/src/core/condense/index.ts index 1a20184371..58a81f3d22 100644 --- a/src/core/condense/index.ts +++ b/src/core/condense/index.ts @@ -1,4 +1,5 @@ import Anthropic from "@anthropic-ai/sdk" +import { t } from "../../i18n" import { ApiHandler } from "../../api" import { ApiMessage } from "../task-persistence/apiMessages" import { maybeRemoveImageBlocks } from "../../api/transform/image-cleaning" @@ -51,6 +52,7 @@ export type SummarizeResponse = { summary: string // The summary text; empty string for no summary cost: number // The cost of the summarization operation newContextTokens?: number // The number of tokens in the context for the next API request + error?: string // Populated iff the operation fails: error message shown to the user on failure (see Task.ts) } /** @@ -70,6 +72,7 @@ export type SummarizeResponse = { * @param {ApiHandler} apiHandler - The API handler to use for token counting (fallback if condensingApiHandler not provided) * @param {string} systemPrompt - The system prompt for API requests (fallback if customCondensingPrompt not provided) * @param {string} taskId - The task ID for the conversation, used for telemetry + * @param {number} prevContextTokens - The number of tokens currently in the context, used to ensure we don't grow the context * @param {boolean} isAutomaticTrigger - Whether the summarization is triggered automatically * @param {string} customCondensingPrompt - Optional custom prompt to use for condensing * @param {ApiHandler} condensingApiHandler - Optional specific API handler to use for condensing @@ -80,6 +83,7 @@ export async function summarizeConversation( apiHandler: ApiHandler, systemPrompt: string, taskId: string, + prevContextTokens: number, isAutomaticTrigger?: boolean, customCondensingPrompt?: string, condensingApiHandler?: ApiHandler, @@ -93,13 +97,18 @@ export async function summarizeConversation( const response: SummarizeResponse = { messages, cost: 0, summary: "" } const messagesToSummarize = getMessagesSinceLastSummary(messages.slice(0, -N_MESSAGES_TO_KEEP)) if (messagesToSummarize.length <= 1) { - return response // Not enough messages to warrant a summary + const error = + messages.length <= N_MESSAGES_TO_KEEP + 1 + ? t("common:errors.condense_not_enough_messages") + : t("common:errors.condensed_recently") + return { ...response, error } } const keepMessages = messages.slice(-N_MESSAGES_TO_KEEP) // Check if there's a recent summary in the messages we're keeping const recentSummaryExists = keepMessages.some((message) => message.isSummary) if (recentSummaryExists) { - return response // We recently summarized these messages; it's too soon to summarize again. + const error = t("common:errors.condensed_recently") + return { ...response, error } } const finalRequestMessage: Anthropic.MessageParam = { role: "user", @@ -127,12 +136,8 @@ export async function summarizeConversation( // Consider throwing an error or returning a specific error response. console.error("Main API handler is also invalid for condensing. Cannot proceed.") // Return an appropriate error structure for SummarizeResponse - return { - messages, - summary: "", - cost: 0, - newContextTokens: 0, - } + const error = t("common:errors.condense_handler_invalid") + return { ...response, error } } } @@ -151,8 +156,8 @@ export async function summarizeConversation( } summary = summary.trim() if (summary.length === 0) { - console.warn("Received empty summary from API") - return { ...response, cost } + const error = t("common:errors.condense_failed") + return { ...response, cost, error } } const summaryMessage: ApiMessage = { role: "assistant", @@ -172,6 +177,10 @@ export async function summarizeConversation( typeof message.content === "string" ? [{ text: message.content, type: "text" as const }] : message.content, ) const newContextTokens = outputTokens + (await apiHandler.countTokens(contextBlocks)) + if (newContextTokens >= prevContextTokens) { + const error = t("common:errors.condense_context_grew") + return { ...response, cost, error } + } return { messages: newMessages, summary, cost, newContextTokens } } diff --git a/src/core/sliding-window/__tests__/sliding-window.test.ts b/src/core/sliding-window/__tests__/sliding-window.test.ts index 74bbdf0caa..e99e2ed61f 100644 --- a/src/core/sliding-window/__tests__/sliding-window.test.ts +++ b/src/core/sliding-window/__tests__/sliding-window.test.ts @@ -532,6 +532,7 @@ describe("truncateConversationIfNeeded", () => { mockApiHandler, "System prompt", taskId, + 70001, true, undefined, // customCondensingPrompt undefined, // condensingApiHandler @@ -551,11 +552,12 @@ describe("truncateConversationIfNeeded", () => { }) it("should fall back to truncateConversation when autoCondenseContext is true but summarization fails", async () => { - // Mock the summarizeConversation function to return empty summary + // Mock the summarizeConversation function to return an error const mockSummarizeResponse: condenseModule.SummarizeResponse = { messages: messages, // Original messages unchanged - summary: "", // Empty summary indicates failure + summary: "", // Empty summary cost: 0.01, + error: "Summarization failed", // Error indicates failure } const summarizeSpy = jest @@ -678,6 +680,7 @@ describe("truncateConversationIfNeeded", () => { mockApiHandler, "System prompt", taskId, + 60000, true, undefined, // customCondensingPrompt undefined, // condensingApiHandler diff --git a/src/core/sliding-window/index.ts b/src/core/sliding-window/index.ts index b883c97407..6a0b0f1b27 100644 --- a/src/core/sliding-window/index.ts +++ b/src/core/sliding-window/index.ts @@ -96,6 +96,8 @@ export async function truncateConversationIfNeeded({ customCondensingPrompt, condensingApiHandler, }: TruncateOptions): Promise { + let error: string | undefined + let cost = 0 // Calculate the maximum tokens reserved for response const reservedTokens = maxTokens || contextWindow * 0.2 @@ -122,11 +124,15 @@ export async function truncateConversationIfNeeded({ apiHandler, systemPrompt, taskId, + prevContextTokens, true, // automatic trigger customCondensingPrompt, condensingApiHandler, ) - if (result.summary) { + if (result.error) { + error = result.error + cost = result.cost + } else { return { ...result, prevContextTokens } } } @@ -135,8 +141,8 @@ export async function truncateConversationIfNeeded({ // Fall back to sliding window truncation if needed if (prevContextTokens > allowedTokens) { const truncatedMessages = truncateConversation(messages, 0.5, taskId) - return { messages: truncatedMessages, prevContextTokens, summary: "", cost: 0 } + return { messages: truncatedMessages, prevContextTokens, summary: "", cost, error } } // No truncation or condensation needed - return { messages, summary: "", cost: 0, prevContextTokens } + return { messages, summary: "", cost, prevContextTokens, error } } diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 6223f0fdfc..8165953e7d 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -509,26 +509,37 @@ export class Task extends EventEmitter { } } + const { contextTokens: prevContextTokens } = this.getTokenUsage() const { messages, summary, cost, newContextTokens = 0, + error, } = await summarizeConversation( this.apiConversationHistory, this.api, // Main API handler (fallback) systemPrompt, // Default summarization prompt (fallback) this.taskId, + prevContextTokens, false, // manual trigger customCondensingPrompt, // User's custom prompt condensingApiHandler, // Specific handler for condensing ) - if (!summary) { + if (error) { + this.say( + "condense_context_error", + error, + undefined /* images */, + false /* partial */, + undefined /* checkpoint */, + undefined /* progressStatus */, + { isNonInteractive: true } /* options */, + ) return } await this.overwriteApiConversationHistory(messages) - const { contextTokens } = this.getTokenUsage() - const contextCondense: ContextCondense = { summary, cost, newContextTokens, prevContextTokens: contextTokens } + const contextCondense: ContextCondense = { summary, cost, newContextTokens, prevContextTokens } await this.say( "condense_context", undefined /* text */, @@ -1598,7 +1609,9 @@ export class Task extends EventEmitter { if (truncateResult.messages !== this.apiConversationHistory) { await this.overwriteApiConversationHistory(truncateResult.messages) } - if (truncateResult.summary) { + if (truncateResult.error) { + await this.say("condense_context_error", truncateResult.error) + } else if (truncateResult.summary) { const { summary, cost, prevContextTokens, newContextTokens = 0 } = truncateResult const contextCondense: ContextCondense = { summary, cost, newContextTokens, prevContextTokens } await this.say( diff --git a/src/i18n/locales/ca/common.json b/src/i18n/locales/ca/common.json index 739972f996..195fe2dac8 100644 --- a/src/i18n/locales/ca/common.json +++ b/src/i18n/locales/ca/common.json @@ -57,7 +57,12 @@ "custom_storage_path_unusable": "La ruta d'emmagatzematge personalitzada \"{{path}}\" no és utilitzable, s'utilitzarà la ruta predeterminada", "cannot_access_path": "No es pot accedir a la ruta {{path}}: {{error}}", "settings_import_failed": "Ha fallat la importació de la configuració: {{error}}.", - "mistake_limit_guidance": "Això pot indicar un error en el procés de pensament del model o la incapacitat d'utilitzar una eina correctament, que es pot mitigar amb orientació de l'usuari (p. ex. \"Prova de dividir la tasca en passos més petits\")." + "mistake_limit_guidance": "Això pot indicar un error en el procés de pensament del model o la incapacitat d'utilitzar una eina correctament, que es pot mitigar amb orientació de l'usuari (p. ex. \"Prova de dividir la tasca en passos més petits\").", + "condense_failed": "Ha fallat la condensació del context", + "condense_not_enough_messages": "No hi ha prou missatges per condensar el context", + "condensed_recently": "El context s'ha condensat recentment; s'omet aquest intent", + "condense_handler_invalid": "El gestor de l'API per condensar el context no és vàlid", + "condense_context_grew": "La mida del context ha augmentat durant la condensació; s'omet aquest intent" }, "warnings": { "no_terminal_content": "No s'ha seleccionat contingut de terminal", diff --git a/src/i18n/locales/de/common.json b/src/i18n/locales/de/common.json index ec88125bdd..a9b39e3df1 100644 --- a/src/i18n/locales/de/common.json +++ b/src/i18n/locales/de/common.json @@ -53,7 +53,12 @@ "custom_storage_path_unusable": "Benutzerdefinierter Speicherpfad \"{{path}}\" ist nicht verwendbar, Standardpfad wird verwendet", "cannot_access_path": "Zugriff auf Pfad {{path}} nicht möglich: {{error}}", "settings_import_failed": "Fehler beim Importieren der Einstellungen: {{error}}.", - "mistake_limit_guidance": "Dies kann auf einen Fehler im Denkprozess des Modells oder die Unfähigkeit hinweisen, ein Tool richtig zu verwenden, was durch Benutzerführung behoben werden kann (z.B. \"Versuche, die Aufgabe in kleinere Schritte zu unterteilen\")." + "mistake_limit_guidance": "Dies kann auf einen Fehler im Denkprozess des Modells oder die Unfähigkeit hinweisen, ein Tool richtig zu verwenden, was durch Benutzerführung behoben werden kann (z.B. \"Versuche, die Aufgabe in kleinere Schritte zu unterteilen\").", + "condense_failed": "Fehler beim Verdichten des Kontexts", + "condense_not_enough_messages": "Nicht genügend Nachrichten zum Verdichten des Kontexts", + "condensed_recently": "Kontext wurde kürzlich verdichtet; dieser Versuch wird übersprungen", + "condense_handler_invalid": "API-Handler zum Verdichten des Kontexts ist ungültig", + "condense_context_grew": "Kontextgröße ist während der Verdichtung gewachsen; dieser Versuch wird übersprungen" }, "warnings": { "no_terminal_content": "Kein Terminal-Inhalt ausgewählt", diff --git a/src/i18n/locales/en/common.json b/src/i18n/locales/en/common.json index b4f69c23ab..de8ea1e11c 100644 --- a/src/i18n/locales/en/common.json +++ b/src/i18n/locales/en/common.json @@ -53,7 +53,12 @@ "cannot_access_path": "Cannot access path {{path}}: {{error}}", "failed_update_project_mcp": "Failed to update project MCP servers", "settings_import_failed": "Settings import failed: {{error}}.", - "mistake_limit_guidance": "This may indicate a failure in the model's thought process or inability to use a tool properly, which can be mitigated with some user guidance (e.g. \"Try breaking down the task into smaller steps\")." + "mistake_limit_guidance": "This may indicate a failure in the model's thought process or inability to use a tool properly, which can be mitigated with some user guidance (e.g. \"Try breaking down the task into smaller steps\").", + "condense_failed": "Failed to condense context", + "condense_not_enough_messages": "Not enough messages to condense context", + "condensed_recently": "Context was condensed recently; skipping this attempt", + "condense_handler_invalid": "API handler for condensing context is invalid", + "condense_context_grew": "Context size increased during condensing; skipping this attempt" }, "warnings": { "no_terminal_content": "No terminal content selected", diff --git a/src/i18n/locales/es/common.json b/src/i18n/locales/es/common.json index 47c97b717b..95285e8628 100644 --- a/src/i18n/locales/es/common.json +++ b/src/i18n/locales/es/common.json @@ -53,7 +53,12 @@ "custom_storage_path_unusable": "La ruta de almacenamiento personalizada \"{{path}}\" no es utilizable, se usará la ruta predeterminada", "cannot_access_path": "No se puede acceder a la ruta {{path}}: {{error}}", "settings_import_failed": "Error al importar la configuración: {{error}}.", - "mistake_limit_guidance": "Esto puede indicar un fallo en el proceso de pensamiento del modelo o la incapacidad de usar una herramienta correctamente, lo cual puede mitigarse con orientación del usuario (ej. \"Intenta dividir la tarea en pasos más pequeños\")." + "mistake_limit_guidance": "Esto puede indicar un fallo en el proceso de pensamiento del modelo o la incapacidad de usar una herramienta correctamente, lo cual puede mitigarse con orientación del usuario (ej. \"Intenta dividir la tarea en pasos más pequeños\").", + "condense_failed": "Error al condensar el contexto", + "condense_not_enough_messages": "No hay suficientes mensajes para condensar el contexto", + "condensed_recently": "El contexto se condensó recientemente; se omite este intento", + "condense_handler_invalid": "El manejador de API para condensar el contexto no es válido", + "condense_context_grew": "El tamaño del contexto aumentó durante la condensación; se omite este intento" }, "warnings": { "no_terminal_content": "No hay contenido de terminal seleccionado", diff --git a/src/i18n/locales/fr/common.json b/src/i18n/locales/fr/common.json index 23b3470a0c..c0ef1a5816 100644 --- a/src/i18n/locales/fr/common.json +++ b/src/i18n/locales/fr/common.json @@ -53,7 +53,12 @@ "custom_storage_path_unusable": "Le chemin de stockage personnalisé \"{{path}}\" est inutilisable, le chemin par défaut sera utilisé", "cannot_access_path": "Impossible d'accéder au chemin {{path}} : {{error}}", "settings_import_failed": "Échec de l'importation des paramètres : {{error}}", - "mistake_limit_guidance": "Cela peut indiquer un échec dans le processus de réflexion du modèle ou une incapacité à utiliser un outil correctement, ce qui peut être atténué avec des conseils de l'utilisateur (par ex. \"Essaie de diviser la tâche en étapes plus petites\")." + "mistake_limit_guidance": "Cela peut indiquer un échec dans le processus de réflexion du modèle ou une incapacité à utiliser un outil correctement, ce qui peut être atténué avec des conseils de l'utilisateur (par ex. \"Essaie de diviser la tâche en étapes plus petites\").", + "condense_failed": "Échec de la condensation du contexte", + "condense_not_enough_messages": "Pas assez de messages pour condenser le contexte", + "condensed_recently": "Le contexte a été condensé récemment ; cette tentative est ignorée", + "condense_handler_invalid": "Le gestionnaire d'API pour condenser le contexte est invalide", + "condense_context_grew": "La taille du contexte a augmenté pendant la condensation ; cette tentative est ignorée" }, "warnings": { "no_terminal_content": "Aucun contenu de terminal sélectionné", diff --git a/src/i18n/locales/hi/common.json b/src/i18n/locales/hi/common.json index 9f52fa4714..5718134f5b 100644 --- a/src/i18n/locales/hi/common.json +++ b/src/i18n/locales/hi/common.json @@ -53,7 +53,12 @@ "custom_storage_path_unusable": "कस्टम स्टोरेज पाथ \"{{path}}\" उपयोग योग्य नहीं है, डिफ़ॉल्ट पाथ का उपयोग किया जाएगा", "cannot_access_path": "पाथ {{path}} तक पहुंच नहीं पा रहे हैं: {{error}}", "settings_import_failed": "सेटिंग्स इम्पोर्ट करने में विफल: {{error}}।", - "mistake_limit_guidance": "यह मॉडल की सोच प्रक्रिया में विफलता या किसी टूल का सही उपयोग न कर पाने का संकेत हो सकता है, जिसे उपयोगकर्ता के मार्गदर्शन से ठीक किया जा सकता है (जैसे \"कार्य को छोटे चरणों में बांटने की कोशिश करें\")।" + "mistake_limit_guidance": "यह मॉडल की सोच प्रक्रिया में विफलता या किसी टूल का सही उपयोग न कर पाने का संकेत हो सकता है, जिसे उपयोगकर्ता के मार्गदर्शन से ठीक किया जा सकता है (जैसे \"कार्य को छोटे चरणों में बांटने की कोशिश करें\")।", + "condense_failed": "संदर्भ को संक्षिप्त करने में विफल", + "condense_not_enough_messages": "संदर्भ को संक्षिप्त करने के लिए पर्याप्त संदेश नहीं हैं", + "condensed_recently": "संदर्भ हाल ही में संक्षिप्त किया गया था; इस प्रयास को छोड़ा जा रहा है", + "condense_handler_invalid": "संदर्भ को संक्षिप्त करने के लिए API हैंडलर अमान्य है", + "condense_context_grew": "संक्षिप्तीकरण के दौरान संदर्भ का आकार बढ़ गया; इस प्रयास को छोड़ा जा रहा है" }, "warnings": { "no_terminal_content": "कोई टर्मिनल सामग्री चयनित नहीं", diff --git a/src/i18n/locales/it/common.json b/src/i18n/locales/it/common.json index c4da3cbfaa..ec754bd0df 100644 --- a/src/i18n/locales/it/common.json +++ b/src/i18n/locales/it/common.json @@ -53,7 +53,12 @@ "custom_storage_path_unusable": "Il percorso di archiviazione personalizzato \"{{path}}\" non è utilizzabile, verrà utilizzato il percorso predefinito", "cannot_access_path": "Impossibile accedere al percorso {{path}}: {{error}}", "settings_import_failed": "Importazione delle impostazioni fallita: {{error}}.", - "mistake_limit_guidance": "Questo può indicare un fallimento nel processo di pensiero del modello o l'incapacità di utilizzare correttamente uno strumento, che può essere mitigato con la guida dell'utente (ad es. \"Prova a suddividere l'attività in passaggi più piccoli\")." + "mistake_limit_guidance": "Questo può indicare un fallimento nel processo di pensiero del modello o l'incapacità di utilizzare correttamente uno strumento, che può essere mitigato con la guida dell'utente (ad es. \"Prova a suddividere l'attività in passaggi più piccoli\").", + "condense_failed": "Impossibile condensare il contesto", + "condense_not_enough_messages": "Non ci sono abbastanza messaggi per condensare il contesto", + "condensed_recently": "Il contesto è stato condensato di recente; questo tentativo viene saltato", + "condense_handler_invalid": "Il gestore API per condensare il contesto non è valido", + "condense_context_grew": "La dimensione del contesto è aumentata durante la condensazione; questo tentativo viene saltato" }, "warnings": { "no_terminal_content": "Nessun contenuto del terminale selezionato", diff --git a/src/i18n/locales/ja/common.json b/src/i18n/locales/ja/common.json index 30c2b754f4..b73d932e91 100644 --- a/src/i18n/locales/ja/common.json +++ b/src/i18n/locales/ja/common.json @@ -53,7 +53,12 @@ "custom_storage_path_unusable": "カスタムストレージパス \"{{path}}\" が使用できないため、デフォルトパスを使用します", "cannot_access_path": "パス {{path}} にアクセスできません:{{error}}", "settings_import_failed": "設定のインポートに失敗しました:{{error}}", - "mistake_limit_guidance": "これは、モデルの思考プロセスの失敗やツールを適切に使用できないことを示している可能性があり、ユーザーのガイダンスによって軽減できます(例:「タスクをより小さなステップに分割してみてください」)。" + "mistake_limit_guidance": "これは、モデルの思考プロセスの失敗やツールを適切に使用できないことを示している可能性があり、ユーザーのガイダンスによって軽減できます(例:「タスクをより小さなステップに分割してみてください」)。", + "condense_failed": "コンテキストの圧縮に失敗しました", + "condense_not_enough_messages": "コンテキストを圧縮するのに十分なメッセージがありません", + "condensed_recently": "コンテキストは最近圧縮されました;この試行をスキップします", + "condense_handler_invalid": "コンテキストを圧縮するためのAPIハンドラーが無効です", + "condense_context_grew": "圧縮中にコンテキストサイズが増加しました;この試行をスキップします" }, "warnings": { "no_terminal_content": "選択されたターミナルコンテンツがありません", diff --git a/src/i18n/locales/ko/common.json b/src/i18n/locales/ko/common.json index f90637a196..38c3c5a15c 100644 --- a/src/i18n/locales/ko/common.json +++ b/src/i18n/locales/ko/common.json @@ -53,7 +53,12 @@ "custom_storage_path_unusable": "사용자 지정 저장 경로 \"{{path}}\"를 사용할 수 없어 기본 경로를 사용합니다", "cannot_access_path": "경로 {{path}}에 접근할 수 없습니다: {{error}}", "settings_import_failed": "설정 가져오기 실패: {{error}}.", - "mistake_limit_guidance": "이는 모델의 사고 과정 실패나 도구를 제대로 사용하지 못하는 것을 나타낼 수 있으며, 사용자 가이드를 통해 완화할 수 있습니다 (예: \"작업을 더 작은 단계로 나누어 시도해보세요\")." + "mistake_limit_guidance": "이는 모델의 사고 과정 실패나 도구를 제대로 사용하지 못하는 것을 나타낼 수 있으며, 사용자 가이드를 통해 완화할 수 있습니다 (예: \"작업을 더 작은 단계로 나누어 시도해보세요\").", + "condense_failed": "컨텍스트 압축에 실패했습니다", + "condense_not_enough_messages": "컨텍스트를 압축할 메시지가 충분하지 않습니다", + "condensed_recently": "컨텍스트가 최근 압축되었습니다; 이 시도를 건너뜁니다", + "condense_handler_invalid": "컨텍스트 압축을 위한 API 핸들러가 유효하지 않습니다", + "condense_context_grew": "압축 중 컨텍스트 크기가 증가했습니다; 이 시도를 건너뜁니다" }, "warnings": { "no_terminal_content": "선택된 터미널 내용이 없습니다", diff --git a/src/i18n/locales/nl/common.json b/src/i18n/locales/nl/common.json index 395e0bfe75..285086575a 100644 --- a/src/i18n/locales/nl/common.json +++ b/src/i18n/locales/nl/common.json @@ -53,7 +53,12 @@ "cannot_access_path": "Kan pad {{path}} niet openen: {{error}}", "failed_update_project_mcp": "Bijwerken van project MCP-servers mislukt", "settings_import_failed": "Importeren van instellingen mislukt: {{error}}.", - "mistake_limit_guidance": "Dit kan duiden op een fout in het denkproces van het model of het onvermogen om een tool correct te gebruiken, wat kan worden verminderd met gebruikersbegeleiding (bijv. \"Probeer de taak op te delen in kleinere stappen\")." + "mistake_limit_guidance": "Dit kan duiden op een fout in het denkproces van het model of het onvermogen om een tool correct te gebruiken, wat kan worden verminderd met gebruikersbegeleiding (bijv. \"Probeer de taak op te delen in kleinere stappen\").", + "condense_failed": "Comprimeren van context mislukt", + "condense_not_enough_messages": "Niet genoeg berichten om context te comprimeren", + "condensed_recently": "Context is recent gecomprimeerd; deze poging wordt overgeslagen", + "condense_handler_invalid": "API-handler voor het comprimeren van context is ongeldig", + "condense_context_grew": "Contextgrootte nam toe tijdens comprimeren; deze poging wordt overgeslagen" }, "warnings": { "no_terminal_content": "Geen terminalinhoud geselecteerd", diff --git a/src/i18n/locales/pl/common.json b/src/i18n/locales/pl/common.json index f17cf06894..0fad576518 100644 --- a/src/i18n/locales/pl/common.json +++ b/src/i18n/locales/pl/common.json @@ -53,7 +53,12 @@ "custom_storage_path_unusable": "Niestandardowa ścieżka przechowywania \"{{path}}\" nie jest użyteczna, zostanie użyta domyślna ścieżka", "cannot_access_path": "Nie można uzyskać dostępu do ścieżki {{path}}: {{error}}", "settings_import_failed": "Nie udało się zaimportować ustawień: {{error}}.", - "mistake_limit_guidance": "To może wskazywać na błąd w procesie myślowym modelu lub niezdolność do prawidłowego użycia narzędzia, co można złagodzić poprzez wskazówki użytkownika (np. \"Spróbuj podzielić zadanie na mniejsze kroki\")." + "mistake_limit_guidance": "To może wskazywać na błąd w procesie myślowym modelu lub niezdolność do prawidłowego użycia narzędzia, co można złagodzić poprzez wskazówki użytkownika (np. \"Spróbuj podzielić zadanie na mniejsze kroki\").", + "condense_failed": "Nie udało się skondensować kontekstu", + "condense_not_enough_messages": "Za mało wiadomości do skondensowania kontekstu", + "condensed_recently": "Kontekst został niedawno skondensowany; pomijanie tej próby", + "condense_handler_invalid": "Nieprawidłowy handler API do kondensowania kontekstu", + "condense_context_grew": "Rozmiar kontekstu wzrósł podczas kondensacji; pomijanie tej próby" }, "warnings": { "no_terminal_content": "Nie wybrano zawartości terminala", diff --git a/src/i18n/locales/pt-BR/common.json b/src/i18n/locales/pt-BR/common.json index e0b71c0b0e..d97067daf6 100644 --- a/src/i18n/locales/pt-BR/common.json +++ b/src/i18n/locales/pt-BR/common.json @@ -57,7 +57,12 @@ "custom_storage_path_unusable": "O caminho de armazenamento personalizado \"{{path}}\" não pode ser usado, será usado o caminho padrão", "cannot_access_path": "Não é possível acessar o caminho {{path}}: {{error}}", "settings_import_failed": "Falha ao importar configurações: {{error}}", - "mistake_limit_guidance": "Isso pode indicar uma falha no processo de pensamento do modelo ou incapacidade de usar uma ferramenta adequadamente, o que pode ser mitigado com orientação do usuário (ex. \"Tente dividir a tarefa em etapas menores\")." + "mistake_limit_guidance": "Isso pode indicar uma falha no processo de pensamento do modelo ou incapacidade de usar uma ferramenta adequadamente, o que pode ser mitigado com orientação do usuário (ex. \"Tente dividir a tarefa em etapas menores\").", + "condense_failed": "Falha ao condensar o contexto", + "condense_not_enough_messages": "Não há mensagens suficientes para condensar o contexto", + "condensed_recently": "O contexto foi condensado recentemente; pulando esta tentativa", + "condense_handler_invalid": "O manipulador de API para condensar o contexto é inválido", + "condense_context_grew": "O tamanho do contexto aumentou durante a condensação; pulando esta tentativa" }, "warnings": { "no_terminal_content": "Nenhum conteúdo do terminal selecionado", diff --git a/src/i18n/locales/ru/common.json b/src/i18n/locales/ru/common.json index ba557059a8..f00422e26e 100644 --- a/src/i18n/locales/ru/common.json +++ b/src/i18n/locales/ru/common.json @@ -53,7 +53,12 @@ "cannot_access_path": "Невозможно получить доступ к пути {{path}}: {{error}}", "failed_update_project_mcp": "Не удалось обновить серверы проекта MCP", "settings_import_failed": "Не удалось импортировать настройки: {{error}}.", - "mistake_limit_guidance": "Это может указывать на сбой в процессе мышления модели или неспособность правильно использовать инструмент, что можно смягчить с помощью руководства пользователя (например, \"Попробуйте разбить задачу на более мелкие шаги\")." + "mistake_limit_guidance": "Это может указывать на сбой в процессе мышления модели или неспособность правильно использовать инструмент, что можно смягчить с помощью руководства пользователя (например, \"Попробуйте разбить задачу на более мелкие шаги\").", + "condense_failed": "Не удалось сжать контекст", + "condense_not_enough_messages": "Недостаточно сообщений для сжатия контекста", + "condensed_recently": "Контекст был недавно сжат; пропускаем эту попытку", + "condense_handler_invalid": "Обработчик API для сжатия контекста недействителен", + "condense_context_grew": "Размер контекста увеличился во время сжатия; пропускаем эту попытку" }, "warnings": { "no_terminal_content": "Не выбрано содержимое терминала", diff --git a/src/i18n/locales/tr/common.json b/src/i18n/locales/tr/common.json index 77af2295f7..c55e4faa2f 100644 --- a/src/i18n/locales/tr/common.json +++ b/src/i18n/locales/tr/common.json @@ -53,7 +53,12 @@ "custom_storage_path_unusable": "Özel depolama yolu \"{{path}}\" kullanılamıyor, varsayılan yol kullanılacak", "cannot_access_path": "{{path}} yoluna erişilemiyor: {{error}}", "settings_import_failed": "Ayarlar içe aktarılamadı: {{error}}.", - "mistake_limit_guidance": "Bu, modelin düşünce sürecindeki bir başarısızlığı veya bir aracı düzgün kullanamama durumunu gösterebilir, bu da kullanıcı rehberliği ile hafifletilebilir (örn. \"Görevi daha küçük adımlara bölmeyi deneyin\")." + "mistake_limit_guidance": "Bu, modelin düşünce sürecindeki bir başarısızlığı veya bir aracı düzgün kullanamama durumunu gösterebilir, bu da kullanıcı rehberliği ile hafifletilebilir (örn. \"Görevi daha küçük adımlara bölmeyi deneyin\").", + "condense_failed": "Bağlam sıkıştırılamadı", + "condense_not_enough_messages": "Bağlamı sıkıştırmak için yeterli mesaj yok", + "condensed_recently": "Bağlam yakın zamanda sıkıştırıldı; bu deneme atlanıyor", + "condense_handler_invalid": "Bağlamı sıkıştırmak için API işleyicisi geçersiz", + "condense_context_grew": "Sıkıştırma sırasında bağlam boyutu arttı; bu deneme atlanıyor" }, "warnings": { "no_terminal_content": "Seçili terminal içeriği yok", diff --git a/src/i18n/locales/vi/common.json b/src/i18n/locales/vi/common.json index 3919eb607e..b7efe4be2f 100644 --- a/src/i18n/locales/vi/common.json +++ b/src/i18n/locales/vi/common.json @@ -53,7 +53,12 @@ "custom_storage_path_unusable": "Đường dẫn lưu trữ tùy chỉnh \"{{path}}\" không thể sử dụng được, sẽ sử dụng đường dẫn mặc định", "cannot_access_path": "Không thể truy cập đường dẫn {{path}}: {{error}}", "settings_import_failed": "Nhập cài đặt thất bại: {{error}}.", - "mistake_limit_guidance": "Điều này có thể cho thấy sự thất bại trong quá trình suy nghĩ của mô hình hoặc không thể sử dụng công cụ đúng cách, có thể được giảm thiểu bằng hướng dẫn của người dùng (ví dụ: \"Hãy thử chia nhỏ nhiệm vụ thành các bước nhỏ hơn\")." + "mistake_limit_guidance": "Điều này có thể cho thấy sự thất bại trong quá trình suy nghĩ của mô hình hoặc không thể sử dụng công cụ đúng cách, có thể được giảm thiểu bằng hướng dẫn của người dùng (ví dụ: \"Hãy thử chia nhỏ nhiệm vụ thành các bước nhỏ hơn\").", + "condense_failed": "Không thể nén ngữ cảnh", + "condense_not_enough_messages": "Không đủ tin nhắn để nén ngữ cảnh", + "condensed_recently": "Ngữ cảnh đã được nén gần đây; bỏ qua lần thử này", + "condense_handler_invalid": "Trình xử lý API để nén ngữ cảnh không hợp lệ", + "condense_context_grew": "Kích thước ngữ cảnh tăng lên trong quá trình nén; bỏ qua lần thử này" }, "warnings": { "no_terminal_content": "Không có nội dung terminal được chọn", diff --git a/src/i18n/locales/zh-CN/common.json b/src/i18n/locales/zh-CN/common.json index e169355628..a88c81305b 100644 --- a/src/i18n/locales/zh-CN/common.json +++ b/src/i18n/locales/zh-CN/common.json @@ -53,7 +53,12 @@ "custom_storage_path_unusable": "自定义存储路径 \"{{path}}\" 不可用,将使用默认路径", "cannot_access_path": "无法访问路径 {{path}}:{{error}}", "settings_import_failed": "设置导入失败:{{error}}。", - "mistake_limit_guidance": "这可能表明模型思维过程失败或无法正确使用工具,可通过用户指导来缓解(例如\"尝试将任务分解为更小的步骤\")。" + "mistake_limit_guidance": "这可能表明模型思维过程失败或无法正确使用工具,可通过用户指导来缓解(例如\"尝试将任务分解为更小的步骤\")。", + "condense_failed": "压缩上下文失败", + "condense_not_enough_messages": "没有足够的对话来压缩上下文", + "condensed_recently": "上下文最近已压缩;跳过此次尝试", + "condense_handler_invalid": "压缩上下文的API处理程序无效", + "condense_context_grew": "压缩过程中上下文大小增加;跳过此次尝试" }, "warnings": { "no_terminal_content": "没有选择终端内容", diff --git a/src/i18n/locales/zh-TW/common.json b/src/i18n/locales/zh-TW/common.json index 2331f767d1..e7cff14c91 100644 --- a/src/i18n/locales/zh-TW/common.json +++ b/src/i18n/locales/zh-TW/common.json @@ -53,7 +53,12 @@ "custom_storage_path_unusable": "自訂儲存路徑 \"{{path}}\" 無法使用,將使用預設路徑", "cannot_access_path": "無法存取路徑 {{path}}:{{error}}", "settings_import_failed": "設定匯入失敗:{{error}}。", - "mistake_limit_guidance": "這可能表明模型思維過程失敗或無法正確使用工具,可透過使用者指導來緩解(例如「嘗試將工作分解為更小的步驟」)。" + "mistake_limit_guidance": "這可能表明模型思維過程失敗或無法正確使用工具,可透過使用者指導來緩解(例如「嘗試將工作分解為更小的步驟」)。", + "condense_failed": "壓縮上下文失敗", + "condense_not_enough_messages": "沒有足夠的訊息來壓縮上下文", + "condensed_recently": "上下文最近已壓縮;跳過此次嘗試", + "condense_handler_invalid": "壓縮上下文的 API 處理程式無效", + "condense_context_grew": "壓縮過程中上下文大小增加;跳過此次嘗試" }, "warnings": { "no_terminal_content": "沒有選擇終端機內容", diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index eaabb77e70..675eb2ba5e 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -35,7 +35,7 @@ import { Markdown } from "./Markdown" import { CommandExecution } from "./CommandExecution" import { CommandExecutionError } from "./CommandExecutionError" import { AutoApprovedRequestLimitWarning } from "./AutoApprovedRequestLimitWarning" -import { CondensingContextRow, ContextCondenseRow } from "./ContextCondenseRow" +import { CondenseContextErrorRow, CondensingContextRow, ContextCondenseRow } from "./ContextCondenseRow" import CodebaseSearchResultsDisplay from "./CodebaseSearchResultsDisplay" interface ChatRowProps { @@ -969,6 +969,8 @@ export const ChatRowContent = ({ return } return message.contextCondense ? : null + case "condense_context_error": + return case "codebase_search_result": let parsed: { content: { diff --git a/webview-ui/src/components/chat/ContextCondenseRow.tsx b/webview-ui/src/components/chat/ContextCondenseRow.tsx index 6a80208770..9664b03e00 100644 --- a/webview-ui/src/components/chat/ContextCondenseRow.tsx +++ b/webview-ui/src/components/chat/ContextCondenseRow.tsx @@ -59,3 +59,16 @@ export const CondensingContextRow = () => { ) } + +export const CondenseContextErrorRow = ({ errorText }: { errorText?: string }) => { + const { t } = useTranslation() + return ( +
+
+ + {t("chat:contextCondense.errorHeader")} +
+ {errorText} +
+ ) +} diff --git a/webview-ui/src/i18n/locales/ca/chat.json b/webview-ui/src/i18n/locales/ca/chat.json index 3cb2874c61..4babea2510 100644 --- a/webview-ui/src/i18n/locales/ca/chat.json +++ b/webview-ui/src/i18n/locales/ca/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "Context condensat", "condensing": "Condensant context...", + "errorHeader": "Error en condensar el context", "tokens": "tokens" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/de/chat.json b/webview-ui/src/i18n/locales/de/chat.json index ca2bb62b20..1a0aaa4cd7 100644 --- a/webview-ui/src/i18n/locales/de/chat.json +++ b/webview-ui/src/i18n/locales/de/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "Kontext komprimiert", "condensing": "Kontext wird komprimiert...", + "errorHeader": "Kontext konnte nicht komprimiert werden", "tokens": "Tokens" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index adc47fc7d4..c96e18b6c6 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -133,6 +133,7 @@ "contextCondense": { "title": "Context Condensed", "condensing": "Condensing context...", + "errorHeader": "Failed to condense context", "tokens": "tokens" }, "instructions": { diff --git a/webview-ui/src/i18n/locales/es/chat.json b/webview-ui/src/i18n/locales/es/chat.json index c05863f3dc..c8d5c4e25d 100644 --- a/webview-ui/src/i18n/locales/es/chat.json +++ b/webview-ui/src/i18n/locales/es/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "Contexto condensado", "condensing": "Condensando contexto...", + "errorHeader": "Error al condensar el contexto", "tokens": "tokens" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/fr/chat.json b/webview-ui/src/i18n/locales/fr/chat.json index 073da26ee2..753a86f614 100644 --- a/webview-ui/src/i18n/locales/fr/chat.json +++ b/webview-ui/src/i18n/locales/fr/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "Contexte condensé", "condensing": "Condensation du contexte...", + "errorHeader": "Échec de la condensation du contexte", "tokens": "tokens" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/hi/chat.json b/webview-ui/src/i18n/locales/hi/chat.json index 26c94b8574..beebba1157 100644 --- a/webview-ui/src/i18n/locales/hi/chat.json +++ b/webview-ui/src/i18n/locales/hi/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "संदर्भ संक्षिप्त किया गया", "condensing": "संदर्भ संघनित कर रहा है...", + "errorHeader": "संदर्भ संघनित करने में विफल", "tokens": "टोकन" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/it/chat.json b/webview-ui/src/i18n/locales/it/chat.json index 4208040b59..5795ef9a7f 100644 --- a/webview-ui/src/i18n/locales/it/chat.json +++ b/webview-ui/src/i18n/locales/it/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "Contesto condensato", "condensing": "Condensazione del contesto...", + "errorHeader": "Impossibile condensare il contesto", "tokens": "token" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/ja/chat.json b/webview-ui/src/i18n/locales/ja/chat.json index c09ed2bc99..a04f04f952 100644 --- a/webview-ui/src/i18n/locales/ja/chat.json +++ b/webview-ui/src/i18n/locales/ja/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "コンテキスト要約", "condensing": "コンテキストを圧縮中...", + "errorHeader": "コンテキストの圧縮に失敗しました", "tokens": "トークン" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/ko/chat.json b/webview-ui/src/i18n/locales/ko/chat.json index 6c2a6de5c7..4c0d7fc5b2 100644 --- a/webview-ui/src/i18n/locales/ko/chat.json +++ b/webview-ui/src/i18n/locales/ko/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "컨텍스트 요약됨", "condensing": "컨텍스트 압축 중...", + "errorHeader": "컨텍스트 압축 실패", "tokens": "토큰" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/nl/chat.json b/webview-ui/src/i18n/locales/nl/chat.json index b5217049aa..5d7eb16ba5 100644 --- a/webview-ui/src/i18n/locales/nl/chat.json +++ b/webview-ui/src/i18n/locales/nl/chat.json @@ -212,6 +212,7 @@ "contextCondense": { "title": "Context samengevat", "condensing": "Context aan het samenvatten...", + "errorHeader": "Context samenvatten mislukt", "tokens": "tokens" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/pl/chat.json b/webview-ui/src/i18n/locales/pl/chat.json index a33c3126d8..2f42b32974 100644 --- a/webview-ui/src/i18n/locales/pl/chat.json +++ b/webview-ui/src/i18n/locales/pl/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "Kontekst skondensowany", "condensing": "Kondensowanie kontekstu...", + "errorHeader": "Nie udało się skondensować kontekstu", "tokens": "tokeny" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/pt-BR/chat.json b/webview-ui/src/i18n/locales/pt-BR/chat.json index f75634c45b..185b133ac2 100644 --- a/webview-ui/src/i18n/locales/pt-BR/chat.json +++ b/webview-ui/src/i18n/locales/pt-BR/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "Contexto condensado", "condensing": "Condensando contexto...", + "errorHeader": "Falha ao condensar contexto", "tokens": "tokens" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/ru/chat.json b/webview-ui/src/i18n/locales/ru/chat.json index 4c7ef08021..218c389c96 100644 --- a/webview-ui/src/i18n/locales/ru/chat.json +++ b/webview-ui/src/i18n/locales/ru/chat.json @@ -212,6 +212,7 @@ "contextCondense": { "title": "Контекст сжат", "condensing": "Сжатие контекста...", + "errorHeader": "Не удалось сжать контекст", "tokens": "токены" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/tr/chat.json b/webview-ui/src/i18n/locales/tr/chat.json index 98fde1efe7..94b0e86c7e 100644 --- a/webview-ui/src/i18n/locales/tr/chat.json +++ b/webview-ui/src/i18n/locales/tr/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "Bağlam Özetlendi", "condensing": "Bağlam yoğunlaştırılıyor...", + "errorHeader": "Bağlam yoğunlaştırılamadı", "tokens": "token" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/vi/chat.json b/webview-ui/src/i18n/locales/vi/chat.json index a6b661a1ae..2832380c93 100644 --- a/webview-ui/src/i18n/locales/vi/chat.json +++ b/webview-ui/src/i18n/locales/vi/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "Ngữ cảnh đã tóm tắt", "condensing": "Đang cô đọng ngữ cảnh...", + "errorHeader": "Không thể cô đọng ngữ cảnh", "tokens": "token" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/zh-CN/chat.json b/webview-ui/src/i18n/locales/zh-CN/chat.json index 80cae1e519..59a4f7200b 100644 --- a/webview-ui/src/i18n/locales/zh-CN/chat.json +++ b/webview-ui/src/i18n/locales/zh-CN/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "上下文已压缩", "condensing": "正在压缩上下文...", + "errorHeader": "上下文压缩失败", "tokens": "tokens" }, "followUpSuggest": { diff --git a/webview-ui/src/i18n/locales/zh-TW/chat.json b/webview-ui/src/i18n/locales/zh-TW/chat.json index 75b42c44e2..683f10fdf5 100644 --- a/webview-ui/src/i18n/locales/zh-TW/chat.json +++ b/webview-ui/src/i18n/locales/zh-TW/chat.json @@ -202,6 +202,7 @@ "contextCondense": { "title": "上下文已壓縮", "condensing": "正在壓縮上下文...", + "errorHeader": "上下文壓縮失敗", "tokens": "tokens" }, "followUpSuggest": { From 57038b75a0410cc6512149ba27c0dee4e3fc27fd Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Wed, 28 May 2025 23:07:03 +0200 Subject: [PATCH 043/104] Fix menu breaking when Roo is moved between primary and secondary sidebars (#4045) * Fix menu breaking when Roo is moved between primary and secondary sidebars Hello Roo Team! We changed this on the Kilo side and thought it might be useful to you! The menu buttons (Settings etc.) stop working when Roo is moved between the primary and secondary sidebars. This is because ClineProvider is prematurely disposed in that case This change prevents the ClineProvider from being disposed when hosted in a sidebar. It should still be disposed when hosted in a tab, because they have their own ClineProvider instance. Found while investigating https://github.com/Kilo-Org/kilocode/issues/502. * refactor: improve logging * fix: extra bracket --------- Co-authored-by: Daniel <57051444+daniel-lxs@users.noreply.github.com> --- src/core/webview/ClineProvider.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 13121531a7..932809202e 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -339,7 +339,8 @@ export class ClineProvider this.view = webviewView // Set panel reference according to webview type - if ("onDidChangeViewState" in webviewView) { + const inTabMode = "onDidChangeViewState" in webviewView + if (inTabMode) { // Tag page type setPanel(webviewView, "tab") } else if ("onDidChangeVisibility" in webviewView) { @@ -441,7 +442,12 @@ export class ClineProvider // This happens when the user closes the view or when the view is closed programmatically webviewView.onDidDispose( async () => { - await this.dispose() + if (inTabMode) { + this.log("Disposing ClineProvider instance for tab view") + await this.dispose() + } else { + this.log("Preserving ClineProvider instance for sidebar view reuse") + } }, null, this.disposables, From b7f606db34e6e896b4522567e1932e296ad1628c Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Wed, 28 May 2025 23:46:40 +0200 Subject: [PATCH 044/104] Improve POSIX shell compatibility in pre-push hook (#4053) Co-authored-by: Peter Dave Hello --- .husky/pre-push | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.husky/pre-push b/.husky/pre-push index ce9b06149e..3c206835b7 100644 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -22,7 +22,7 @@ $pnpm_cmd run check-types NEW_CHANGESETS=$(find .changeset -name "*.md" ! -name "README.md" | wc -l | tr -d ' ') echo "Changeset files: $NEW_CHANGESETS" -if [ "$NEW_CHANGESETS" == "0" ]; then +if [ "$NEW_CHANGESETS" = "0" ]; then echo "-------------------------------------------------------------------------------------" echo "Changes detected. Please run 'pnpm changeset' to create a changeset if applicable." echo "-------------------------------------------------------------------------------------" From 12669e1e64cfe2afac78cf7504e4745fcea5ffb8 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Wed, 28 May 2025 17:47:37 -0400 Subject: [PATCH 045/104] Turn Prompts tab into Modes tab and move support prompts to Settings (#4078) --- src/package.json | 2 +- src/package.nls.ca.json | 2 +- src/package.nls.de.json | 2 +- src/package.nls.es.json | 2 +- src/package.nls.fr.json | 2 +- src/package.nls.hi.json | 2 +- src/package.nls.it.json | 2 +- src/package.nls.ja.json | 2 +- src/package.nls.json | 2 +- src/package.nls.ko.json | 2 +- src/package.nls.nl.json | 2 +- src/package.nls.pl.json | 2 +- src/package.nls.pt-BR.json | 2 +- src/package.nls.ru.json | 2 +- src/package.nls.tr.json | 2 +- src/package.nls.vi.json | 2 +- src/package.nls.zh-CN.json | 2 +- src/package.nls.zh-TW.json | 2 +- .../__tests__/ShadowCheckpointService.test.ts | 18 +- src/services/mcp/McpHub.ts | 2 +- webview-ui/src/App.tsx | 8 +- webview-ui/src/__tests__/App.test.tsx | 6 +- .../PromptsView.tsx => modes/ModesView.tsx} | 174 +--------------- .../__tests__/ModesView.test.tsx} | 26 +-- .../components/settings/PromptsSettings.tsx | 193 ++++++++++++++++++ .../src/components/settings/SettingsView.tsx | 7 + webview-ui/src/i18n/locales/ca/prompts.json | 5 +- webview-ui/src/i18n/locales/ca/settings.json | 4 + webview-ui/src/i18n/locales/de/prompts.json | 5 +- webview-ui/src/i18n/locales/de/settings.json | 4 + webview-ui/src/i18n/locales/en/prompts.json | 5 +- webview-ui/src/i18n/locales/en/settings.json | 4 + webview-ui/src/i18n/locales/es/prompts.json | 5 +- webview-ui/src/i18n/locales/es/settings.json | 4 + webview-ui/src/i18n/locales/fr/prompts.json | 5 +- webview-ui/src/i18n/locales/fr/settings.json | 4 + webview-ui/src/i18n/locales/hi/prompts.json | 5 +- webview-ui/src/i18n/locales/hi/settings.json | 4 + webview-ui/src/i18n/locales/it/prompts.json | 5 +- webview-ui/src/i18n/locales/it/settings.json | 4 + webview-ui/src/i18n/locales/ja/prompts.json | 5 +- webview-ui/src/i18n/locales/ja/settings.json | 4 + webview-ui/src/i18n/locales/ko/prompts.json | 5 +- webview-ui/src/i18n/locales/ko/settings.json | 4 + webview-ui/src/i18n/locales/nl/prompts.json | 5 +- webview-ui/src/i18n/locales/nl/settings.json | 4 + webview-ui/src/i18n/locales/pl/prompts.json | 5 +- webview-ui/src/i18n/locales/pl/settings.json | 4 + .../src/i18n/locales/pt-BR/prompts.json | 5 +- .../src/i18n/locales/pt-BR/settings.json | 4 + webview-ui/src/i18n/locales/ru/prompts.json | 5 +- webview-ui/src/i18n/locales/ru/settings.json | 4 + webview-ui/src/i18n/locales/tr/prompts.json | 5 +- webview-ui/src/i18n/locales/tr/settings.json | 4 + webview-ui/src/i18n/locales/vi/prompts.json | 5 +- webview-ui/src/i18n/locales/vi/settings.json | 4 + .../src/i18n/locales/zh-CN/prompts.json | 5 +- .../src/i18n/locales/zh-CN/settings.json | 4 + .../src/i18n/locales/zh-TW/prompts.json | 5 +- .../src/i18n/locales/zh-TW/settings.json | 4 + 60 files changed, 364 insertions(+), 259 deletions(-) rename webview-ui/src/components/{prompts/PromptsView.tsx => modes/ModesView.tsx} (88%) rename webview-ui/src/components/{prompts/__tests__/PromptsView.test.tsx => modes/__tests__/ModesView.test.tsx} (87%) create mode 100644 webview-ui/src/components/settings/PromptsSettings.tsx diff --git a/src/package.json b/src/package.json index 599ff3abad..2453389f0a 100644 --- a/src/package.json +++ b/src/package.json @@ -83,7 +83,7 @@ { "command": "roo-cline.promptsButtonClicked", "title": "%command.prompts.title%", - "icon": "$(notebook)" + "icon": "$(organization)" }, { "command": "roo-cline.historyButtonClicked", diff --git a/src/package.nls.ca.json b/src/package.nls.ca.json index 91745efabf..7e1cb6f376 100644 --- a/src/package.nls.ca.json +++ b/src/package.nls.ca.json @@ -17,7 +17,7 @@ "views.contextMenu.label": "Roo Code", "views.terminalMenu.label": "Roo Code", "command.mcpServers.title": "Servidors MCP", - "command.prompts.title": "Indicacions", + "command.prompts.title": "Modes", "command.history.title": "Historial", "command.openInEditor.title": "Obrir a l'Editor", "command.settings.title": "Configuració", diff --git a/src/package.nls.de.json b/src/package.nls.de.json index 83c358a4b5..a967118150 100644 --- a/src/package.nls.de.json +++ b/src/package.nls.de.json @@ -17,7 +17,7 @@ "views.contextMenu.label": "Roo Code", "views.terminalMenu.label": "Roo Code", "command.mcpServers.title": "MCP Server", - "command.prompts.title": "Prompts", + "command.prompts.title": "Modi", "command.history.title": "Verlauf", "command.openInEditor.title": "Im Editor Öffnen", "command.settings.title": "Einstellungen", diff --git a/src/package.nls.es.json b/src/package.nls.es.json index a116a762a9..4f5734bdae 100644 --- a/src/package.nls.es.json +++ b/src/package.nls.es.json @@ -17,7 +17,7 @@ "views.contextMenu.label": "Roo Code", "views.terminalMenu.label": "Roo Code", "command.mcpServers.title": "Servidores MCP", - "command.prompts.title": "Indicaciones", + "command.prompts.title": "Modos", "command.history.title": "Historial", "command.openInEditor.title": "Abrir en Editor", "command.settings.title": "Configuración", diff --git a/src/package.nls.fr.json b/src/package.nls.fr.json index 55b56bf33c..34bdddc440 100644 --- a/src/package.nls.fr.json +++ b/src/package.nls.fr.json @@ -17,7 +17,7 @@ "views.contextMenu.label": "Roo Code", "views.terminalMenu.label": "Roo Code", "command.mcpServers.title": "Serveurs MCP", - "command.prompts.title": "Invites", + "command.prompts.title": "Modes", "command.history.title": "Historique", "command.openInEditor.title": "Ouvrir dans l'Éditeur", "command.settings.title": "Paramètres", diff --git a/src/package.nls.hi.json b/src/package.nls.hi.json index fdef15fff8..c9b7fd651b 100644 --- a/src/package.nls.hi.json +++ b/src/package.nls.hi.json @@ -17,7 +17,7 @@ "views.contextMenu.label": "Roo Code", "views.terminalMenu.label": "Roo Code", "command.mcpServers.title": "एमसीपी सर्वर", - "command.prompts.title": "प्रॉम्प्ट्स", + "command.prompts.title": "मोड्स", "command.history.title": "इतिहास", "command.openInEditor.title": "एडिटर में खोलें", "command.settings.title": "सेटिंग्स", diff --git a/src/package.nls.it.json b/src/package.nls.it.json index aa238eaae7..c3f875bff6 100644 --- a/src/package.nls.it.json +++ b/src/package.nls.it.json @@ -17,7 +17,7 @@ "views.contextMenu.label": "Roo Code", "views.terminalMenu.label": "Roo Code", "command.mcpServers.title": "Server MCP", - "command.prompts.title": "Prompt", + "command.prompts.title": "Modi", "command.history.title": "Cronologia", "command.openInEditor.title": "Apri nell'Editor", "command.settings.title": "Impostazioni", diff --git a/src/package.nls.ja.json b/src/package.nls.ja.json index cec6408ffd..fb76cd597b 100644 --- a/src/package.nls.ja.json +++ b/src/package.nls.ja.json @@ -6,7 +6,7 @@ "views.activitybar.title": "Roo Code", "command.newTask.title": "新しいタスク", "command.mcpServers.title": "MCPサーバー", - "command.prompts.title": "プロンプト", + "command.prompts.title": "モード", "command.history.title": "履歴", "command.openInEditor.title": "エディタで開く", "command.settings.title": "設定", diff --git a/src/package.nls.json b/src/package.nls.json index 4bcb49723a..b1ea6c1b58 100644 --- a/src/package.nls.json +++ b/src/package.nls.json @@ -6,7 +6,7 @@ "views.activitybar.title": "Roo Code", "command.newTask.title": "New Task", "command.mcpServers.title": "MCP Servers", - "command.prompts.title": "Prompts", + "command.prompts.title": "Modes", "command.history.title": "History", "command.openInEditor.title": "Open in Editor", "command.settings.title": "Settings", diff --git a/src/package.nls.ko.json b/src/package.nls.ko.json index 54d54a6709..9c090a03a9 100644 --- a/src/package.nls.ko.json +++ b/src/package.nls.ko.json @@ -17,7 +17,7 @@ "views.contextMenu.label": "Roo Code", "views.terminalMenu.label": "Roo Code", "command.mcpServers.title": "MCP 서버", - "command.prompts.title": "프롬프트", + "command.prompts.title": "모드", "command.history.title": "기록", "command.openInEditor.title": "에디터에서 열기", "command.settings.title": "설정", diff --git a/src/package.nls.nl.json b/src/package.nls.nl.json index 8cd0b0e71f..0e0e88f0b0 100644 --- a/src/package.nls.nl.json +++ b/src/package.nls.nl.json @@ -6,7 +6,7 @@ "views.activitybar.title": "Roo Code", "command.newTask.title": "Nieuwe Taak", "command.mcpServers.title": "MCP Servers", - "command.prompts.title": "Prompts", + "command.prompts.title": "Modi", "command.history.title": "Geschiedenis", "command.openInEditor.title": "Openen in Editor", "command.settings.title": "Instellingen", diff --git a/src/package.nls.pl.json b/src/package.nls.pl.json index c22b4e99e6..c17d30b1e0 100644 --- a/src/package.nls.pl.json +++ b/src/package.nls.pl.json @@ -17,7 +17,7 @@ "views.contextMenu.label": "Roo Code", "views.terminalMenu.label": "Roo Code", "command.mcpServers.title": "Serwery MCP", - "command.prompts.title": "Podpowiedzi", + "command.prompts.title": "Tryby", "command.history.title": "Historia", "command.openInEditor.title": "Otwórz w Edytorze", "command.settings.title": "Ustawienia", diff --git a/src/package.nls.pt-BR.json b/src/package.nls.pt-BR.json index 0b93b1fbfe..7bdd28595e 100644 --- a/src/package.nls.pt-BR.json +++ b/src/package.nls.pt-BR.json @@ -17,7 +17,7 @@ "views.contextMenu.label": "Roo Code", "views.terminalMenu.label": "Roo Code", "command.mcpServers.title": "Servidores MCP", - "command.prompts.title": "Prompts", + "command.prompts.title": "Modos", "command.history.title": "Histórico", "command.openInEditor.title": "Abrir no Editor", "command.settings.title": "Configurações", diff --git a/src/package.nls.ru.json b/src/package.nls.ru.json index ec122061a3..f484951102 100644 --- a/src/package.nls.ru.json +++ b/src/package.nls.ru.json @@ -6,7 +6,7 @@ "views.activitybar.title": "Roo Code", "command.newTask.title": "Новая задача", "command.mcpServers.title": "MCP серверы", - "command.prompts.title": "Промпты", + "command.prompts.title": "Режимы", "command.history.title": "История", "command.openInEditor.title": "Открыть в редакторе", "command.settings.title": "Настройки", diff --git a/src/package.nls.tr.json b/src/package.nls.tr.json index c980e90b91..b54776c1dd 100644 --- a/src/package.nls.tr.json +++ b/src/package.nls.tr.json @@ -17,7 +17,7 @@ "views.contextMenu.label": "Roo Code", "views.terminalMenu.label": "Roo Code", "command.mcpServers.title": "MCP Sunucuları", - "command.prompts.title": "Komut İstemleri", + "command.prompts.title": "Modlar", "command.history.title": "Geçmiş", "command.openInEditor.title": "Düzenleyicide Aç", "command.settings.title": "Ayarlar", diff --git a/src/package.nls.vi.json b/src/package.nls.vi.json index 34788bbef7..9cd3d9672c 100644 --- a/src/package.nls.vi.json +++ b/src/package.nls.vi.json @@ -17,7 +17,7 @@ "views.contextMenu.label": "Roo Code", "views.terminalMenu.label": "Roo Code", "command.mcpServers.title": "Máy Chủ MCP", - "command.prompts.title": "Lời Nhắc", + "command.prompts.title": "Chế Độ", "command.history.title": "Lịch Sử", "command.openInEditor.title": "Mở trong Trình Soạn Thảo", "command.settings.title": "Cài Đặt", diff --git a/src/package.nls.zh-CN.json b/src/package.nls.zh-CN.json index ac64f36bff..709cd9b32d 100644 --- a/src/package.nls.zh-CN.json +++ b/src/package.nls.zh-CN.json @@ -17,7 +17,7 @@ "views.contextMenu.label": "Roo Code", "views.terminalMenu.label": "Roo Code", "command.mcpServers.title": "MCP 服务器", - "command.prompts.title": "提示", + "command.prompts.title": "模式", "command.history.title": "历史记录", "command.openInEditor.title": "在编辑器中打开", "command.settings.title": "设置", diff --git a/src/package.nls.zh-TW.json b/src/package.nls.zh-TW.json index bb31058fc6..4a3a935539 100644 --- a/src/package.nls.zh-TW.json +++ b/src/package.nls.zh-TW.json @@ -17,7 +17,7 @@ "views.contextMenu.label": "Roo Code", "views.terminalMenu.label": "Roo Code", "command.mcpServers.title": "MCP 伺服器", - "command.prompts.title": "提示", + "command.prompts.title": "模式", "command.history.title": "歷史記錄", "command.openInEditor.title": "在編輯器中開啟", "command.settings.title": "設定", diff --git a/src/services/checkpoints/__tests__/ShadowCheckpointService.test.ts b/src/services/checkpoints/__tests__/ShadowCheckpointService.test.ts index ad155b36c3..9c1019f53a 100644 --- a/src/services/checkpoints/__tests__/ShadowCheckpointService.test.ts +++ b/src/services/checkpoints/__tests__/ShadowCheckpointService.test.ts @@ -658,7 +658,9 @@ describe.each([[RepoPerTaskCheckpointService, "RepoPerTaskCheckpointService"]])( it("creates checkpoint with changes regardless of allowEmpty setting", async () => { await fs.writeFile(testFile, "Modified content for allowEmpty test") - const resultWithAllowEmpty = await service.saveCheckpoint("With changes and allowEmpty", { allowEmpty: true }) + const resultWithAllowEmpty = await service.saveCheckpoint("With changes and allowEmpty", { + allowEmpty: true, + }) expect(resultWithAllowEmpty?.commit).toBeTruthy() await fs.writeFile(testFile, "Another modification for allowEmpty test") @@ -685,7 +687,7 @@ describe.each([[RepoPerTaskCheckpointService, "RepoPerTaskCheckpointService"]])( // First, create a checkpoint to ensure we're not in the initial state await fs.writeFile(testFile, "Setup content") await service.saveCheckpoint("Setup checkpoint") - + // Reset the file to original state await fs.writeFile(testFile, "Hello, world!") await service.saveCheckpoint("Reset to original") @@ -723,15 +725,15 @@ describe.each([[RepoPerTaskCheckpointService, "RepoPerTaskCheckpointService"]])( await testService.saveCheckpoint("Test logging with allowEmpty", { allowEmpty: true }) - const saveCheckpointLogs = logMessages.filter(msg => - msg.includes("starting checkpoint save") && msg.includes("allowEmpty: true") + const saveCheckpointLogs = logMessages.filter( + (msg) => msg.includes("starting checkpoint save") && msg.includes("allowEmpty: true"), ) expect(saveCheckpointLogs).toHaveLength(1) await testService.saveCheckpoint("Test logging without allowEmpty") - const defaultLogs = logMessages.filter(msg => - msg.includes("starting checkpoint save") && msg.includes("allowEmpty: false") + const defaultLogs = logMessages.filter( + (msg) => msg.includes("starting checkpoint save") && msg.includes("allowEmpty: false"), ) expect(defaultLogs).toHaveLength(1) }) @@ -776,7 +778,7 @@ describe.each([[RepoPerTaskCheckpointService, "RepoPerTaskCheckpointService"]])( // Get diff between regular commit and empty commit const diff = await service.getDiff({ from: beforeEmpty!.commit, - to: emptyCommit!.commit + to: emptyCommit!.commit, }) // Should have no differences since empty commit doesn't change anything @@ -802,7 +804,7 @@ describe.each([[RepoPerTaskCheckpointService, "RepoPerTaskCheckpointService"]])( // Restore to the new task checkpoint await service.restoreCheckpoint(newTaskCheckpoint!.commit) - + // File should be back to original state expect(await fs.readFile(testFile, "utf-8")).toBe("Hello, world!") }) diff --git a/src/services/mcp/McpHub.ts b/src/services/mcp/McpHub.ts index 1e8aabf1f5..e1009d9741 100644 --- a/src/services/mcp/McpHub.ts +++ b/src/services/mcp/McpHub.ts @@ -452,7 +452,7 @@ export class McpHub { args: configInjected.args, cwd: configInjected.cwd, env: { - ...(configInjected.env || {}), + ...(configInjected.env || {}), ...(process.env.PATH ? { PATH: process.env.PATH } : {}), ...(process.env.HOME ? { HOME: process.env.HOME } : {}), }, diff --git a/webview-ui/src/App.tsx b/webview-ui/src/App.tsx index 053c9f2456..b82d73a709 100644 --- a/webview-ui/src/App.tsx +++ b/webview-ui/src/App.tsx @@ -13,15 +13,15 @@ import HistoryView from "./components/history/HistoryView" import SettingsView, { SettingsViewRef } from "./components/settings/SettingsView" import WelcomeView from "./components/welcome/WelcomeView" import McpView from "./components/mcp/McpView" -import PromptsView from "./components/prompts/PromptsView" +import ModesView from "./components/modes/ModesView" import { HumanRelayDialog } from "./components/human-relay/HumanRelayDialog" -type Tab = "settings" | "history" | "mcp" | "prompts" | "chat" +type Tab = "settings" | "history" | "mcp" | "modes" | "chat" const tabsByMessageAction: Partial, Tab>> = { chatButtonClicked: "chat", settingsButtonClicked: "settings", - promptsButtonClicked: "prompts", + promptsButtonClicked: "modes", mcpButtonClicked: "mcp", historyButtonClicked: "history", } @@ -112,7 +112,7 @@ const App = () => { ) : ( <> - {tab === "prompts" && switchTab("chat")} />} + {tab === "modes" && switchTab("chat")} />} {tab === "mcp" && switchTab("chat")} />} {tab === "history" && switchTab("chat")} />} {tab === "settings" && ( diff --git a/webview-ui/src/__tests__/App.test.tsx b/webview-ui/src/__tests__/App.test.tsx index 3262cef69c..eeb173b206 100644 --- a/webview-ui/src/__tests__/App.test.tsx +++ b/webview-ui/src/__tests__/App.test.tsx @@ -56,12 +56,12 @@ jest.mock("@src/components/mcp/McpView", () => ({ }, })) -jest.mock("@src/components/prompts/PromptsView", () => ({ +jest.mock("@src/components/modes/ModesView", () => ({ __esModule: true, - default: function PromptsView({ onDone }: { onDone: () => void }) { + default: function ModesView({ onDone }: { onDone: () => void }) { return (
- Prompts View + Modes View
) }, diff --git a/webview-ui/src/components/prompts/PromptsView.tsx b/webview-ui/src/components/modes/ModesView.tsx similarity index 88% rename from webview-ui/src/components/prompts/PromptsView.tsx rename to webview-ui/src/components/modes/ModesView.tsx index 8ee63f3dfb..18f4bdf3d2 100644 --- a/webview-ui/src/components/prompts/PromptsView.tsx +++ b/webview-ui/src/components/modes/ModesView.tsx @@ -19,7 +19,6 @@ import { getAllModes, findModeBySlug as findCustomModeBySlug, } from "@roo/modes" -import { supportPrompt, SupportPromptType } from "@roo/support-prompt" import { TOOL_GROUPS } from "@roo/tools" import { vscode } from "@src/utils/vscode" @@ -51,7 +50,7 @@ const availableGroups = (Object.keys(TOOL_GROUPS) as ToolGroup[]).filter((group) type ModeSource = "global" | "project" -type PromptsViewProps = { +type ModesViewProps = { onDone: () => void } @@ -60,16 +59,13 @@ function getGroupName(group: GroupEntry): ToolGroup { return Array.isArray(group) ? group[0] : group } -const PromptsView = ({ onDone }: PromptsViewProps) => { +const ModesView = ({ onDone }: ModesViewProps) => { const { t } = useAppTranslation() const { customModePrompts, - customSupportPrompts, listApiConfigMeta, currentApiConfigName, - enhancementApiConfigId, - setEnhancementApiConfigId, mode, customInstructions, setCustomInstructions, @@ -86,15 +82,12 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { // Memoize modes to preserve array order const modes = useMemo(() => getAllModes(customModes), [customModes]) - const [testPrompt, setTestPrompt] = useState("") - const [isEnhancing, setIsEnhancing] = useState(false) const [isDialogOpen, setIsDialogOpen] = useState(false) const [selectedPromptContent, setSelectedPromptContent] = useState("") const [selectedPromptTitle, setSelectedPromptTitle] = useState("") const [isToolsEditMode, setIsToolsEditMode] = useState(false) const [showConfigMenu, setShowConfigMenu] = useState(false) const [isCreateModeDialogOpen, setIsCreateModeDialogOpen] = useState(false) - const [activeSupportOption, setActiveSupportOption] = useState("ENHANCE") const [isSystemPromptDisclosureOpen, setIsSystemPromptDisclosureOpen] = useState(false) // State for mode selection popover and search @@ -380,12 +373,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { useEffect(() => { const handler = (event: MessageEvent) => { const message = event.data - if (message.type === "enhancedPrompt") { - if (message.text) { - setTestPrompt(message.text) - } - setIsEnhancing(false) - } else if (message.type === "systemPrompt") { + if (message.type === "systemPrompt") { if (message.text) { setSelectedPromptContent(message.text) setSelectedPromptTitle(`System Prompt (${message.mode} mode)`) @@ -398,15 +386,6 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { return () => window.removeEventListener("message", handler) }, []) - const updateSupportPrompt = (type: SupportPromptType, value: string | undefined) => { - vscode.postMessage({ - type: "updateSupportPrompt", - values: { - [type]: value, - }, - }) - } - const handleAgentReset = (modeSlug: string, type: "roleDefinition" | "whenToUse" | "customInstructions") => { // Only reset for built-in modes const existingPrompt = customModePrompts?.[modeSlug] as PromptComponent @@ -420,27 +399,6 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { }) } - const handleSupportReset = (type: SupportPromptType) => { - vscode.postMessage({ - type: "resetSupportPrompt", - text: type, - }) - } - - const getSupportPromptValue = (type: SupportPromptType): string => { - return supportPrompt.get(customSupportPrompts, type) - } - - const handleTestEnhancement = () => { - if (!testPrompt.trim()) return - - setIsEnhancing(true) - vscode.postMessage({ - type: "enhancePrompt", - text: testPrompt, - }) - } - return ( @@ -1078,7 +1036,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { -
+

{t("prompts:globalCustomInstructions.title")}

@@ -1131,128 +1089,6 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { />
- -
-

{t("prompts:supportPrompts.title")}

-
- -
- - {/* Support prompt description */} -
- {t(`prompts:supportPrompts.types.${activeSupportOption}.description`)} -
- -
-
-
{t("prompts:supportPrompts.prompt")}
- -
- - { - const value = - (e as unknown as CustomEvent)?.detail?.target?.value || - ((e as any).target as HTMLTextAreaElement).value - const trimmedValue = value.trim() - updateSupportPrompt(activeSupportOption, trimmedValue || undefined) - }} - rows={6} - className="w-full" - /> - - {activeSupportOption === "ENHANCE" && ( - <> -
-
-
-
-
- {t("prompts:supportPrompts.enhance.apiConfiguration")} -
-
- {t("prompts:supportPrompts.enhance.apiConfigDescription")} -
-
- -
-
- -
- setTestPrompt((e.target as HTMLTextAreaElement).value)} - placeholder={t("prompts:supportPrompts.enhance.testPromptPlaceholder")} - rows={3} - className="w-full" - data-testid="test-prompt-textarea" - /> -
- -
-
- - )} -
-
{isCreateModeDialogOpen && ( @@ -1461,4 +1297,4 @@ const PromptsView = ({ onDone }: PromptsViewProps) => { ) } -export default PromptsView +export default ModesView diff --git a/webview-ui/src/components/prompts/__tests__/PromptsView.test.tsx b/webview-ui/src/components/modes/__tests__/ModesView.test.tsx similarity index 87% rename from webview-ui/src/components/prompts/__tests__/PromptsView.test.tsx rename to webview-ui/src/components/modes/__tests__/ModesView.test.tsx index 3a92e4d3e1..e0a9736337 100644 --- a/webview-ui/src/components/prompts/__tests__/PromptsView.test.tsx +++ b/webview-ui/src/components/modes/__tests__/ModesView.test.tsx @@ -1,7 +1,7 @@ // npx jest src/components/prompts/__tests__/PromptsView.test.tsx import { render, screen, fireEvent, waitFor } from "@testing-library/react" -import PromptsView from "../PromptsView" +import ModesView from "../ModesView" import { ExtensionStateContext } from "@src/context/ExtensionStateContext" import { vscode } from "@src/utils/vscode" @@ -48,7 +48,7 @@ const renderPromptsView = (props = {}) => { const mockOnDone = jest.fn() return render( - + , ) } @@ -144,7 +144,7 @@ describe("PromptsView", () => { const { unmount } = render( - + , ) @@ -167,7 +167,7 @@ describe("PromptsView", () => { render( - + , ) @@ -175,24 +175,6 @@ describe("PromptsView", () => { expect(screen.queryByTestId("role-definition-reset")).not.toBeInTheDocument() }) - it("handles API configuration selection", async () => { - renderPromptsView() - - const trigger = screen.getByTestId("support-prompt-select-trigger") - fireEvent.click(trigger) - - const enhanceOption = await waitFor(() => screen.getByTestId("ENHANCE-option")) - fireEvent.click(enhanceOption) - - const apiConfig = await waitFor(() => screen.getByTestId("api-config-select")) - fireEvent.click(apiConfig) - - const config1 = await waitFor(() => screen.getByTestId("config1-option")) - fireEvent.click(config1) - - expect(mockExtensionState.setEnhancementApiConfigId).toHaveBeenCalledWith("config1") // Ensure this is not called by mode switch - }) - it("handles clearing custom instructions correctly", async () => { const setCustomInstructions = jest.fn() renderPromptsView({ diff --git a/webview-ui/src/components/settings/PromptsSettings.tsx b/webview-ui/src/components/settings/PromptsSettings.tsx new file mode 100644 index 0000000000..ffeffca8ea --- /dev/null +++ b/webview-ui/src/components/settings/PromptsSettings.tsx @@ -0,0 +1,193 @@ +import React, { useState, useEffect } from "react" +import { VSCodeTextArea } from "@vscode/webview-ui-toolkit/react" + +import { supportPrompt, SupportPromptType } from "@roo/support-prompt" + +import { vscode } from "@src/utils/vscode" +import { useAppTranslation } from "@src/i18n/TranslationContext" +import { useExtensionState } from "@src/context/ExtensionStateContext" +import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@src/components/ui" +import { SectionHeader } from "./SectionHeader" +import { Section } from "./Section" +import { MessageSquare } from "lucide-react" + +const PromptsSettings = () => { + const { t } = useAppTranslation() + + const { customSupportPrompts, listApiConfigMeta, enhancementApiConfigId, setEnhancementApiConfigId } = + useExtensionState() + + const [testPrompt, setTestPrompt] = useState("") + const [isEnhancing, setIsEnhancing] = useState(false) + const [activeSupportOption, setActiveSupportOption] = useState("ENHANCE") + + useEffect(() => { + const handler = (event: MessageEvent) => { + const message = event.data + if (message.type === "enhancedPrompt") { + if (message.text) { + setTestPrompt(message.text) + } + setIsEnhancing(false) + } + } + + window.addEventListener("message", handler) + return () => window.removeEventListener("message", handler) + }, []) + + const updateSupportPrompt = (type: SupportPromptType, value: string | undefined) => { + vscode.postMessage({ + type: "updateSupportPrompt", + values: { + [type]: value, + }, + }) + } + + const handleSupportReset = (type: SupportPromptType) => { + vscode.postMessage({ + type: "resetSupportPrompt", + text: type, + }) + } + + const getSupportPromptValue = (type: SupportPromptType): string => { + return supportPrompt.get(customSupportPrompts, type) + } + + const handleTestEnhancement = () => { + if (!testPrompt.trim()) return + + setIsEnhancing(true) + vscode.postMessage({ + type: "enhancePrompt", + text: testPrompt, + }) + } + + return ( +
+ +
+ +
{t("settings:sections.prompts")}
+
+
+ +
+
+ +
+ {t(`prompts:supportPrompts.types.${activeSupportOption}.description`)} +
+
+ +
+
+ + +
+ + { + const value = + (e as unknown as CustomEvent)?.detail?.target?.value || + ((e as any).target as HTMLTextAreaElement).value + const trimmedValue = value.trim() + updateSupportPrompt(activeSupportOption, trimmedValue || undefined) + }} + rows={6} + className="w-full" + /> + + {activeSupportOption === "ENHANCE" && ( +
+
+ + +
+ {t("prompts:supportPrompts.enhance.apiConfigDescription")} +
+
+ +
+ + setTestPrompt((e.target as HTMLTextAreaElement).value)} + placeholder={t("prompts:supportPrompts.enhance.testPromptPlaceholder")} + rows={3} + className="w-full" + data-testid="test-prompt-textarea" + /> +
+ +
+
+
+ )} +
+
+
+ ) +} + +export default PromptsSettings diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 8243632469..27b7859540 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -21,6 +21,7 @@ import { AlertTriangle, Globe, Info, + MessageSquare, LucideIcon, } from "lucide-react" @@ -62,6 +63,7 @@ import { ExperimentalSettings } from "./ExperimentalSettings" import { LanguageSettings } from "./LanguageSettings" import { About } from "./About" import { Section } from "./Section" +import PromptsSettings from "./PromptsSettings" import { cn } from "@/lib/utils" export const settingsTabsContainer = "flex flex-1 overflow-hidden [&.narrow_.tab-label]:hidden" @@ -83,6 +85,7 @@ const sectionNames = [ "notifications", "contextManagement", "terminal", + "prompts", "experimental", "language", "about", @@ -369,6 +372,7 @@ const SettingsView = forwardRef(({ onDone, t { id: "notifications", icon: Bell }, { id: "contextManagement", icon: Database }, { id: "terminal", icon: SquareTerminal }, + { id: "prompts", icon: MessageSquare }, { id: "experimental", icon: FlaskConical }, { id: "language", icon: Globe }, { id: "about", icon: Info }, @@ -635,6 +639,9 @@ const SettingsView = forwardRef(({ onDone, t /> )} + {/* Prompts Section */} + {activeTab === "prompts" && } + {/* Experimental Section */} {activeTab === "experimental" && ( Date: Wed, 28 May 2025 17:05:39 -0700 Subject: [PATCH 046/104] [Condense] Move condense settings out of experimental and defualt enable (#4088) * [Condense] Move condense settings out of experimental and defualt enable * tests * wip * Update translations * fixes * more tests * changeset * wip * update translations * fix more translations --- .changeset/eager-buckets-feel.md | 5 + evals/packages/types/src/roo-code.ts | 3 +- packages/types/src/experiment.ts | 3 +- packages/types/src/global-settings.ts | 2 + src/core/task/Task.ts | 3 +- src/core/webview/ClineProvider.ts | 3 + .../webview/__tests__/ClineProvider.test.ts | 19 + src/core/webview/webviewMessageHandler.ts | 4 + src/shared/ExtensionMessage.ts | 1 + src/shared/WebviewMessage.ts | 1 + src/shared/__tests__/experiments.test.ts | 28 - src/shared/experiments.ts | 2 - .../settings/ContextManagementSettings.tsx | 184 ++++++- .../settings/ExperimentalSettings.tsx | 164 +----- .../src/components/settings/SettingsView.tsx | 13 +- .../ContextManagementSettings.test.tsx | 487 +++++++++++++++++- .../src/context/ExtensionStateContext.tsx | 4 + .../__tests__/ExtensionStateContext.test.tsx | 1 + webview-ui/src/i18n/locales/ca/settings.json | 39 +- webview-ui/src/i18n/locales/de/settings.json | 39 +- webview-ui/src/i18n/locales/en/settings.json | 39 +- webview-ui/src/i18n/locales/es/settings.json | 39 +- webview-ui/src/i18n/locales/fr/settings.json | 39 +- webview-ui/src/i18n/locales/hi/settings.json | 39 +- webview-ui/src/i18n/locales/it/settings.json | 39 +- webview-ui/src/i18n/locales/ja/settings.json | 39 +- webview-ui/src/i18n/locales/ko/settings.json | 39 +- webview-ui/src/i18n/locales/nl/settings.json | 39 +- webview-ui/src/i18n/locales/pl/settings.json | 39 +- .../src/i18n/locales/pt-BR/settings.json | 39 +- webview-ui/src/i18n/locales/ru/settings.json | 39 +- webview-ui/src/i18n/locales/tr/settings.json | 39 +- webview-ui/src/i18n/locales/vi/settings.json | 39 +- .../src/i18n/locales/zh-CN/settings.json | 39 +- .../src/i18n/locales/zh-TW/settings.json | 39 +- 35 files changed, 1025 insertions(+), 565 deletions(-) create mode 100644 .changeset/eager-buckets-feel.md diff --git a/.changeset/eager-buckets-feel.md b/.changeset/eager-buckets-feel.md new file mode 100644 index 0000000000..801da70a10 --- /dev/null +++ b/.changeset/eager-buckets-feel.md @@ -0,0 +1,5 @@ +--- +"roo-cline": major +--- + +Default enabled autoCondenseContext and moved settings out of Experimental diff --git a/evals/packages/types/src/roo-code.ts b/evals/packages/types/src/roo-code.ts index b397d37b64..0363c888b6 100644 --- a/evals/packages/types/src/roo-code.ts +++ b/evals/packages/types/src/roo-code.ts @@ -297,7 +297,7 @@ export type CommandExecutionStatus = z.infer */ const experimentsSchema = z.object({ - autoCondenseContext: z.boolean(), powerSteering: z.boolean(), }) diff --git a/packages/types/src/experiment.ts b/packages/types/src/experiment.ts index 6b43327207..c8e39f5c38 100644 --- a/packages/types/src/experiment.ts +++ b/packages/types/src/experiment.ts @@ -6,7 +6,7 @@ import type { Keys, Equals, AssertEqual } from "./type-fu.js" * ExperimentId */ -export const experimentIds = ["autoCondenseContext", "powerSteering"] as const +export const experimentIds = ["powerSteering"] as const export const experimentIdsSchema = z.enum(experimentIds) @@ -17,7 +17,6 @@ export type ExperimentId = z.infer */ export const experimentsSchema = z.object({ - autoCondenseContext: z.boolean(), powerSteering: z.boolean(), }) diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index 10b7d6ab18..3d9a414a59 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -46,6 +46,7 @@ export const globalSettingsSchema = z.object({ alwaysAllowExecute: z.boolean().optional(), allowedCommands: z.array(z.string()).optional(), allowedMaxRequests: z.number().nullish(), + autoCondenseContext: z.boolean().optional(), autoCondenseContextPercent: z.number().optional(), browserToolEnabled: z.boolean().optional(), @@ -131,6 +132,7 @@ export const GLOBAL_SETTINGS_KEYS = keysOf()([ "alwaysAllowExecute", "allowedCommands", "allowedMaxRequests", + "autoCondenseContext", "autoCondenseContextPercent", "browserToolEnabled", diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 8165953e7d..667c1ba3ba 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -1527,8 +1527,8 @@ export class Task extends EventEmitter { autoApprovalEnabled, alwaysApproveResubmit, requestDelaySeconds, - experiments, mode, + autoCondenseContext = true, autoCondenseContextPercent = 100, } = state ?? {} @@ -1592,7 +1592,6 @@ export class Task extends EventEmitter { const contextWindow = modelInfo.contextWindow - const autoCondenseContext = experiments?.autoCondenseContext ?? false const truncateResult = await truncateConversationIfNeeded({ messages: this.apiConversationHistory, totalTokens: contextTokens, diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 932809202e..a8f0473dd5 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1225,6 +1225,7 @@ export class ClineProvider alwaysAllowModeSwitch, alwaysAllowSubtasks, allowedMaxRequests, + autoCondenseContext, autoCondenseContextPercent, soundEnabled, ttsEnabled, @@ -1301,6 +1302,7 @@ export class ClineProvider alwaysAllowModeSwitch: alwaysAllowModeSwitch ?? false, alwaysAllowSubtasks: alwaysAllowSubtasks ?? false, allowedMaxRequests, + autoCondenseContext: autoCondenseContext ?? true, autoCondenseContextPercent: autoCondenseContextPercent ?? 100, uriScheme: vscode.env.uriScheme, currentTaskItem: this.getCurrentCline()?.taskId @@ -1415,6 +1417,7 @@ export class ClineProvider alwaysAllowModeSwitch: stateValues.alwaysAllowModeSwitch ?? false, alwaysAllowSubtasks: stateValues.alwaysAllowSubtasks ?? false, allowedMaxRequests: stateValues.allowedMaxRequests, + autoCondenseContext: stateValues.autoCondenseContext ?? true, autoCondenseContextPercent: stateValues.autoCondenseContextPercent ?? 100, taskHistory: stateValues.taskHistory, allowedCommands: stateValues.allowedCommands, diff --git a/src/core/webview/__tests__/ClineProvider.test.ts b/src/core/webview/__tests__/ClineProvider.test.ts index f141dace36..f545dccb1a 100644 --- a/src/core/webview/__tests__/ClineProvider.test.ts +++ b/src/core/webview/__tests__/ClineProvider.test.ts @@ -422,6 +422,7 @@ describe("ClineProvider", () => { showRooIgnoredFiles: true, renderContext: "sidebar", maxReadFileLine: 500, + autoCondenseContext: true, autoCondenseContextPercent: 100, } @@ -594,6 +595,24 @@ describe("ClineProvider", () => { expect(state.alwaysApproveResubmit).toBe(false) }) + test("autoCondenseContext defaults to true", async () => { + // Mock globalState.get to return undefined for autoCondenseContext + ;(mockContext.globalState.get as jest.Mock).mockImplementation((key: string) => + key === "autoCondenseContext" ? undefined : null, + ) + const state = await provider.getState() + expect(state.autoCondenseContext).toBe(true) + }) + + test("handles autoCondenseContext message", async () => { + await provider.resolveWebviewView(mockWebviewView) + const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0] + await messageHandler({ type: "autoCondenseContext", bool: false }) + expect(updateGlobalStateSpy).toHaveBeenCalledWith("autoCondenseContext", false) + expect(mockContext.globalState.update).toHaveBeenCalledWith("autoCondenseContext", false) + expect(mockPostMessage).toHaveBeenCalled() + }) + test("autoCondenseContextPercent defaults to 100", async () => { // Mock globalState.get to return undefined for autoCondenseContextPercent ;(mockContext.globalState.get as jest.Mock).mockImplementation((key: string) => diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 1a0b64605d..fea39d175c 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -173,6 +173,10 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We case "askResponse": provider.getCurrentCline()?.handleWebviewAskResponse(message.askResponse!, message.text, message.images) break + case "autoCondenseContext": + await updateGlobalState("autoCondenseContext", message.bool) + await provider.postStateToWebview() + break case "autoCondenseContextPercent": await updateGlobalState("autoCondenseContextPercent", message.value) await provider.postStateToWebview() diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 5586e1327b..bb6c1ded52 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -210,6 +210,7 @@ export type ExtensionState = Pick< renderContext: "sidebar" | "editor" settingsImportedAt?: number historyPreviewCollapsed?: boolean + autoCondenseContext: boolean autoCondenseContextPercent: number } diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 9ce596deb7..f26c4ab822 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -60,6 +60,7 @@ export interface WebviewMessage { | "alwaysAllowModeSwitch" | "allowedMaxRequests" | "alwaysAllowSubtasks" + | "autoCondenseContext" | "autoCondenseContextPercent" | "condensingApiConfigId" | "updateCondensingPrompt" diff --git a/src/shared/__tests__/experiments.test.ts b/src/shared/__tests__/experiments.test.ts index 1e7ce0993a..9902f57888 100644 --- a/src/shared/__tests__/experiments.test.ts +++ b/src/shared/__tests__/experiments.test.ts @@ -14,20 +14,10 @@ describe("experiments", () => { }) }) - describe("AUTO_CONDENSE_CONTEXT", () => { - it("is configured correctly", () => { - expect(EXPERIMENT_IDS.AUTO_CONDENSE_CONTEXT).toBe("autoCondenseContext") - expect(experimentConfigsMap.AUTO_CONDENSE_CONTEXT).toMatchObject({ - enabled: false, - }) - }) - }) - describe("isEnabled", () => { it("returns false when POWER_STEERING experiment is not enabled", () => { const experiments: Record = { powerSteering: false, - autoCondenseContext: false, } expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false) }) @@ -35,7 +25,6 @@ describe("experiments", () => { it("returns true when experiment POWER_STEERING is enabled", () => { const experiments: Record = { powerSteering: true, - autoCondenseContext: false, } expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(true) }) @@ -43,25 +32,8 @@ describe("experiments", () => { it("returns false when experiment is not present", () => { const experiments: Record = { powerSteering: false, - autoCondenseContext: false, } expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false) }) - - it("returns false when AUTO_CONDENSE_CONTEXT experiment is not enabled", () => { - const experiments: Record = { - powerSteering: false, - autoCondenseContext: false, - } - expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.AUTO_CONDENSE_CONTEXT)).toBe(false) - }) - - it("returns true when AUTO_CONDENSE_CONTEXT experiment is enabled", () => { - const experiments: Record = { - powerSteering: false, - autoCondenseContext: true, - } - expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.AUTO_CONDENSE_CONTEXT)).toBe(true) - }) }) }) diff --git a/src/shared/experiments.ts b/src/shared/experiments.ts index fbcea728ac..a34fcbe5bb 100644 --- a/src/shared/experiments.ts +++ b/src/shared/experiments.ts @@ -2,7 +2,6 @@ import type { AssertEqual, Equals, Keys, Values, ExperimentId } from "@roo-code/ export const EXPERIMENT_IDS = { POWER_STEERING: "powerSteering", - AUTO_CONDENSE_CONTEXT: "autoCondenseContext", } as const satisfies Record type _AssertExperimentIds = AssertEqual>> @@ -15,7 +14,6 @@ interface ExperimentConfig { export const experimentConfigsMap: Record = { POWER_STEERING: { enabled: false }, - AUTO_CONDENSE_CONTEXT: { enabled: false }, // Keep this last, there is a slider below it in the UI } export const experimentDefault = Object.fromEntries( diff --git a/webview-ui/src/components/settings/ContextManagementSettings.tsx b/webview-ui/src/components/settings/ContextManagementSettings.tsx index d400f941df..b4920ceb21 100644 --- a/webview-ui/src/components/settings/ContextManagementSettings.tsx +++ b/webview-ui/src/components/settings/ContextManagementSettings.tsx @@ -1,26 +1,84 @@ import { HTMLAttributes } from "react" import { useAppTranslation } from "@/i18n/TranslationContext" -import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react" +import { VSCodeCheckbox, VSCodeTextArea } from "@vscode/webview-ui-toolkit/react" import { Database } from "lucide-react" import { cn } from "@/lib/utils" -import { Input, Slider } from "@/components/ui" +import { Button, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Slider } from "@/components/ui" import { SetCachedStateField } from "./types" import { SectionHeader } from "./SectionHeader" import { Section } from "./Section" +import { vscode } from "@/utils/vscode" + +const SUMMARY_PROMPT = `\ +Your task is to create a detailed summary of the conversation so far, paying close attention to the user's explicit requests and your previous actions. +This summary should be thorough in capturing technical details, code patterns, and architectural decisions that would be essential for continuing with the conversation and supporting any continuing tasks. + +Your summary should be structured as follows: +Context: The context to continue the conversation with. If applicable based on the current task, this should include: + 1. Previous Conversation: High level details about what was discussed throughout the entire conversation with the user. This should be written to allow someone to be able to follow the general overarching conversation flow. + 2. Current Work: Describe in detail what was being worked on prior to this request to summarize the conversation. Pay special attention to the more recent messages in the conversation. + 3. Key Technical Concepts: List all important technical concepts, technologies, coding conventions, and frameworks discussed, which might be relevant for continuing with this work. + 4. Relevant Files and Code: If applicable, enumerate specific files and code sections examined, modified, or created for the task continuation. Pay special attention to the most recent messages and changes. + 5. Problem Solving: Document problems solved thus far and any ongoing troubleshooting efforts. + 6. Pending Tasks and Next Steps: Outline all pending tasks that you have explicitly been asked to work on, as well as list the next steps you will take for all outstanding work, if applicable. Include code snippets where they add clarity. For any next steps, include direct quotes from the most recent conversation showing exactly what task you were working on and where you left off. This should be verbatim to ensure there's no information loss in context between tasks. + +Example summary structure: +1. Previous Conversation: + [Detailed description] +2. Current Work: + [Detailed description] +3. Key Technical Concepts: + - [Concept 1] + - [Concept 2] + - [...] +4. Relevant Files and Code: + - [File Name 1] + - [Summary of why this file is important] + - [Summary of the changes made to this file, if any] + - [Important Code Snippet] + - [File Name 2] + - [Important Code Snippet] + - [...] +5. Problem Solving: + [Detailed description] +6. Pending Tasks and Next Steps: + - [Task 1 details & next steps] + - [Task 2 details & next steps] + - [...] + +Output only the summary of the conversation so far, without any additional commentary or explanation. +` type ContextManagementSettingsProps = HTMLAttributes & { + autoCondenseContext: boolean + autoCondenseContextPercent: number + condensingApiConfigId?: string + customCondensingPrompt?: string + listApiConfigMeta: any[] maxOpenTabsContext: number maxWorkspaceFiles: number showRooIgnoredFiles?: boolean maxReadFileLine?: number setCachedStateField: SetCachedStateField< - "maxOpenTabsContext" | "maxWorkspaceFiles" | "showRooIgnoredFiles" | "maxReadFileLine" + | "autoCondenseContext" + | "autoCondenseContextPercent" + | "condensingApiConfigId" + | "customCondensingPrompt" + | "maxOpenTabsContext" + | "maxWorkspaceFiles" + | "showRooIgnoredFiles" + | "maxReadFileLine" > } export const ContextManagementSettings = ({ + autoCondenseContext, + autoCondenseContextPercent, + condensingApiConfigId, + customCondensingPrompt, + listApiConfigMeta, maxOpenTabsContext, maxWorkspaceFiles, showRooIgnoredFiles, @@ -128,6 +186,126 @@ export const ContextManagementSettings = ({
+ +
+ setCachedStateField("autoCondenseContext", e.target.checked)} + data-testid="auto-condense-context-checkbox"> + {t("settings:contextManagement.autoCondenseContext.name")} + + {autoCondenseContext && ( +
+
+ +
{t("settings:contextManagement.autoCondenseContextPercent.label")}
+
+
+
+ + setCachedStateField("autoCondenseContextPercent", value) + } + data-testid="auto-condense-percent-slider" + /> + {autoCondenseContextPercent}% +
+
+ {t("settings:contextManagement.autoCondenseContextPercent.description")} +
+
+ + {/* API Configuration Selection */} +
+
+ +
{t("settings:contextManagement.condensingApiConfiguration.label")}
+
+
+
+ {t("settings:contextManagement.condensingApiConfiguration.description")} +
+ +
+
+ + {/* Custom Prompt Section */} +
+
+ +
{t("settings:contextManagement.customCondensingPrompt.label")}
+
+
+
+ {t("settings:contextManagement.customCondensingPrompt.description")} +
+ { + const value = (e.target as HTMLTextAreaElement).value + setCachedStateField("customCondensingPrompt", value) + vscode.postMessage({ + type: "updateCondensingPrompt", + text: value, + }) + }} + rows={8} + className="w-full font-mono text-sm" + /> +
+ +
+
+
+
+ )} +
) } diff --git a/webview-ui/src/components/settings/ExperimentalSettings.tsx b/webview-ui/src/components/settings/ExperimentalSettings.tsx index ee7cde49fb..a317e45a55 100644 --- a/webview-ui/src/components/settings/ExperimentalSettings.tsx +++ b/webview-ui/src/components/settings/ExperimentalSettings.tsx @@ -1,16 +1,13 @@ import { HTMLAttributes } from "react" -import { VSCodeTextArea } from "@vscode/webview-ui-toolkit/react" import { FlaskConical } from "lucide-react" import type { ExperimentId, CodebaseIndexConfig, CodebaseIndexModels, ProviderSettings } from "@roo-code/types" import { EXPERIMENT_IDS, experimentConfigsMap } from "@roo/experiments" -import { vscode } from "@src/utils/vscode" import { ExtensionStateContextType } from "@src/context/ExtensionStateContext" import { useAppTranslation } from "@src/i18n/TranslationContext" import { cn } from "@src/lib/utils" -import { Button, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Slider } from "@src/components/ui" import { SetCachedStateField, SetExperimentEnabled } from "./types" import { SectionHeader } from "./SectionHeader" @@ -18,56 +15,10 @@ import { Section } from "./Section" import { ExperimentalFeature } from "./ExperimentalFeature" import { CodeIndexSettings } from "./CodeIndexSettings" -const SUMMARY_PROMPT = `\ -Your task is to create a detailed summary of the conversation so far, paying close attention to the user's explicit requests and your previous actions. -This summary should be thorough in capturing technical details, code patterns, and architectural decisions that would be essential for continuing with the conversation and supporting any continuing tasks. - -Your summary should be structured as follows: -Context: The context to continue the conversation with. If applicable based on the current task, this should include: - 1. Previous Conversation: High level details about what was discussed throughout the entire conversation with the user. This should be written to allow someone to be able to follow the general overarching conversation flow. - 2. Current Work: Describe in detail what was being worked on prior to this request to summarize the conversation. Pay special attention to the more recent messages in the conversation. - 3. Key Technical Concepts: List all important technical concepts, technologies, coding conventions, and frameworks discussed, which might be relevant for continuing with this work. - 4. Relevant Files and Code: If applicable, enumerate specific files and code sections examined, modified, or created for the task continuation. Pay special attention to the most recent messages and changes. - 5. Problem Solving: Document problems solved thus far and any ongoing troubleshooting efforts. - 6. Pending Tasks and Next Steps: Outline all pending tasks that you have explicitly been asked to work on, as well as list the next steps you will take for all outstanding work, if applicable. Include code snippets where they add clarity. For any next steps, include direct quotes from the most recent conversation showing exactly what task you were working on and where you left off. This should be verbatim to ensure there's no information loss in context between tasks. - -Example summary structure: -1. Previous Conversation: - [Detailed description] -2. Current Work: - [Detailed description] -3. Key Technical Concepts: - - [Concept 1] - - [Concept 2] - - [...] -4. Relevant Files and Code: - - [File Name 1] - - [Summary of why this file is important] - - [Summary of the changes made to this file, if any] - - [Important Code Snippet] - - [File Name 2] - - [Important Code Snippet] - - [...] -5. Problem Solving: - [Detailed description] -6. Pending Tasks and Next Steps: - - [Task 1 details & next steps] - - [Task 2 details & next steps] - - [...] - -Output only the summary of the conversation so far, without any additional commentary or explanation. -` - type ExperimentalSettingsProps = HTMLAttributes & { experiments: Record setExperimentEnabled: SetExperimentEnabled - autoCondenseContextPercent: number - setCachedStateField: SetCachedStateField<"autoCondenseContextPercent" | "codebaseIndexConfig"> - condensingApiConfigId?: string - setCondensingApiConfigId: (value: string) => void - customCondensingPrompt?: string - setCustomCondensingPrompt: (value: string) => void - listApiConfigMeta: any[] + setCachedStateField: SetCachedStateField<"codebaseIndexConfig"> // CodeIndexSettings props codebaseIndexModels: CodebaseIndexModels | undefined codebaseIndexConfig: CodebaseIndexConfig | undefined @@ -79,13 +30,7 @@ type ExperimentalSettingsProps = HTMLAttributes & { export const ExperimentalSettings = ({ experiments, setExperimentEnabled, - autoCondenseContextPercent, setCachedStateField, - condensingApiConfigId, - setCondensingApiConfigId, - customCondensingPrompt, - setCustomCondensingPrompt, - listApiConfigMeta, codebaseIndexModels, codebaseIndexConfig, apiConfiguration, @@ -118,113 +63,6 @@ export const ExperimentalSettings = ({ } /> ))} - {experiments[EXPERIMENT_IDS.AUTO_CONDENSE_CONTEXT] && ( -
-
- -
{t("settings:experimental.autoCondenseContextPercent.label")}
-
-
-
- - setCachedStateField("autoCondenseContextPercent", value) - } - /> - {autoCondenseContextPercent}% -
-
- {t("settings:experimental.autoCondenseContextPercent.description")} -
-
- - {/* API Configuration Selection */} -
-
- -
{t("settings:experimental.condensingApiConfiguration.label")}
-
-
-
- {t("settings:experimental.condensingApiConfiguration.description")} -
- -
-
- - {/* Custom Prompt Section */} -
-
- -
{t("settings:experimental.customCondensingPrompt.label")}
-
-
-
- {t("settings:experimental.customCondensingPrompt.description")} -
- { - const value = (e.target as HTMLTextAreaElement).value - setCustomCondensingPrompt(value) - vscode.postMessage({ - type: "updateCondensingPrompt", - text: value, - }) - }} - rows={8} - className="w-full font-mono text-sm" - /> -
- -
-
-
-
- )} (({ onDone, t alwaysAllowWrite, alwaysAllowWriteOutsideWorkspace, alwaysApproveResubmit, + autoCondenseContext, autoCondenseContextPercent, browserToolEnabled, browserViewportSize, @@ -256,6 +257,7 @@ const SettingsView = forwardRef(({ onDone, t vscode.postMessage({ type: "alwaysAllowMcp", bool: alwaysAllowMcp }) vscode.postMessage({ type: "allowedCommands", commands: allowedCommands ?? [] }) vscode.postMessage({ type: "allowedMaxRequests", value: allowedMaxRequests ?? undefined }) + vscode.postMessage({ type: "autoCondenseContext", bool: autoCondenseContext }) vscode.postMessage({ type: "autoCondenseContextPercent", value: autoCondenseContextPercent }) vscode.postMessage({ type: "browserToolEnabled", bool: browserToolEnabled }) vscode.postMessage({ type: "soundEnabled", bool: soundEnabled }) @@ -614,6 +616,11 @@ const SettingsView = forwardRef(({ onDone, t {/* Context Management Section */} {activeTab === "contextManagement" && ( (({ onDone, t setCachedStateField("condensingApiConfigId", value)} - customCondensingPrompt={customCondensingPrompt} - setCustomCondensingPrompt={(value) => setCachedStateField("customCondensingPrompt", value)} - listApiConfigMeta={listApiConfigMeta ?? []} setCachedStateField={setCachedStateField} codebaseIndexModels={codebaseIndexModels} codebaseIndexConfig={codebaseIndexConfig} diff --git a/webview-ui/src/components/settings/__tests__/ContextManagementSettings.test.tsx b/webview-ui/src/components/settings/__tests__/ContextManagementSettings.test.tsx index 955ce61936..94669516c6 100644 --- a/webview-ui/src/components/settings/__tests__/ContextManagementSettings.test.tsx +++ b/webview-ui/src/components/settings/__tests__/ContextManagementSettings.test.tsx @@ -1,5 +1,6 @@ // npx jest src/components/settings/__tests__/ContextManagementSettings.test.ts +import React from "react" import { render, screen, fireEvent } from "@testing-library/react" import { ContextManagementSettings } from "@src/components/settings/ContextManagementSettings" @@ -12,20 +13,41 @@ class MockResizeObserver { global.ResizeObserver = MockResizeObserver -jest.mock("@/components/ui", () => ({ - ...jest.requireActual("@/components/ui"), - Slider: ({ value, onValueChange, "data-testid": dataTestId }: any) => ( - onValueChange([parseFloat(e.target.value)])} - data-testid={dataTestId} - /> - ), +// Mock lucide-react icons - these don't work well in Jest/JSDOM environment +jest.mock("lucide-react", () => { + return { + Database: React.forwardRef((props: any, ref: any) =>
), + ChevronDown: React.forwardRef((props: any, ref: any) => ( +
+ )), + ChevronUp: React.forwardRef((props: any, ref: any) => ( +
+ )), + Check: React.forwardRef((props: any, ref: any) =>
), + } +}) + +// Mock translation hook to return the key as the translation +jest.mock("@/i18n/TranslationContext", () => ({ + useAppTranslation: () => ({ + t: (key: string) => key, + }), +})) + +// Mock vscode utilities - this is necessary since we're not in a VSCode environment +import { vscode } from "@/utils/vscode" + +jest.mock("@/utils/vscode", () => ({ + vscode: { + postMessage: jest.fn(), + }, })) describe("ContextManagementSettings", () => { const defaultProps = { + autoCondenseContext: true, + autoCondenseContextPercent: 100, + listApiConfigMeta: [], maxOpenTabsContext: 20, maxWorkspaceFiles: 200, showRooIgnoredFiles: false, @@ -54,21 +76,41 @@ describe("ContextManagementSettings", () => { }) it("updates open tabs context limit", () => { - render() + const mockSetCachedStateField = jest.fn() + const props = { ...defaultProps, setCachedStateField: mockSetCachedStateField } + render() const slider = screen.getByTestId("open-tabs-limit-slider") - fireEvent.change(slider, { target: { value: "50" } }) + expect(slider).toBeInTheDocument() - expect(defaultProps.setCachedStateField).toHaveBeenCalledWith("maxOpenTabsContext", 50) + // Check that the current value is displayed + expect(screen.getByText("20")).toBeInTheDocument() + + // Test slider interaction using keyboard events (ArrowRight increases value) + slider.focus() + fireEvent.keyDown(slider, { key: "ArrowRight" }) + + // The callback should have been called with the new value (20 + 1 = 21) + expect(mockSetCachedStateField).toHaveBeenCalledWith("maxOpenTabsContext", 21) }) - it("updates workspace files contextlimit", () => { - render() + it("updates workspace files limit", () => { + const mockSetCachedStateField = jest.fn() + const props = { ...defaultProps, setCachedStateField: mockSetCachedStateField } + render() const slider = screen.getByTestId("workspace-files-limit-slider") - fireEvent.change(slider, { target: { value: "50" } }) + expect(slider).toBeInTheDocument() - expect(defaultProps.setCachedStateField).toHaveBeenCalledWith("maxWorkspaceFiles", 50) + // Check that the current value is displayed + expect(screen.getByText("200")).toBeInTheDocument() + + // Test slider interaction using keyboard events (ArrowRight increases value) + slider.focus() + fireEvent.keyDown(slider, { key: "ArrowRight" }) + + // The callback should have been called with the new value (200 + 1 = 201) + expect(mockSetCachedStateField).toHaveBeenCalledWith("maxWorkspaceFiles", 201) }) it("updates show rooignored files setting", () => { @@ -79,4 +121,415 @@ describe("ContextManagementSettings", () => { expect(defaultProps.setCachedStateField).toHaveBeenCalledWith("showRooIgnoredFiles", true) }) + + it("renders max read file line controls", () => { + const propsWithMaxReadFileLine = { + ...defaultProps, + maxReadFileLine: 500, + } + render() + + // Max read file line input + const maxReadFileInput = screen.getByTestId("max-read-file-line-input") + expect(maxReadFileInput).toBeInTheDocument() + expect(maxReadFileInput).toHaveValue(500) + + // Always full read checkbox + const alwaysFullReadCheckbox = screen.getByTestId("max-read-file-always-full-checkbox") + expect(alwaysFullReadCheckbox).toBeInTheDocument() + expect(alwaysFullReadCheckbox).not.toBeChecked() + }) + + it("updates max read file line setting", () => { + const propsWithMaxReadFileLine = { + ...defaultProps, + maxReadFileLine: 500, + } + render() + + const input = screen.getByTestId("max-read-file-line-input") + fireEvent.change(input, { target: { value: "1000" } }) + + expect(defaultProps.setCachedStateField).toHaveBeenCalledWith("maxReadFileLine", 1000) + }) + + it("toggles always full read setting", () => { + const propsWithMaxReadFileLine = { + ...defaultProps, + maxReadFileLine: 500, + } + render() + + const checkbox = screen.getByTestId("max-read-file-always-full-checkbox") + fireEvent.click(checkbox) + + expect(defaultProps.setCachedStateField).toHaveBeenCalledWith("maxReadFileLine", -1) + }) + + it("renders with autoCondenseContext enabled", () => { + const propsWithAutoCondense = { + ...defaultProps, + autoCondenseContext: true, + autoCondenseContextPercent: 75, + condensingApiConfigId: "test-config", + customCondensingPrompt: "Test prompt", + } + render() + + // Should render the auto condense section + // Should render the auto condense section + const autoCondenseCheckbox = screen.getByTestId("auto-condense-context-checkbox") + expect(autoCondenseCheckbox).toBeInTheDocument() + + // Should render the slider with correct value + const slider = screen.getByTestId("auto-condense-percent-slider") + expect(slider).toBeInTheDocument() + + // Should render the API config select + const apiSelect = screen.getByRole("combobox") + expect(apiSelect).toBeInTheDocument() + + // Should render the custom prompt textarea + const textarea = screen.getByRole("textbox") + expect(textarea).toBeInTheDocument() + }) + + describe("Auto Condense Context functionality", () => { + const autoCondenseProps = { + ...defaultProps, + autoCondenseContext: true, + autoCondenseContextPercent: 75, + condensingApiConfigId: "test-config", + customCondensingPrompt: "Custom test prompt", + listApiConfigMeta: [ + { id: "config-1", name: "Config 1" }, + { id: "config-2", name: "Config 2" }, + ], + } + + it("toggles auto condense context setting", () => { + const mockSetCachedStateField = jest.fn() + const props = { ...autoCondenseProps, setCachedStateField: mockSetCachedStateField } + const { rerender } = render() + + const checkbox = screen.getByTestId("auto-condense-context-checkbox") + expect(checkbox).toBeChecked() + + // Toggle off + fireEvent.click(checkbox) + expect(mockSetCachedStateField).toHaveBeenCalledWith("autoCondenseContext", false) + + // Re-render with updated props to simulate the state change + rerender() + + // Additional settings should not be visible when disabled + expect(screen.queryByTestId("auto-condense-percent-slider")).not.toBeInTheDocument() + expect(screen.queryByRole("combobox")).not.toBeInTheDocument() + expect(screen.queryByRole("textbox")).not.toBeInTheDocument() + }) + + it("shows additional settings when auto condense is enabled", () => { + render() + + // Additional settings should be visible + expect(screen.getByTestId("auto-condense-percent-slider")).toBeInTheDocument() + expect(screen.getByRole("combobox")).toBeInTheDocument() + expect(screen.getByRole("textbox")).toBeInTheDocument() + }) + + it("hides additional settings when auto condense is disabled", () => { + const props = { ...autoCondenseProps, autoCondenseContext: false } + render() + + // Additional settings should not be visible + expect(screen.queryByTestId("auto-condense-percent-slider")).not.toBeInTheDocument() + expect(screen.queryByRole("combobox")).not.toBeInTheDocument() + expect(screen.queryByRole("textbox")).not.toBeInTheDocument() + }) + + it("updates auto condense context percent", () => { + const mockSetCachedStateField = jest.fn() + const props = { ...autoCondenseProps, setCachedStateField: mockSetCachedStateField } + render() + + // Find the auto condense percent slider + const slider = screen.getByTestId("auto-condense-percent-slider") + + // Test slider interaction + slider.focus() + fireEvent.keyDown(slider, { key: "ArrowRight" }) + + expect(mockSetCachedStateField).toHaveBeenCalledWith("autoCondenseContextPercent", 76) + }) + + it("displays correct auto condense context percent value", () => { + render() + expect(screen.getByText("75%")).toBeInTheDocument() + }) + + it("updates condensing API configuration", () => { + const mockSetCachedStateField = jest.fn() + const mockPostMessage = jest.fn() + const postMessageSpy = jest.spyOn(vscode, "postMessage") + postMessageSpy.mockImplementation(mockPostMessage) + + const props = { ...autoCondenseProps, setCachedStateField: mockSetCachedStateField } + render() + + const apiSelect = screen.getByRole("combobox") + fireEvent.click(apiSelect) + + const configOption = screen.getByText("Config 1") + fireEvent.click(configOption) + + expect(mockSetCachedStateField).toHaveBeenCalledWith("condensingApiConfigId", "config-1") + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "condensingApiConfigId", + text: "config-1", + }) + }) + + it("handles selecting default config option", () => { + const mockSetCachedStateField = jest.fn() + const mockPostMessage = jest.fn() + const postMessageSpy = jest.spyOn(vscode, "postMessage") + postMessageSpy.mockImplementation(mockPostMessage) + + const props = { ...autoCondenseProps, setCachedStateField: mockSetCachedStateField } + render() + + // Test selecting default config + const apiSelect = screen.getByRole("combobox") + fireEvent.click(apiSelect) + const defaultOption = screen.getByText( + "settings:contextManagement.condensingApiConfiguration.useCurrentConfig", + ) + fireEvent.click(defaultOption) + + expect(mockSetCachedStateField).toHaveBeenCalledWith("condensingApiConfigId", "") + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "condensingApiConfigId", + text: "", + }) + }) + + it("updates custom condensing prompt", () => { + const mockSetCachedStateField = jest.fn() + const mockPostMessage = jest.fn() + const postMessageSpy = jest.spyOn(vscode, "postMessage") + postMessageSpy.mockImplementation(mockPostMessage) + + const props = { ...autoCondenseProps, setCachedStateField: mockSetCachedStateField } + render() + + const textarea = screen.getByRole("textbox") + const newPrompt = "Updated custom prompt" + fireEvent.change(textarea, { target: { value: newPrompt } }) + + expect(mockSetCachedStateField).toHaveBeenCalledWith("customCondensingPrompt", newPrompt) + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "updateCondensingPrompt", + text: newPrompt, + }) + }) + + it("resets custom condensing prompt to default", () => { + const mockSetCachedStateField = jest.fn() + const mockPostMessage = jest.fn() + const postMessageSpy = jest.spyOn(vscode, "postMessage") + postMessageSpy.mockImplementation(mockPostMessage) + + const props = { ...autoCondenseProps, setCachedStateField: mockSetCachedStateField } + render() + + const resetButton = screen.getByRole("button", { + name: "settings:contextManagement.customCondensingPrompt.reset", + }) + fireEvent.click(resetButton) + + // Should reset to the default SUMMARY_PROMPT + expect(mockSetCachedStateField).toHaveBeenCalledWith( + "customCondensingPrompt", + expect.stringContaining("Your task is to create a detailed summary"), + ) + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "updateCondensingPrompt", + text: expect.stringContaining("Your task is to create a detailed summary"), + }) + }) + + it("uses default prompt when customCondensingPrompt is undefined", () => { + const propsWithoutCustomPrompt = { + ...autoCondenseProps, + customCondensingPrompt: undefined, + } + render() + + const textarea = screen.getByRole("textbox") as HTMLTextAreaElement + // The textarea should contain the full default SUMMARY_PROMPT + expect(textarea.value).toContain("Your task is to create a detailed summary") + }) + }) + + describe("Edge cases and validation", () => { + it("handles invalid max read file line input", () => { + const mockSetCachedStateField = jest.fn() + const propsWithMaxReadFileLine = { + ...defaultProps, + maxReadFileLine: 500, + setCachedStateField: mockSetCachedStateField, + } + render() + + const input = screen.getByTestId("max-read-file-line-input") + + // Test invalid input (non-numeric) + fireEvent.change(input, { target: { value: "abc" } }) + expect(mockSetCachedStateField).not.toHaveBeenCalled() + + // Test negative value below -1 + fireEvent.change(input, { target: { value: "-5" } }) + expect(mockSetCachedStateField).not.toHaveBeenCalled() + + // Test valid input + fireEvent.change(input, { target: { value: "1000" } }) + expect(mockSetCachedStateField).toHaveBeenCalledWith("maxReadFileLine", 1000) + }) + + it("selects input text on click", () => { + const propsWithMaxReadFileLine = { + ...defaultProps, + maxReadFileLine: 500, + } + render() + + const input = screen.getByTestId("max-read-file-line-input") as HTMLInputElement + const selectSpy = jest.spyOn(input, "select") + + fireEvent.click(input) + expect(selectSpy).toHaveBeenCalled() + }) + + it("disables max read file input when always full read is checked", () => { + const propsWithAlwaysFullRead = { + ...defaultProps, + maxReadFileLine: -1, + } + render() + + const input = screen.getByTestId("max-read-file-line-input") + expect(input).toBeDisabled() + + const checkbox = screen.getByTestId("max-read-file-always-full-checkbox") + expect(checkbox).toBeChecked() + }) + + it("handles boundary values for sliders", () => { + const mockSetCachedStateField = jest.fn() + const props = { + ...defaultProps, + maxOpenTabsContext: 0, + maxWorkspaceFiles: 500, + setCachedStateField: mockSetCachedStateField, + } + render() + + // Check boundary values are displayed + expect(screen.getByText("0")).toBeInTheDocument() // min open tabs + expect(screen.getByText("500")).toBeInTheDocument() // max workspace files + }) + + it("handles undefined optional props gracefully", () => { + const propsWithUndefined = { + ...defaultProps, + showRooIgnoredFiles: undefined, + maxReadFileLine: undefined, + condensingApiConfigId: undefined, + customCondensingPrompt: undefined, + } + + expect(() => { + render() + }).not.toThrow() + + // Should use default values + expect(screen.getByText("20")).toBeInTheDocument() // default maxOpenTabsContext + expect(screen.getByText("200")).toBeInTheDocument() // default maxWorkspaceFiles + }) + }) + + describe("Conditional rendering", () => { + it("does not render auto condense section when autoCondenseContext is false", () => { + const propsWithoutAutoCondense = { + ...defaultProps, + autoCondenseContext: false, + } + render() + + expect(screen.queryByText("settings:experimental.autoCondenseContextPercent.label")).not.toBeInTheDocument() + expect(screen.queryByText("settings:experimental.condensingApiConfiguration.label")).not.toBeInTheDocument() + expect(screen.queryByText("settings:experimental.customCondensingPrompt.label")).not.toBeInTheDocument() + }) + + it("renders max read file controls with default value when maxReadFileLine is undefined", () => { + const propsWithoutMaxReadFile = { + ...defaultProps, + maxReadFileLine: undefined, + } + render() + + // Controls should still be rendered with default value of -1 + const input = screen.getByTestId("max-read-file-line-input") + const checkbox = screen.getByTestId("max-read-file-always-full-checkbox") + + expect(input).toBeInTheDocument() + expect(input).toHaveValue(-1) + expect(input).not.toBeDisabled() // Input is not disabled when maxReadFileLine is undefined (only when explicitly set to -1) + expect(checkbox).toBeInTheDocument() + expect(checkbox).not.toBeChecked() // Checkbox is not checked when maxReadFileLine is undefined (only when explicitly set to -1) + }) + }) + + describe("Accessibility", () => { + it("has proper labels and descriptions", () => { + render() + + // Check that labels are present + expect(screen.getByText("settings:contextManagement.openTabs.label")).toBeInTheDocument() + expect(screen.getByText("settings:contextManagement.workspaceFiles.label")).toBeInTheDocument() + expect(screen.getByText("settings:contextManagement.rooignore.label")).toBeInTheDocument() + + // Check that descriptions are present + expect(screen.getByText("settings:contextManagement.openTabs.description")).toBeInTheDocument() + expect(screen.getByText("settings:contextManagement.workspaceFiles.description")).toBeInTheDocument() + expect(screen.getByText("settings:contextManagement.rooignore.description")).toBeInTheDocument() + }) + + it("has proper test ids for all interactive elements", () => { + const propsWithMaxReadFile = { + ...defaultProps, + maxReadFileLine: 500, + } + render() + + expect(screen.getByTestId("open-tabs-limit-slider")).toBeInTheDocument() + expect(screen.getByTestId("workspace-files-limit-slider")).toBeInTheDocument() + expect(screen.getByTestId("show-rooignored-files-checkbox")).toBeInTheDocument() + expect(screen.getByTestId("max-read-file-line-input")).toBeInTheDocument() + expect(screen.getByTestId("max-read-file-always-full-checkbox")).toBeInTheDocument() + }) + }) + + describe("Integration with translation system", () => { + it("uses translation keys for all text content", () => { + render() + + // Verify that translation keys are being used (mocked to return the key) + expect(screen.getByText("settings:sections.contextManagement")).toBeInTheDocument() + expect(screen.getByText("settings:contextManagement.description")).toBeInTheDocument() + expect(screen.getByText("settings:contextManagement.openTabs.label")).toBeInTheDocument() + expect(screen.getByText("settings:contextManagement.workspaceFiles.label")).toBeInTheDocument() + expect(screen.getByText("settings:contextManagement.rooignore.label")).toBeInTheDocument() + }) + }) }) diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index b7cdf75f25..f3cb99f9ad 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -108,6 +108,8 @@ export interface ExtensionStateContextType extends ExtensionState { terminalCompressProgressBar?: boolean setTerminalCompressProgressBar: (value: boolean) => void setHistoryPreviewCollapsed: (value: boolean) => void + autoCondenseContext: boolean + setAutoCondenseContext: (value: boolean) => void autoCondenseContextPercent: number setAutoCondenseContextPercent: (value: number) => void routerModels?: RouterModels @@ -191,6 +193,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode terminalZdotdir: false, // Default ZDOTDIR handling setting terminalCompressProgressBar: true, // Default to compress progress bar output historyPreviewCollapsed: false, // Initialize the new state (default to expanded) + autoCondenseContext: true, autoCondenseContextPercent: 100, codebaseIndexConfig: { codebaseIndexEnabled: false, @@ -385,6 +388,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode }), setHistoryPreviewCollapsed: (value) => setState((prevState) => ({ ...prevState, historyPreviewCollapsed: value })), + setAutoCondenseContext: (value) => setState((prevState) => ({ ...prevState, autoCondenseContext: value })), setAutoCondenseContextPercent: (value) => setState((prevState) => ({ ...prevState, autoCondenseContextPercent: value })), setCondensingApiConfigId: (value) => setState((prevState) => ({ ...prevState, condensingApiConfigId: value })), diff --git a/webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx b/webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx index 911d516caa..36684969d7 100644 --- a/webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx +++ b/webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx @@ -203,6 +203,7 @@ describe("mergeExtensionState", () => { showRooIgnoredFiles: true, renderContext: "sidebar", maxReadFileLine: 500, + autoCondenseContext: true, autoCondenseContextPercent: 100, } diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index cb4eddc47b..39f4acbe26 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -337,6 +337,25 @@ }, "contextManagement": { "description": "Controleu quina informació s'inclou a la finestra de context de la IA, afectant l'ús de token i la qualitat de resposta", + "autoCondenseContextPercent": { + "label": "Llindar per activar la condensació intel·ligent de context", + "description": "Quan la finestra de context assoleix aquest llindar, Roo la condensarà automàticament." + }, + "condensingApiConfiguration": { + "label": "Configuració d'API per a la condensació de context", + "description": "Seleccioneu quina configuració d'API utilitzar per a les operacions de condensació de context. Deixeu-ho sense seleccionar per utilitzar la configuració activa actual.", + "useCurrentConfig": "Per defecte" + }, + "customCondensingPrompt": { + "label": "Indicació personalitzada de condensació de context", + "description": "Personalitzeu la indicació del sistema utilitzada per a la condensació de context. Deixeu-ho buit per utilitzar la indicació per defecte.", + "placeholder": "Introduïu aquí la vostra indicació de condensació personalitzada...\n\nPodeu utilitzar la mateixa estructura que la indicació per defecte:\n- Conversa anterior\n- Treball actual\n- Conceptes tècnics clau\n- Fitxers i codi rellevants\n- Resolució de problemes\n- Tasques pendents i següents passos", + "reset": "Restablir als valors per defecte", + "hint": "Buit = utilitzar indicació per defecte" + }, + "autoCondenseContext": { + "name": "Activar automàticament la condensació intel·ligent de context" + }, "openTabs": { "label": "Límit de context de pestanyes obertes", "description": "Nombre màxim de pestanyes obertes de VSCode a incloure al context. Valors més alts proporcionen més context però augmenten l'ús de token." @@ -434,14 +453,6 @@ } }, "experimental": { - "autoCondenseContextPercent": { - "label": "Llindar per activar la condensació intel·ligent de context", - "description": "Quan la finestra de context assoleix aquest llindar, Roo la condensarà automàticament." - }, - "AUTO_CONDENSE_CONTEXT": { - "name": "Activar automàticament la condensació intel·ligent de context", - "description": "La condensació intel·ligent de context utilitza una crida LLM per resumir la conversa anterior quan la finestra de context de la tasca assoleix un llindar predefinit, en lloc d'eliminar missatges antics quan el context s'omple." - }, "DIFF_STRATEGY_UNIFIED": { "name": "Utilitzar estratègia diff unificada experimental", "description": "Activar l'estratègia diff unificada experimental. Aquesta estratègia podria reduir el nombre de reintents causats per errors del model, però pot causar comportaments inesperats o edicions incorrectes. Activeu-la només si enteneu els riscos i esteu disposats a revisar acuradament tots els canvis." @@ -461,18 +472,6 @@ "MULTI_SEARCH_AND_REPLACE": { "name": "Utilitzar eina diff de blocs múltiples experimental", "description": "Quan està activat, Roo utilitzarà l'eina diff de blocs múltiples. Això intentarà actualitzar múltiples blocs de codi a l'arxiu en una sola petició." - }, - "condensingApiConfiguration": { - "label": "API Configuration for Context Condensing", - "description": "Select which API configuration to use for context condensing operations. Leave unselected to use the current active configuration.", - "useCurrentConfig": "Default" - }, - "customCondensingPrompt": { - "label": "Custom Context Condensing Prompt", - "description": "Customize the system prompt used for context condensing. Leave empty to use the default prompt.", - "placeholder": "Enter your custom condensing prompt here...\n\nYou can use the same structure as the default prompt:\n- Previous Conversation\n- Current Work\n- Key Technical Concepts\n- Relevant Files and Code\n- Problem Solving\n- Pending Tasks and Next Steps", - "reset": "Reset to Default", - "hint": "Empty = use default prompt" } }, "promptCaching": { diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index ae5a2822f7..80fd93e497 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -337,6 +337,25 @@ }, "contextManagement": { "description": "Steuern Sie, welche Informationen im KI-Kontextfenster enthalten sind, was den Token-Verbrauch und die Antwortqualität beeinflusst", + "autoCondenseContextPercent": { + "label": "Schwellenwert für intelligente Kontextkomprimierung", + "description": "Wenn das Kontextfenster diesen Schwellenwert erreicht, wird Roo es automatisch komprimieren." + }, + "condensingApiConfiguration": { + "label": "API-Konfiguration für Kontextkomprimierung", + "description": "Wählen Sie, welche API-Konfiguration für Kontextkomprimierungsoperationen verwendet werden soll. Lassen Sie unausgewählt, um die aktuelle aktive Konfiguration zu verwenden.", + "useCurrentConfig": "Aktuelle Konfiguration verwenden" + }, + "customCondensingPrompt": { + "label": "Benutzerdefinierter Kontextkomprimierungs-Prompt", + "description": "Passen Sie den System-Prompt an, der für die Kontextkomprimierung verwendet wird. Lassen Sie leer, um den Standard-Prompt zu verwenden.", + "placeholder": "Geben Sie hier Ihren benutzerdefinierten Komprimierungs-Prompt ein...\n\nSie können die gleiche Struktur wie der Standard-Prompt verwenden:\n- Vorherige Konversation\n- Aktuelle Arbeit\n- Wichtige technische Konzepte\n- Relevante Dateien und Code\n- Problemlösung\n- Ausstehende Aufgaben und nächste Schritte", + "reset": "Auf Standard zurücksetzen", + "hint": "Leer = Standard-Prompt verwenden" + }, + "autoCondenseContext": { + "name": "Intelligente Kontextkomprimierung automatisch auslösen" + }, "openTabs": { "label": "Geöffnete Tabs Kontextlimit", "description": "Maximale Anzahl von geöffneten VSCode-Tabs, die im Kontext enthalten sein sollen. Höhere Werte bieten mehr Kontext, erhöhen aber den Token-Verbrauch." @@ -434,26 +453,6 @@ } }, "experimental": { - "autoCondenseContextPercent": { - "label": "Schwellenwert für intelligente Kontextkomprimierung", - "description": "Wenn das Kontextfenster diesen Schwellenwert erreicht, wird Roo es automatisch komprimieren." - }, - "condensingApiConfiguration": { - "label": "API-Konfiguration für Kontextkomprimierung", - "description": "Wählen Sie, welche API-Konfiguration für Kontextkomprimierungsoperationen verwendet werden soll. Lassen Sie unausgewählt, um die aktuelle aktive Konfiguration zu verwenden.", - "useCurrentConfig": "Aktuelle Konfiguration verwenden" - }, - "customCondensingPrompt": { - "label": "Benutzerdefinierter Kontextkomprimierungs-Prompt", - "description": "Passen Sie den System-Prompt an, der für die Kontextkomprimierung verwendet wird. Lassen Sie leer, um den Standard-Prompt zu verwenden.", - "placeholder": "Geben Sie hier Ihren benutzerdefinierten Komprimierungs-Prompt ein...\n\nSie können die gleiche Struktur wie der Standard-Prompt verwenden:\n- Vorherige Konversation\n- Aktuelle Arbeit\n- Wichtige technische Konzepte\n- Relevante Dateien und Code\n- Problemlösung\n- Ausstehende Aufgaben und nächste Schritte", - "reset": "Auf Standard zurücksetzen", - "hint": "Leer = Standard-Prompt verwenden" - }, - "AUTO_CONDENSE_CONTEXT": { - "name": "Intelligente Kontextkomprimierung automatisch auslösen", - "description": "Intelligente Kontextkomprimierung verwendet einen LLM-Aufruf, um das vorherige Gespräch zusammenzufassen, wenn das Kontextfenster der Aufgabe einen voreingestellten Schwellenwert erreicht, anstatt alte Nachrichten zu verwerfen, wenn der Kontext voll ist." - }, "DIFF_STRATEGY_UNIFIED": { "name": "Experimentelle einheitliche Diff-Strategie verwenden", "description": "Aktiviert die experimentelle einheitliche Diff-Strategie. Diese Strategie könnte die Anzahl der durch Modellfehler verursachten Wiederholungen reduzieren, kann aber unerwartetes Verhalten oder falsche Bearbeitungen verursachen. Nur aktivieren, wenn du die Risiken verstehst und bereit bist, alle Änderungen sorgfältig zu überprüfen." diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index cb06a436c9..3264a60553 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -337,6 +337,25 @@ }, "contextManagement": { "description": "Control what information is included in the AI's context window, affecting token usage and response quality", + "autoCondenseContextPercent": { + "label": "Threshold to trigger intelligent context condensing", + "description": "When the context window reaches this threshold, Roo will automatically condense it." + }, + "condensingApiConfiguration": { + "label": "API Configuration for Context Condensing", + "description": "Select which API configuration to use for context condensing operations. Leave unselected to use the current active configuration.", + "useCurrentConfig": "Default" + }, + "customCondensingPrompt": { + "label": "Custom Context Condensing Prompt", + "description": "Customize the system prompt used for context condensing. Leave empty to use the default prompt.", + "placeholder": "Enter your custom condensing prompt here...\n\nYou can use the same structure as the default prompt:\n- Previous Conversation\n- Current Work\n- Key Technical Concepts\n- Relevant Files and Code\n- Problem Solving\n- Pending Tasks and Next Steps", + "reset": "Reset to Default", + "hint": "Empty = use default prompt" + }, + "autoCondenseContext": { + "name": "Automatically trigger intelligent context condensing" + }, "openTabs": { "label": "Open tabs context limit", "description": "Maximum number of VSCode open tabs to include in context. Higher values provide more context but increase token usage." @@ -434,26 +453,6 @@ } }, "experimental": { - "autoCondenseContextPercent": { - "label": "Threshold to trigger intelligent context condensing", - "description": "When the context window reaches this threshold, Roo will automatically condense it." - }, - "condensingApiConfiguration": { - "label": "API Configuration for Context Condensing", - "description": "Select which API configuration to use for context condensing operations. Leave unselected to use the current active configuration.", - "useCurrentConfig": "Default" - }, - "customCondensingPrompt": { - "label": "Custom Context Condensing Prompt", - "description": "Customize the system prompt used for context condensing. Leave empty to use the default prompt.", - "placeholder": "Enter your custom condensing prompt here...\n\nYou can use the same structure as the default prompt:\n- Previous Conversation\n- Current Work\n- Key Technical Concepts\n- Relevant Files and Code\n- Problem Solving\n- Pending Tasks and Next Steps", - "reset": "Reset to Default", - "hint": "Empty = use default prompt" - }, - "AUTO_CONDENSE_CONTEXT": { - "name": "Automatically trigger intelligent context condensing", - "description": "Intelligent context condensing uses an LLM call to summarize the past conversation when the task's context window reaches a preset threshold, rather than dropping old messages when the context fills." - }, "DIFF_STRATEGY_UNIFIED": { "name": "Use experimental unified diff strategy", "description": "Enable the experimental unified diff strategy. This strategy might reduce the number of retries caused by model errors but may cause unexpected behavior or incorrect edits. Only enable if you understand the risks and are willing to carefully review all changes." diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index ec431ece59..8b6605c04e 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -337,6 +337,25 @@ }, "contextManagement": { "description": "Controle qué información se incluye en la ventana de contexto de la IA, afectando el uso de token y la calidad de respuesta", + "autoCondenseContextPercent": { + "label": "Umbral para activar la condensación inteligente de contexto", + "description": "Cuando la ventana de contexto alcanza este umbral, Roo la condensará automáticamente." + }, + "condensingApiConfiguration": { + "label": "Configuración de API para condensación de contexto", + "description": "Seleccione qué configuración de API usar para operaciones de condensación de contexto. Deje sin seleccionar para usar la configuración activa actual.", + "useCurrentConfig": "Usar configuración actual" + }, + "customCondensingPrompt": { + "label": "Prompt personalizado para condensación de contexto", + "description": "Personalice el prompt del sistema utilizado para la condensación de contexto. Deje vacío para usar el prompt predeterminado.", + "placeholder": "Ingrese su prompt de condensación personalizado aquí...\n\nPuede usar la misma estructura que el prompt predeterminado:\n- Conversación anterior\n- Trabajo actual\n- Conceptos técnicos clave\n- Archivos y código relevantes\n- Resolución de problemas\n- Tareas pendientes y próximos pasos", + "reset": "Restablecer a predeterminado", + "hint": "Vacío = usar prompt predeterminado" + }, + "autoCondenseContext": { + "name": "Activar automáticamente la condensación inteligente de contexto" + }, "openTabs": { "label": "Límite de contexto de pestañas abiertas", "description": "Número máximo de pestañas abiertas de VSCode a incluir en el contexto. Valores más altos proporcionan más contexto pero aumentan el uso de token." @@ -434,26 +453,6 @@ } }, "experimental": { - "autoCondenseContextPercent": { - "label": "Umbral para activar la condensación inteligente de contexto", - "description": "Cuando la ventana de contexto alcanza este umbral, Roo la condensará automáticamente." - }, - "condensingApiConfiguration": { - "label": "Configuración de API para condensación de contexto", - "description": "Seleccione qué configuración de API usar para operaciones de condensación de contexto. Deje sin seleccionar para usar la configuración activa actual.", - "useCurrentConfig": "Usar configuración actual" - }, - "customCondensingPrompt": { - "label": "Prompt personalizado para condensación de contexto", - "description": "Personalice el prompt del sistema utilizado para la condensación de contexto. Deje vacío para usar el prompt predeterminado.", - "placeholder": "Ingrese su prompt de condensación personalizado aquí...\n\nPuede usar la misma estructura que el prompt predeterminado:\n- Conversación anterior\n- Trabajo actual\n- Conceptos técnicos clave\n- Archivos y código relevantes\n- Resolución de problemas\n- Tareas pendientes y próximos pasos", - "reset": "Restablecer a predeterminado", - "hint": "Vacío = usar prompt predeterminado" - }, - "AUTO_CONDENSE_CONTEXT": { - "name": "Activar automáticamente la condensación inteligente de contexto", - "description": "La condensación inteligente de contexto utiliza una llamada LLM para resumir la conversación anterior cuando la ventana de contexto de la tarea alcanza un umbral preestablecido, en lugar de eliminar mensajes antiguos cuando el contexto se llena." - }, "DIFF_STRATEGY_UNIFIED": { "name": "Usar estrategia de diff unificada experimental", "description": "Habilitar la estrategia de diff unificada experimental. Esta estrategia podría reducir el número de reintentos causados por errores del modelo, pero puede causar comportamientos inesperados o ediciones incorrectas. Habilítela solo si comprende los riesgos y está dispuesto a revisar cuidadosamente todos los cambios." diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 3193d54a55..7120655ade 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -337,6 +337,25 @@ }, "contextManagement": { "description": "Contrôlez quelles informations sont incluses dans la fenêtre de contexte de l'IA, affectant l'utilisation de token et la qualité des réponses", + "autoCondenseContextPercent": { + "label": "Seuil de déclenchement de la condensation intelligente du contexte", + "description": "Lorsque la fenêtre de contexte atteint ce seuil, Roo la condensera automatiquement." + }, + "condensingApiConfiguration": { + "label": "Configuration API pour la condensation du contexte", + "description": "Sélectionnez quelle configuration API utiliser pour les opérations de condensation du contexte. Laissez non sélectionné pour utiliser la configuration active actuelle.", + "useCurrentConfig": "Par défaut" + }, + "customCondensingPrompt": { + "label": "Prompt personnalisé de condensation du contexte", + "description": "Personnalisez le prompt système utilisé pour la condensation du contexte. Laissez vide pour utiliser le prompt par défaut.", + "placeholder": "Entrez votre prompt de condensation personnalisé ici...\n\nVous pouvez utiliser la même structure que le prompt par défaut :\n- Conversation précédente\n- Travail en cours\n- Concepts techniques clés\n- Fichiers et code pertinents\n- Résolution de problèmes\n- Tâches en attente et prochaines étapes", + "reset": "Réinitialiser par défaut", + "hint": "Vide = utiliser le prompt par défaut" + }, + "autoCondenseContext": { + "name": "Déclencher automatiquement la condensation intelligente du contexte" + }, "openTabs": { "label": "Limite de contexte des onglets ouverts", "description": "Nombre maximum d'onglets VSCode ouverts à inclure dans le contexte. Des valeurs plus élevées fournissent plus de contexte mais augmentent l'utilisation de token." @@ -434,26 +453,6 @@ } }, "experimental": { - "autoCondenseContextPercent": { - "label": "Seuil pour déclencher la condensation intelligente du contexte", - "description": "Lorsque la fenêtre de contexte atteint ce seuil, Roo la condensera automatiquement." - }, - "condensingApiConfiguration": { - "label": "Configuration API pour la condensation du contexte", - "description": "Sélectionnez quelle configuration API utiliser pour les opérations de condensation du contexte. Laissez non sélectionné pour utiliser la configuration active actuelle.", - "useCurrentConfig": "Utiliser la configuration actuelle" - }, - "customCondensingPrompt": { - "label": "Prompt personnalisé pour la condensation du contexte", - "description": "Personnalisez le prompt système utilisé pour la condensation du contexte. Laissez vide pour utiliser le prompt par défaut.", - "placeholder": "Entrez votre prompt de condensation personnalisé ici...\n\nVous pouvez utiliser la même structure que le prompt par défaut :\n- Conversation précédente\n- Travail actuel\n- Concepts techniques clés\n- Fichiers et code pertinents\n- Résolution de problèmes\n- Tâches en attente et prochaines étapes", - "reset": "Réinitialiser par défaut", - "hint": "Vide = utiliser le prompt par défaut" - }, - "AUTO_CONDENSE_CONTEXT": { - "name": "Déclencher automatiquement la condensation intelligente du contexte", - "description": "La condensation intelligente du contexte utilise un appel LLM pour résumer la conversation passée lorsque la fenêtre de contexte de la tâche atteint un seuil prédéfini, plutôt que de supprimer les anciens messages lorsque le contexte est plein." - }, "DIFF_STRATEGY_UNIFIED": { "name": "Utiliser la stratégie diff unifiée expérimentale", "description": "Activer la stratégie diff unifiée expérimentale. Cette stratégie pourrait réduire le nombre de tentatives causées par des erreurs de modèle, mais peut provoquer des comportements inattendus ou des modifications incorrectes. Activez-la uniquement si vous comprenez les risques et êtes prêt à examiner attentivement tous les changements." diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index 558a9dfa9b..329b328416 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -337,6 +337,25 @@ }, "contextManagement": { "description": "AI के संदर्भ विंडो में शामिल जानकारी को नियंत्रित करें, जो token उपयोग और प्रतिक्रिया गुणवत्ता को प्रभावित करता है", + "autoCondenseContextPercent": { + "label": "बुद्धिमान संदर्भ संघनन को ट्रिगर करने की सीमा", + "description": "जब संदर्भ विंडो इस सीमा तक पहुंचती है, तो Roo इसे स्वचालित रूप से संघनित कर देगा।" + }, + "condensingApiConfiguration": { + "label": "संदर्भ संघनन के लिए API कॉन्फ़िगरेशन", + "description": "संदर्भ संघनन कार्यों के लिए किस API कॉन्फ़िगरेशन का उपयोग करना है, यह चुनें। वर्तमान सक्रिय कॉन्फ़िगरेशन का उपयोग करने के लिए अचयनित छोड़ें।", + "useCurrentConfig": "डिफ़ॉल्ट" + }, + "customCondensingPrompt": { + "label": "कस्टम संदर्भ संघनन प्रॉम्प्ट", + "description": "संदर्भ संघनन के लिए कस्टम सिस्टम प्रॉम्प्ट। डिफ़ॉल्ट प्रॉम्प्ट का उपयोग करने के लिए खाली छोड़ें।", + "placeholder": "अपना कस्टम संघनन प्रॉम्प्ट यहाँ दर्ज करें...\n\nआप डिफ़ॉल्ट प्रॉम्प्ट जैसी ही संरचना का उपयोग कर सकते हैं:\n- पिछली बातचीत\n- वर्तमान कार्य\n- प्रमुख तकनीकी अवधारणाएँ\n- प्रासंगिक फ़ाइलें और कोड\n- समस्या समाधान\n- लंबित कार्य और अगले चरण", + "reset": "डिफ़ॉल्ट पर रीसेट करें", + "hint": "खाली = डिफ़ॉल्ट प्रॉम्प्ट का उपयोग करें" + }, + "autoCondenseContext": { + "name": "बुद्धिमान संदर्भ संघनन को स्वचालित रूप से ट्रिगर करें" + }, "openTabs": { "label": "खुले टैब संदर्भ सीमा", "description": "संदर्भ में शामिल करने के लिए VSCode खुले टैब की अधिकतम संख्या। उच्च मान अधिक संदर्भ प्रदान करते हैं लेकिन token उपयोग बढ़ाते हैं।" @@ -434,26 +453,6 @@ } }, "experimental": { - "condensingApiConfiguration": { - "label": "API Configuration for Context Condensing", - "description": "Select which API configuration to use for context condensing operations. Leave unselected to use the current active configuration.", - "useCurrentConfig": "Default" - }, - "customCondensingPrompt": { - "label": "कस्टम संदर्भ संघनन प्रॉम्प्ट", - "description": "संदर्भ संघनन के लिए कस्टम सिस्टम प्रॉम्प्ट। डिफ़ॉल्ट प्रॉम्प्ट का उपयोग करने के लिए खाली छोड़ें।", - "placeholder": "Enter your custom condensing prompt here...\n\nYou can use the same structure as the default prompt:\n- Previous Conversation\n- Current Work\n- Key Technical Concepts\n- Relevant Files and Code\n- Problem Solving\n- Pending Tasks and Next Steps", - "reset": "Reset to Default", - "hint": "Empty = use default prompt" - }, - "autoCondenseContextPercent": { - "label": "बुद्धिमान संदर्भ संघनन को ट्रिगर करने की सीमा", - "description": "जब संदर्भ विंडो इस सीमा तक पहुंचती है, तो Roo इसे स्वचालित रूप से संघनित कर देगा।" - }, - "AUTO_CONDENSE_CONTEXT": { - "name": "बुद्धिमान संदर्भ संघनन को स्वचालित रूप से ट्रिगर करें", - "description": "बुद्धिमान संदर्भ संघनन कार्य के संदर्भ विंडो के पूर्व-निर्धारित सीमा तक पहुंचने पर पिछली बातचीत को संक्षेप में प्रस्तुत करने के लिए LLM कॉल का उपयोग करता है, बजाय इसके कि संदर्भ भरने पर पुराने संदेशों को हटा दिया जाए।" - }, "DIFF_STRATEGY_UNIFIED": { "name": "प्रायोगिक एकीकृत diff रणनीति का उपयोग करें", "description": "प्रायोगिक एकीकृत diff रणनीति सक्षम करें। यह रणनीति मॉडल त्रुटियों के कारण पुनः प्रयासों की संख्या को कम कर सकती है, लेकिन अप्रत्याशित व्यवहार या गलत संपादन का कारण बन सकती है। केवल तभी सक्षम करें जब आप जोखिमों को समझते हों और सभी परिवर्तनों की सावधानीपूर्वक समीक्षा करने के लिए तैयार हों।" diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index c3de9bd877..6c9dd6db7f 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -337,6 +337,25 @@ }, "contextManagement": { "description": "Controlla quali informazioni sono incluse nella finestra di contesto dell'IA, influenzando l'utilizzo di token e la qualità delle risposte", + "autoCondenseContextPercent": { + "label": "Soglia per attivare la condensazione intelligente del contesto", + "description": "Quando la finestra di contesto raggiunge questa soglia, Roo la condenserà automaticamente." + }, + "condensingApiConfiguration": { + "label": "Configurazione API per la condensazione del contesto", + "description": "Seleziona quale configurazione API utilizzare per le operazioni di condensazione del contesto. Lascia deselezionato per utilizzare la configurazione attiva corrente.", + "useCurrentConfig": "Predefinito" + }, + "customCondensingPrompt": { + "label": "Prompt personalizzato condensazione contesto", + "description": "Prompt di sistema personalizzato per la condensazione del contesto. Lascia vuoto per utilizzare il prompt predefinito.", + "placeholder": "Inserisci qui il tuo prompt di condensazione personalizzato...\n\nPuoi utilizzare la stessa struttura del prompt predefinito:\n- Conversazione precedente\n- Lavoro attuale\n- Concetti tecnici chiave\n- File e codice pertinenti\n- Risoluzione dei problemi\n- Attività in sospeso e prossimi passi", + "reset": "Ripristina predefinito", + "hint": "Vuoto = usa prompt predefinito" + }, + "autoCondenseContext": { + "name": "Attiva automaticamente la condensazione intelligente del contesto" + }, "openTabs": { "label": "Limite contesto schede aperte", "description": "Numero massimo di schede VSCode aperte da includere nel contesto. Valori più alti forniscono più contesto ma aumentano l'utilizzo di token." @@ -434,26 +453,6 @@ } }, "experimental": { - "condensingApiConfiguration": { - "label": "API Configuration for Context Condensing", - "description": "Select which API configuration to use for context condensing operations. Leave unselected to use the current active configuration.", - "useCurrentConfig": "Default" - }, - "customCondensingPrompt": { - "label": "Prompt personalizzato condensazione contesto", - "description": "Prompt di sistema personalizzato per la condensazione del contesto. Lascia vuoto per utilizzare il prompt predefinito.", - "placeholder": "Enter your custom condensing prompt here...\n\nYou can use the same structure as the default prompt:\n- Previous Conversation\n- Current Work\n- Key Technical Concepts\n- Relevant Files and Code\n- Problem Solving\n- Pending Tasks and Next Steps", - "reset": "Reset to Default", - "hint": "Empty = use default prompt" - }, - "autoCondenseContextPercent": { - "label": "Soglia per attivare la condensazione intelligente del contesto", - "description": "Quando la finestra di contesto raggiunge questa soglia, Roo la condenserà automaticamente." - }, - "AUTO_CONDENSE_CONTEXT": { - "name": "Attiva automaticamente la condensazione intelligente del contesto", - "description": "La condensazione intelligente del contesto utilizza una chiamata LLM per riassumere la conversazione precedente quando la finestra di contesto dell'attività raggiunge una soglia preimpostata, invece di eliminare i messaggi vecchi quando il contesto si riempie." - }, "DIFF_STRATEGY_UNIFIED": { "name": "Usa strategia diff unificata sperimentale", "description": "Abilita la strategia diff unificata sperimentale. Questa strategia potrebbe ridurre il numero di tentativi causati da errori del modello, ma può causare comportamenti imprevisti o modifiche errate. Abilitala solo se comprendi i rischi e sei disposto a rivedere attentamente tutte le modifiche." diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index e17ae43fda..98f553f3ce 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -337,6 +337,25 @@ }, "contextManagement": { "description": "AIのコンテキストウィンドウに含まれる情報を制御し、token使用量とレスポンスの品質に影響します", + "autoCondenseContextPercent": { + "label": "インテリジェントなコンテキスト圧縮をトリガーするしきい値", + "description": "コンテキストウィンドウがこのしきい値に達すると、Rooは自動的に圧縮します。" + }, + "condensingApiConfiguration": { + "label": "コンテキスト圧縮用のAPI設定", + "description": "コンテキスト圧縮操作に使用するAPI設定を選択します。選択しない場合は現在のアクティブな設定が使用されます。", + "useCurrentConfig": "現在の設定を使用" + }, + "customCondensingPrompt": { + "label": "カスタムコンテキスト圧縮プロンプト", + "description": "コンテキスト圧縮に使用するシステムプロンプトをカスタマイズします。空のままにするとデフォルトのプロンプトが使用されます。", + "placeholder": "ここにカスタム圧縮プロンプトを入力してください...\n\nデフォルトプロンプトと同じ構造を使用できます:\n- 過去の会話\n- 現在の作業\n- 重要な技術的概念\n- 関連するファイルとコード\n- 問題解決\n- 保留中のタスクと次のステップ", + "reset": "デフォルトにリセット", + "hint": "空 = デフォルトプロンプトを使用" + }, + "autoCondenseContext": { + "name": "インテリジェントなコンテキスト圧縮を自動的にトリガーする" + }, "openTabs": { "label": "オープンタブコンテキスト制限", "description": "コンテキストに含めるVSCodeオープンタブの最大数。高い値はより多くのコンテキストを提供しますが、token使用量が増加します。" @@ -434,26 +453,6 @@ } }, "experimental": { - "autoCondenseContextPercent": { - "label": "インテリジェントなコンテキスト圧縮をトリガーするしきい値", - "description": "コンテキストウィンドウがこのしきい値に達すると、Rooは自動的に圧縮します。" - }, - "condensingApiConfiguration": { - "label": "コンテキスト圧縮用のAPI設定", - "description": "コンテキスト圧縮操作に使用するAPI設定を選択します。選択しない場合は現在のアクティブな設定が使用されます。", - "useCurrentConfig": "現在の設定を使用" - }, - "customCondensingPrompt": { - "label": "カスタムコンテキスト圧縮プロンプト", - "description": "コンテキスト圧縮に使用するシステムプロンプトをカスタマイズします。空のままにするとデフォルトのプロンプトが使用されます。", - "placeholder": "ここにカスタム圧縮プロンプトを入力してください...\n\nデフォルトプロンプトと同じ構造を使用できます:\n- 過去の会話\n- 現在の作業\n- 重要な技術的概念\n- 関連するファイルとコード\n- 問題解決\n- 保留中のタスクと次のステップ", - "reset": "デフォルトにリセット", - "hint": "空 = デフォルトプロンプトを使用" - }, - "AUTO_CONDENSE_CONTEXT": { - "name": "インテリジェントなコンテキスト圧縮を自動的にトリガーする", - "description": "インテリジェントなコンテキスト圧縮は、タスクのコンテキストウィンドウが事前設定されたしきい値に達したとき、コンテキストがいっぱいになって古いメッセージを削除する代わりに、LLM呼び出しを使用して過去の会話を要約します。" - }, "DIFF_STRATEGY_UNIFIED": { "name": "実験的な統合diff戦略を使用する", "description": "実験的な統合diff戦略を有効にします。この戦略はモデルエラーによる再試行の回数を減らす可能性がありますが、予期しない動作や不正確な編集を引き起こす可能性があります。リスクを理解し、すべての変更を注意深く確認する準備がある場合にのみ有効にしてください。" diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 2a61d087c9..80ef086ca2 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -337,6 +337,25 @@ }, "contextManagement": { "description": "AI의 컨텍스트 창에 포함되는 정보를 제어하여 token 사용량과 응답 품질에 영향을 미칩니다", + "autoCondenseContextPercent": { + "label": "지능적 컨텍스트 압축을 트리거하는 임계값", + "description": "컨텍스트 창이 이 임계값에 도달하면 Roo가 자동으로 압축합니다." + }, + "condensingApiConfiguration": { + "label": "컨텍스트 압축을 위한 API 설정", + "description": "컨텍스트 압축 작업에 사용할 API 설정을 선택하세요. 선택하지 않으면 현재 활성화된 설정을 사용합니다.", + "useCurrentConfig": "기본값" + }, + "customCondensingPrompt": { + "label": "사용자 지정 컨텍스트 압축 프롬프트", + "description": "컨텍스트 압축을 위한 사용자 지정 시스템 프롬프트입니다. 기본 프롬프트를 사용하려면 비워 두세요.", + "placeholder": "여기에 사용자 정의 압축 프롬프트를 입력하세요...\n\n기본 프롬프트와 동일한 구조를 사용할 수 있습니다:\n- 이전 대화\n- 현재 작업\n- 주요 기술 개념\n- 관련 파일 및 코드\n- 문제 해결\n- 보류 중인 작업 및 다음 단계", + "reset": "기본값으로 재설정", + "hint": "비어있음 = 기본 프롬프트 사용" + }, + "autoCondenseContext": { + "name": "지능적 컨텍스트 압축 자동 트리거" + }, "openTabs": { "label": "열린 탭 컨텍스트 제한", "description": "컨텍스트에 포함할 VSCode 열린 탭의 최대 수. 높은 값은 더 많은 컨텍스트를 제공하지만 token 사용량이 증가합니다." @@ -434,26 +453,6 @@ } }, "experimental": { - "condensingApiConfiguration": { - "label": "API Configuration for Context Condensing", - "description": "Select which API configuration to use for context condensing operations. Leave unselected to use the current active configuration.", - "useCurrentConfig": "Default" - }, - "customCondensingPrompt": { - "label": "사용자 지정 컨텍스트 압축 프롬프트", - "description": "컨텍스트 압축을 위한 사용자 지정 시스템 프롬프트입니다. 기본 프롬프트를 사용하려면 비워 두세요.", - "placeholder": "Enter your custom condensing prompt here...\n\nYou can use the same structure as the default prompt:\n- Previous Conversation\n- Current Work\n- Key Technical Concepts\n- Relevant Files and Code\n- Problem Solving\n- Pending Tasks and Next Steps", - "reset": "Reset to Default", - "hint": "Empty = use default prompt" - }, - "autoCondenseContextPercent": { - "label": "지능적 컨텍스트 압축을 트리거하는 임계값", - "description": "컨텍스트 창이 이 임계값에 도달하면 Roo가 자동으로 압축합니다." - }, - "AUTO_CONDENSE_CONTEXT": { - "name": "지능적 컨텍스트 압축 자동 트리거", - "description": "지능적 컨텍스트 압축은 작업의 컨텍스트 창이 사전 설정된 임계값에 도달했을 때 컨텍스트가 가득 차서 이전 메시지를 삭제하는 대신 LLM 호출을 사용하여 이전 대화를 요약합니다." - }, "DIFF_STRATEGY_UNIFIED": { "name": "실험적 통합 diff 전략 사용", "description": "실험적 통합 diff 전략을 활성화합니다. 이 전략은 모델 오류로 인한 재시도 횟수를 줄일 수 있지만 예기치 않은 동작이나 잘못된 편집을 일으킬 수 있습니다. 위험을 이해하고 모든 변경 사항을 신중하게 검토할 의향이 있는 경우에만 활성화하십시오." diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index 4931a0e2ef..3eaf3e592b 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -337,6 +337,25 @@ }, "contextManagement": { "description": "Bepaal welke informatie wordt opgenomen in het contextvenster van de AI, wat invloed heeft op tokengebruik en antwoordkwaliteit", + "autoCondenseContextPercent": { + "label": "Drempelwaarde om intelligente contextcompressie te activeren", + "description": "Wanneer het contextvenster deze drempelwaarde bereikt, zal Roo het automatisch comprimeren." + }, + "condensingApiConfiguration": { + "label": "API-configuratie voor contextcondensatie", + "description": "Selecteer welke API-configuratie gebruikt moet worden voor contextcondensatie. Laat leeg om de huidige actieve configuratie te gebruiken.", + "useCurrentConfig": "Standaard" + }, + "customCondensingPrompt": { + "label": "Aangepaste contextcondensatieprompt", + "description": "Aangepaste systeemprompt voor contextcondensatie. Laat leeg om de standaardprompt te gebruiken.", + "placeholder": "Voer hier je aangepaste condensatieprompt in...\n\nJe kunt dezelfde structuur gebruiken als de standaardprompt:\n- Vorig gesprek\n- Huidig werk\n- Belangrijke technische concepten\n- Relevante bestanden en code\n- Probleemoplossing\n- Openstaande taken en volgende stappen", + "reset": "Herstellen naar standaard", + "hint": "Leeg = gebruik standaardprompt" + }, + "autoCondenseContext": { + "name": "Automatisch intelligente contextcompressie activeren" + }, "openTabs": { "label": "Limiet geopende tabbladen in context", "description": "Maximaal aantal geopende VSCode-tabbladen dat in de context wordt opgenomen. Hogere waarden geven meer context maar verhogen het tokengebruik." @@ -434,26 +453,6 @@ } }, "experimental": { - "condensingApiConfiguration": { - "label": "API Configuration for Context Condensing", - "description": "Select which API configuration to use for context condensing operations. Leave unselected to use the current active configuration.", - "useCurrentConfig": "Default" - }, - "customCondensingPrompt": { - "label": "Aangepaste contextcondensatieprompt", - "description": "Aangepaste systeemprompt voor contextcondensatie. Laat leeg om de standaardprompt te gebruiken.", - "placeholder": "Enter your custom condensing prompt here...\n\nYou can use the same structure as the default prompt:\n- Previous Conversation\n- Current Work\n- Key Technical Concepts\n- Relevant Files and Code\n- Problem Solving\n- Pending Tasks and Next Steps", - "reset": "Reset to Default", - "hint": "Empty = use default prompt" - }, - "autoCondenseContextPercent": { - "label": "Drempelwaarde om intelligente contextcompressie te activeren", - "description": "Wanneer het contextvenster deze drempelwaarde bereikt, zal Roo het automatisch comprimeren." - }, - "AUTO_CONDENSE_CONTEXT": { - "name": "Automatisch intelligente contextcompressie activeren", - "description": "Intelligente contextcompressie gebruikt een LLM-aanroep om eerdere gesprekken samen te vatten wanneer het contextvenster van de taak een vooraf ingestelde drempelwaarde bereikt, in plaats van oude berichten te verwijderen wanneer de context vol is." - }, "DIFF_STRATEGY_UNIFIED": { "name": "Experimentele unified diff-strategie gebruiken", "description": "Schakel de experimentele unified diff-strategie in. Deze strategie kan het aantal herhalingen door model fouten verminderen, maar kan onverwacht gedrag of onjuiste bewerkingen veroorzaken. Alleen inschakelen als je de risico's begrijpt en wijzigingen zorgvuldig wilt controleren." diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index 703d795ec6..c1e9c10321 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -337,6 +337,25 @@ }, "contextManagement": { "description": "Kontroluj, jakie informacje są zawarte w oknie kontekstu AI, wpływając na zużycie token i jakość odpowiedzi", + "autoCondenseContextPercent": { + "label": "Próg wyzwalający inteligentną kondensację kontekstu", + "description": "Gdy okno kontekstu osiągnie ten próg, Roo automatycznie je skondensuje." + }, + "condensingApiConfiguration": { + "label": "Konfiguracja API dla kondensacji kontekstu", + "description": "Wybierz, którą konfigurację API używać do operacji kondensacji kontekstu. Pozostaw niewybrane, aby użyć aktualnej aktywnej konfiguracji.", + "useCurrentConfig": "Domyślna" + }, + "customCondensingPrompt": { + "label": "Niestandardowy monit kondensacji kontekstu", + "description": "Niestandardowy monit systemowy dla kondensacji kontekstu. Pozostaw puste, aby użyć domyślnego monitu.", + "placeholder": "Wprowadź tutaj swój niestandardowy monit kondensacji...\n\nMożesz użyć tej samej struktury co domyślny monit:\n- Poprzednia rozmowa\n- Aktualna praca\n- Kluczowe koncepcje techniczne\n- Istotne pliki i kod\n- Rozwiązywanie problemów\n- Oczekujące zadania i następne kroki", + "reset": "Przywróć domyślne", + "hint": "Puste = użyj domyślnego monitu" + }, + "autoCondenseContext": { + "name": "Automatycznie wyzwalaj inteligentną kondensację kontekstu" + }, "openTabs": { "label": "Limit kontekstu otwartych kart", "description": "Maksymalna liczba otwartych kart VSCode do uwzględnienia w kontekście. Wyższe wartości zapewniają więcej kontekstu, ale zwiększają zużycie token." @@ -434,26 +453,6 @@ } }, "experimental": { - "condensingApiConfiguration": { - "label": "API Configuration for Context Condensing", - "description": "Select which API configuration to use for context condensing operations. Leave unselected to use the current active configuration.", - "useCurrentConfig": "Default" - }, - "customCondensingPrompt": { - "label": "Niestandardowy monit kondensacji kontekstu", - "description": "Niestandardowy monit systemowy dla kondensacji kontekstu. Pozostaw puste, aby użyć domyślnego monitu.", - "placeholder": "Enter your custom condensing prompt here...\n\nYou can use the same structure as the default prompt:\n- Previous Conversation\n- Current Work\n- Key Technical Concepts\n- Relevant Files and Code\n- Problem Solving\n- Pending Tasks and Next Steps", - "reset": "Reset to Default", - "hint": "Empty = use default prompt" - }, - "autoCondenseContextPercent": { - "label": "Próg wyzwalający inteligentną kondensację kontekstu", - "description": "Gdy okno kontekstu osiągnie ten próg, Roo automatycznie je skondensuje." - }, - "AUTO_CONDENSE_CONTEXT": { - "name": "Automatycznie wyzwalaj inteligentną kondensację kontekstu", - "description": "Inteligentna kondensacja kontekstu używa wywołania LLM do podsumowania wcześniejszej rozmowy, gdy okno kontekstu zadania osiągnie ustawiony próg, zamiast usuwać stare wiadomości, gdy kontekst się zapełni." - }, "DIFF_STRATEGY_UNIFIED": { "name": "Użyj eksperymentalnej ujednoliconej strategii diff", "description": "Włącz eksperymentalną ujednoliconą strategię diff. Ta strategia może zmniejszyć liczbę ponownych prób spowodowanych błędami modelu, ale może powodować nieoczekiwane zachowanie lub nieprawidłowe edycje. Włącz tylko jeśli rozumiesz ryzyko i jesteś gotów dokładnie przeglądać wszystkie zmiany." diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index 4b44990e8c..534ca7bc28 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -337,6 +337,25 @@ }, "contextManagement": { "description": "Controle quais informações são incluídas na janela de contexto da IA, afetando o uso de token e a qualidade da resposta", + "autoCondenseContextPercent": { + "label": "Limite para acionar a condensação inteligente de contexto", + "description": "Quando a janela de contexto atingir este limite, o Roo a condensará automaticamente." + }, + "condensingApiConfiguration": { + "label": "Configuração de API para Condensação de Contexto", + "description": "Selecione qual configuração de API usar para operações de condensação de contexto. Deixe desmarcado para usar a configuração ativa atual.", + "useCurrentConfig": "Padrão" + }, + "customCondensingPrompt": { + "label": "Prompt Personalizado de Condensação de Contexto", + "description": "Prompt de sistema personalizado para condensação de contexto. Deixe em branco para usar o prompt padrão.", + "placeholder": "Digite seu prompt de condensação personalizado aqui...\n\nVocê pode usar a mesma estrutura do prompt padrão:\n- Conversa Anterior\n- Trabalho Atual\n- Conceitos Técnicos Principais\n- Arquivos e Código Relevantes\n- Resolução de Problemas\n- Tarefas Pendentes e Próximos Passos", + "reset": "Restaurar Padrão", + "hint": "Vazio = usar prompt padrão" + }, + "autoCondenseContext": { + "name": "Acionar automaticamente a condensação inteligente de contexto" + }, "openTabs": { "label": "Limite de contexto de abas abertas", "description": "Número máximo de abas abertas do VSCode a incluir no contexto. Valores mais altos fornecem mais contexto, mas aumentam o uso de token." @@ -434,26 +453,6 @@ } }, "experimental": { - "condensingApiConfiguration": { - "label": "API Configuration for Context Condensing", - "description": "Select which API configuration to use for context condensing operations. Leave unselected to use the current active configuration.", - "useCurrentConfig": "Default" - }, - "customCondensingPrompt": { - "label": "Prompt Personalizado de Condensação de Contexto", - "description": "Prompt de sistema personalizado para condensação de contexto. Deixe em branco para usar o prompt padrão.", - "placeholder": "Enter your custom condensing prompt here...\n\nYou can use the same structure as the default prompt:\n- Previous Conversation\n- Current Work\n- Key Technical Concepts\n- Relevant Files and Code\n- Problem Solving\n- Pending Tasks and Next Steps", - "reset": "Reset to Default", - "hint": "Empty = use default prompt" - }, - "autoCondenseContextPercent": { - "label": "Limite para acionar a condensação inteligente de contexto", - "description": "Quando a janela de contexto atingir este limite, o Roo a condensará automaticamente." - }, - "AUTO_CONDENSE_CONTEXT": { - "name": "Acionar automaticamente a condensação inteligente de contexto", - "description": "A condensação inteligente de contexto usa uma chamada LLM para resumir a conversa anterior quando a janela de contexto da tarefa atinge um limite predefinido, em vez de descartar mensagens antigas quando o contexto estiver cheio." - }, "DIFF_STRATEGY_UNIFIED": { "name": "Usar estratégia diff unificada experimental", "description": "Ativar a estratégia diff unificada experimental. Esta estratégia pode reduzir o número de novas tentativas causadas por erros do modelo, mas pode causar comportamento inesperado ou edições incorretas. Ative apenas se compreender os riscos e estiver disposto a revisar cuidadosamente todas as alterações." diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index 37dc0dbd03..7e4d3bdd56 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -337,6 +337,25 @@ }, "contextManagement": { "description": "Управляйте, какая информация включается в окно контекста ИИ, что влияет на расход токенов и качество ответов", + "autoCondenseContextPercent": { + "label": "Порог для запуска интеллектуального сжатия контекста", + "description": "Когда контекстное окно достигает этого порога, Roo автоматически его сожмёт." + }, + "condensingApiConfiguration": { + "label": "Конфигурация API для сжатия контекста", + "description": "Выберите конфигурацию API для операций сжатия контекста. Оставьте невыбранным, чтобы использовать текущую активную конфигурацию.", + "useCurrentConfig": "По умолчанию" + }, + "customCondensingPrompt": { + "label": "Пользовательская подсказка для сжатия контекста", + "description": "Пользовательская системная подсказка для сжатия контекста. Оставьте пустым, чтобы использовать подсказку по умолчанию.", + "placeholder": "Введите здесь свой пользовательский промпт для сжатия...\n\nВы можете использовать ту же структуру, что и в промпте по умолчанию:\n- Предыдущий разговор\n- Текущая работа\n- Ключевые технические концепции\n- Соответствующие файлы и код\n- Решение проблем\n- Ожидающие задачи и следующие шаги", + "reset": "Сбросить на значение по умолчанию", + "hint": "Пусто = использовать промпт по умолчанию" + }, + "autoCondenseContext": { + "name": "Автоматически запускать интеллектуальное сжатие контекста" + }, "openTabs": { "label": "Лимит контекста открытых вкладок", "description": "Максимальное количество открытых вкладок VSCode, включаемых в контекст. Большее значение даёт больше контекста, но увеличивает расход токенов." @@ -434,26 +453,6 @@ } }, "experimental": { - "condensingApiConfiguration": { - "label": "API Configuration for Context Condensing", - "description": "Select which API configuration to use for context condensing operations. Leave unselected to use the current active configuration.", - "useCurrentConfig": "Default" - }, - "customCondensingPrompt": { - "label": "Пользовательская подсказка для сжатия контекста", - "description": "Пользовательская системная подсказка для сжатия контекста. Оставьте пустым, чтобы использовать подсказку по умолчанию.", - "placeholder": "Enter your custom condensing prompt here...\n\nYou can use the same structure as the default prompt:\n- Previous Conversation\n- Current Work\n- Key Technical Concepts\n- Relevant Files and Code\n- Problem Solving\n- Pending Tasks and Next Steps", - "reset": "Reset to Default", - "hint": "Empty = use default prompt" - }, - "autoCondenseContextPercent": { - "label": "Порог для запуска интеллектуального сжатия контекста", - "description": "Когда контекстное окно достигает этого порога, Roo автоматически его сожмёт." - }, - "AUTO_CONDENSE_CONTEXT": { - "name": "Автоматически запускать интеллектуальное сжатие контекста", - "description": "Интеллектуальное сжатие контекста использует вызов LLM для обобщения предыдущего разговора, когда контекстное окно задачи достигает заданного порога, вместо удаления старых сообщений при заполнении контекста." - }, "DIFF_STRATEGY_UNIFIED": { "name": "Использовать экспериментальную стратегию унифицированного диффа", "description": "Включает экспериментальную стратегию унифицированного диффа. Может уменьшить количество повторных попыток из-за ошибок модели, но может привести к неожиданному поведению или неверным правкам. Включайте только если готовы внимательно проверять все изменения." diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index ee27feb977..6e07f54df0 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -337,6 +337,25 @@ }, "contextManagement": { "description": "Yapay zekanın bağlam penceresine hangi bilgilerin dahil edileceğini kontrol edin, token kullanımını ve yanıt kalitesini etkiler", + "autoCondenseContextPercent": { + "label": "Akıllı bağlam sıkıştırmayı tetikleyecek eşik", + "description": "Bağlam penceresi bu eşiğe ulaştığında, Roo otomatik olarak sıkıştıracaktır." + }, + "condensingApiConfiguration": { + "label": "Bağlam Yoğunlaştırma için API Yapılandırması", + "description": "Bağlam yoğunlaştırma işlemleri için hangi API yapılandırmasının kullanılacağını seçin. Mevcut aktif yapılandırmayı kullanmak için seçimsiz bırakın.", + "useCurrentConfig": "Varsayılan" + }, + "customCondensingPrompt": { + "label": "Özel Bağlam Yoğunlaştırma İstemcisi", + "description": "Bağlam yoğunlaştırma için özel sistem istemcisi. Varsayılan istemciyi kullanmak için boş bırakın.", + "placeholder": "Özel yoğunlaştırma promptunuzu buraya girin...\n\nVarsayılan prompt ile aynı yapıyı kullanabilirsiniz:\n- Önceki Konuşma\n- Mevcut Çalışma\n- Temel Teknik Kavramlar\n- İlgili Dosyalar ve Kod\n- Problem Çözme\n- Bekleyen Görevler ve Sonraki Adımlar", + "reset": "Varsayılana Sıfırla", + "hint": "Boş = varsayılan promptu kullan" + }, + "autoCondenseContext": { + "name": "Akıllı bağlam sıkıştırmayı otomatik olarak tetikle" + }, "openTabs": { "label": "Açık sekmeler bağlam sınırı", "description": "Bağlama dahil edilecek maksimum VSCode açık sekme sayısı. Daha yüksek değerler daha fazla bağlam sağlar ancak token kullanımını artırır." @@ -434,26 +453,6 @@ } }, "experimental": { - "condensingApiConfiguration": { - "label": "API Configuration for Context Condensing", - "description": "Select which API configuration to use for context condensing operations. Leave unselected to use the current active configuration.", - "useCurrentConfig": "Default" - }, - "customCondensingPrompt": { - "label": "Özel Bağlam Yoğunlaştırma İstemcisi", - "description": "Bağlam yoğunlaştırma için özel sistem istemcisi. Varsayılan istemciyi kullanmak için boş bırakın.", - "placeholder": "Enter your custom condensing prompt here...\n\nYou can use the same structure as the default prompt:\n- Previous Conversation\n- Current Work\n- Key Technical Concepts\n- Relevant Files and Code\n- Problem Solving\n- Pending Tasks and Next Steps", - "reset": "Reset to Default", - "hint": "Empty = use default prompt" - }, - "autoCondenseContextPercent": { - "label": "Akıllı bağlam sıkıştırmayı tetikleyecek eşik", - "description": "Bağlam penceresi bu eşiğe ulaştığında, Roo otomatik olarak sıkıştıracaktır." - }, - "AUTO_CONDENSE_CONTEXT": { - "name": "Akıllı bağlam sıkıştırmayı otomatik olarak tetikle", - "description": "Akıllı bağlam sıkıştırma, görevin bağlam penceresi önceden belirlenmiş bir eşiğe ulaştığında, bağlam dolduğunda eski mesajları atmak yerine önceki konuşmayı özetlemek için bir LLM çağrısı kullanır." - }, "DIFF_STRATEGY_UNIFIED": { "name": "Deneysel birleştirilmiş diff stratejisini kullan", "description": "Deneysel birleştirilmiş diff stratejisini etkinleştir. Bu strateji, model hatalarından kaynaklanan yeniden deneme sayısını azaltabilir, ancak beklenmeyen davranışlara veya hatalı düzenlemelere neden olabilir. Yalnızca riskleri anlıyorsanız ve tüm değişiklikleri dikkatlice incelemeye istekliyseniz etkinleştirin." diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index 6df836107d..09b07deb5b 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -337,6 +337,25 @@ }, "contextManagement": { "description": "Kiểm soát thông tin nào được đưa vào cửa sổ ngữ cảnh của AI, ảnh hưởng đến việc sử dụng token và chất lượng phản hồi", + "autoCondenseContextPercent": { + "label": "Ngưỡng kích hoạt nén ngữ cảnh thông minh", + "description": "Khi cửa sổ ngữ cảnh đạt đến ngưỡng này, Roo sẽ tự động nén nó." + }, + "condensingApiConfiguration": { + "label": "Cấu hình API cho Tóm tắt Ngữ cảnh", + "description": "Chọn cấu hình API để sử dụng cho các thao tác tóm tắt ngữ cảnh. Để trống để sử dụng cấu hình đang hoạt động hiện tại.", + "useCurrentConfig": "Mặc định" + }, + "customCondensingPrompt": { + "label": "Lời nhắc nén ngữ cảnh tùy chỉnh", + "description": "Lời nhắc hệ thống tùy chỉnh cho việc nén ngữ cảnh. Để trống để sử dụng lời nhắc mặc định.", + "placeholder": "Nhập prompt tóm tắt tùy chỉnh của bạn tại đây...\n\nBạn có thể sử dụng cùng cấu trúc như prompt mặc định:\n- Cuộc hội thoại trước\n- Công việc hiện tại\n- Khái niệm kỹ thuật chính\n- Tệp và mã liên quan\n- Giải quyết vấn đề\n- Công việc đang chờ và các bước tiếp theo", + "reset": "Khôi phục mặc định", + "hint": "Để trống = sử dụng prompt mặc định" + }, + "autoCondenseContext": { + "name": "Tự động kích hoạt nén ngữ cảnh thông minh" + }, "openTabs": { "label": "Giới hạn ngữ cảnh tab đang mở", "description": "Số lượng tab VSCode đang mở tối đa để đưa vào ngữ cảnh. Giá trị cao hơn cung cấp nhiều ngữ cảnh hơn nhưng tăng sử dụng token." @@ -434,26 +453,6 @@ } }, "experimental": { - "condensingApiConfiguration": { - "label": "API Configuration for Context Condensing", - "description": "Select which API configuration to use for context condensing operations. Leave unselected to use the current active configuration.", - "useCurrentConfig": "Default" - }, - "customCondensingPrompt": { - "label": "Lời nhắc nén ngữ cảnh tùy chỉnh", - "description": "Lời nhắc hệ thống tùy chỉnh cho việc nén ngữ cảnh. Để trống để sử dụng lời nhắc mặc định.", - "placeholder": "Enter your custom condensing prompt here...\n\nYou can use the same structure as the default prompt:\n- Previous Conversation\n- Current Work\n- Key Technical Concepts\n- Relevant Files and Code\n- Problem Solving\n- Pending Tasks and Next Steps", - "reset": "Reset to Default", - "hint": "Empty = use default prompt" - }, - "autoCondenseContextPercent": { - "label": "Ngưỡng kích hoạt nén ngữ cảnh thông minh", - "description": "Khi cửa sổ ngữ cảnh đạt đến ngưỡng này, Roo sẽ tự động nén nó." - }, - "AUTO_CONDENSE_CONTEXT": { - "name": "Tự động kích hoạt nén ngữ cảnh thông minh", - "description": "Nén ngữ cảnh thông minh sử dụng một lệnh gọi LLM để tóm tắt cuộc trò chuyện trước đó khi cửa sổ ngữ cảnh của tác vụ đạt đến ngưỡng đã định, thay vì loại bỏ các tin nhắn cũ khi ngữ cảnh đầy." - }, "DIFF_STRATEGY_UNIFIED": { "name": "Sử dụng chiến lược diff thống nhất thử nghiệm", "description": "Bật chiến lược diff thống nhất thử nghiệm. Chiến lược này có thể giảm số lần thử lại do lỗi mô hình nhưng có thể gây ra hành vi không mong muốn hoặc chỉnh sửa không chính xác. Chỉ bật nếu bạn hiểu rõ các rủi ro và sẵn sàng xem xét cẩn thận tất cả các thay đổi." diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index 9316155368..0a9882e182 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -337,6 +337,25 @@ }, "contextManagement": { "description": "管理AI上下文信息(影响token用量和回答质量)", + "autoCondenseContextPercent": { + "label": "触发智能上下文压缩的阈值", + "description": "当上下文窗口达到此阈值时,Roo 将自动压缩它。" + }, + "condensingApiConfiguration": { + "label": "上下文压缩的API配置", + "description": "选择用于上下文压缩操作的API配置。留空则使用当前活动的配置。", + "useCurrentConfig": "使用当前配置" + }, + "customCondensingPrompt": { + "label": "自定义上下文压缩提示词", + "description": "自定义用于上下文压缩的系统提示词。留空则使用默认提示词。", + "placeholder": "在此输入您的自定义压缩提示词...\n\n您可以使用与默认提示词相同的结构:\n- 之前的对话\n- 当前工作\n- 关键技术概念\n- 相关文件和代码\n- 问题解决\n- 待处理任务和下一步", + "reset": "重置为默认值", + "hint": "留空 = 使用默认提示词" + }, + "autoCondenseContext": { + "name": "自动触发智能上下文压缩" + }, "openTabs": { "label": "标签页数量限制", "description": "允许纳入上下文的最大标签页数(数值越大消耗token越多)" @@ -434,26 +453,6 @@ } }, "experimental": { - "autoCondenseContextPercent": { - "label": "触发智能上下文压缩的阈值", - "description": "当上下文窗口达到此阈值时,Roo 将自动压缩它。" - }, - "condensingApiConfiguration": { - "label": "上下文压缩的API配置", - "description": "选择用于上下文压缩操作的API配置。留空则使用当前活动的配置。", - "useCurrentConfig": "使用当前配置" - }, - "customCondensingPrompt": { - "label": "自定义上下文压缩提示词", - "description": "自定义用于上下文压缩的系统提示词。留空则使用默认提示词。", - "placeholder": "在此输入您的自定义压缩提示词...\n\n您可以使用与默认提示词相同的结构:\n- 之前的对话\n- 当前工作\n- 关键技术概念\n- 相关文件和代码\n- 问题解决\n- 待处理任务和下一步", - "reset": "重置为默认值", - "hint": "留空 = 使用默认提示词" - }, - "AUTO_CONDENSE_CONTEXT": { - "name": "自动触发智能上下文压缩", - "description": "智能上下文压缩使用 LLM 调用来总结过去的对话,在任务上下文窗口达到预设阈值时进行,而不是在上下文填满时丢弃旧消息。" - }, "DIFF_STRATEGY_UNIFIED": { "name": "启用diff更新工具", "description": "可减少因模型错误导致的重复尝试,但可能引发意外操作。启用前请确保理解风险并会仔细检查所有修改。" diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index 194d668a65..bb972ca9b3 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -337,6 +337,25 @@ }, "contextManagement": { "description": "控制 AI 上下文視窗中要包含哪些資訊,會影響 token 用量和回應品質", + "autoCondenseContextPercent": { + "label": "觸發智慧上下文壓縮的閾值", + "description": "當上下文視窗達到此閾值時,Roo 將自動壓縮它。" + }, + "condensingApiConfiguration": { + "label": "上下文壓縮的API配置", + "description": "選擇用於上下文壓縮操作的API配置。留空則使用當前活動的配置。", + "useCurrentConfig": "使用當前配置" + }, + "customCondensingPrompt": { + "label": "自訂上下文壓縮提示", + "description": "自訂用於上下文壓縮的系統提示。留空則使用預設提示。", + "placeholder": "請在此輸入您的自訂上下文壓縮提示...\n\n您可以參考預設提示的結構:\n- 先前對話\n- 目前工作\n- 主要技術概念\n- 相關檔案與程式碼\n- 問題解決\n- 未完成的任務與後續步驟", + "reset": "重設為預設值", + "hint": "留空 = 使用預設提示" + }, + "autoCondenseContext": { + "name": "自動觸發智慧上下文壓縮" + }, "openTabs": { "label": "開啟分頁的上下文限制", "description": "上下文中最多包含多少個 VS Code 開啟的分頁。數值越高提供的上下文越多,但 token 用量也會增加。" @@ -434,26 +453,6 @@ } }, "experimental": { - "condensingApiConfiguration": { - "label": "API Configuration for Context Condensing", - "description": "Select which API configuration to use for context condensing operations. Leave unselected to use the current active configuration.", - "useCurrentConfig": "Default" - }, - "customCondensingPrompt": { - "label": "自訂上下文壓縮提示", - "description": "自訂用於上下文壓縮的系統提示。留空則使用預設提示。", - "placeholder": "請在此輸入您的自訂上下文壓縮提示...\n\n您可以參考預設提示的結構:\n- 先前對話\n- 目前工作\n- 主要技術概念\n- 相關檔案與程式碼\n- 問題解決\n- 未完成的任務與後續步驟", - "reset": "重設為預設值", - "hint": "Empty = use default prompt" - }, - "autoCondenseContextPercent": { - "label": "觸發智慧上下文壓縮的閾值", - "description": "當上下文視窗達到此閾值時,Roo 將自動壓縮它。" - }, - "AUTO_CONDENSE_CONTEXT": { - "name": "自動觸發智慧上下文壓縮", - "description": "智慧上下文壓縮使用 LLM 呼叫來摘要過去的對話,在工作的上下文視窗達到預設閾值時進行,而非在上下文填滿時捨棄舊訊息。" - }, "DIFF_STRATEGY_UNIFIED": { "name": "使用實驗性統一差異比對策略", "description": "啟用實驗性的統一差異比對策略。此策略可能減少因模型錯誤而導致的重試次數,但也可能導致意外行為或錯誤的編輯。請務必了解風險,並願意仔細檢查所有變更後再啟用。" From 3ee7786a1b17c068123d25aac1683613451777dc Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Wed, 28 May 2025 21:41:20 -0400 Subject: [PATCH 047/104] Update AWS regions to include Spain and Hyderabad (solves #3862) (#4042) Update AWS regions to include Spain and Hyderabad --- src/shared/aws_regions.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/shared/aws_regions.ts b/src/shared/aws_regions.ts index 7149acda4b..416197947e 100644 --- a/src/shared/aws_regions.ts +++ b/src/shared/aws_regions.ts @@ -39,9 +39,10 @@ export const AMAZON_BEDROCK_REGION_INFO: Record< "euw2.": { regionId: "eu-west-2", description: "Europe (London)" }, "euw3.": { regionId: "eu-west-3", description: "Europe (Paris)" }, "euc1.": { regionId: "eu-central-1", description: "Europe (Frankfurt)" }, + "euc2.": { regionId: "eu-central-2", description: "Europe (Zurich)" }, "eun1.": { regionId: "eu-north-1", description: "Europe (Stockholm)" }, "eus1.": { regionId: "eu-south-1", description: "Europe (Milan)" }, - "euz1.": { regionId: "eu-central-2", description: "Europe (Zurich)" }, + "eus2.": { regionId: "eu-south-2", description: "Europe (Spain)" }, "ap.": { regionId: "ap-southeast-1", description: "Asia Pacific (Singapore)", @@ -53,6 +54,7 @@ export const AMAZON_BEDROCK_REGION_INFO: Record< "apne2.": { regionId: "ap-northeast-2", description: "Asia Pacific (Seoul)" }, "apne3.": { regionId: "ap-northeast-3", description: "Asia Pacific (Osaka)" }, "aps1.": { regionId: "ap-south-1", description: "Asia Pacific (Mumbai)" }, + "aps2.": { regionId: "ap-south-2", description: "Asia Pacific (Hyderabad)" }, "apse1.": { regionId: "ap-southeast-1", description: "Asia Pacific (Singapore)" }, "apse2.": { regionId: "ap-southeast-2", description: "Asia Pacific (Sydney)" }, "ca.": { regionId: "ca-central-1", description: "Canada (Central)", pattern: "ca-", multiRegion: true }, From 70e753ebb1fe1e0b53c09e6594e9bbbe03a931d0 Mon Sep 17 00:00:00 2001 From: Canyon Robins Date: Wed, 28 May 2025 18:58:08 -0700 Subject: [PATCH 048/104] [Condense] Move condense button out of expanded task menu (#4093) * [Condense] Move condense button out of expanded task menu * tests * fix test * fix test again * tailwind css --- .../src/components/chat/TaskActions.tsx | 9 +---- webview-ui/src/components/chat/TaskHeader.tsx | 24 ++++++------ .../chat/__tests__/TaskHeader.test.tsx | 37 ++++++++++++++++++- 3 files changed, 47 insertions(+), 23 deletions(-) diff --git a/webview-ui/src/components/chat/TaskActions.tsx b/webview-ui/src/components/chat/TaskActions.tsx index 2eebc6ccb3..18325af8fb 100644 --- a/webview-ui/src/components/chat/TaskActions.tsx +++ b/webview-ui/src/components/chat/TaskActions.tsx @@ -12,10 +12,9 @@ import { IconButton } from "./IconButton" interface TaskActionsProps { item?: HistoryItem buttonsDisabled: boolean - handleCondenseContext: (taskId: string) => void } -export const TaskActions = ({ item, buttonsDisabled, handleCondenseContext }: TaskActionsProps) => { +export const TaskActions = ({ item, buttonsDisabled }: TaskActionsProps) => { const [deleteTaskId, setDeleteTaskId] = useState(null) const { t } = useTranslation() @@ -29,12 +28,6 @@ export const TaskActions = ({ item, buttonsDisabled, handleCondenseContext }: Ta /> {!!item?.size && item.size > 0 && ( <> - handleCondenseContext(item.id)} - />
{/* Collapsed state: Track context and cost if we have any */} {!isTaskExpanded && contextWindow > 0 && ( -
+
+ currentTaskItem && handleCondenseContext(currentTaskItem.id)} + className="shrink-0 min-h-[20px] min-w-[20px] p-[2px]" + /> {!!totalCost && ${totalCost.toFixed(2)}}
)} @@ -169,13 +177,7 @@ const TaskHeader = ({ )}
- {!totalCost && ( - - )} + {!totalCost && }
{doesModelSupportPromptCache && @@ -204,11 +206,7 @@ const TaskHeader = ({ {t("chat:task.apiCost")} ${totalCost?.toFixed(2)}
- +
)}
diff --git a/webview-ui/src/components/chat/__tests__/TaskHeader.test.tsx b/webview-ui/src/components/chat/__tests__/TaskHeader.test.tsx index 12f43b8bea..9d0de80191 100644 --- a/webview-ui/src/components/chat/__tests__/TaskHeader.test.tsx +++ b/webview-ui/src/components/chat/__tests__/TaskHeader.test.tsx @@ -1,13 +1,25 @@ // npx jest src/components/chat/__tests__/TaskHeader.test.tsx import React from "react" -import { render, screen } from "@testing-library/react" +import { render, screen, fireEvent } from "@testing-library/react" import { QueryClient, QueryClientProvider } from "@tanstack/react-query" import type { ProviderSettings } from "@roo-code/types" import TaskHeader, { TaskHeaderProps } from "../TaskHeader" +// Mock i18n +jest.mock("react-i18next", () => ({ + useTranslation: () => ({ + t: (key: string) => key, // Simple mock that returns the key + }), + // Mock initReactI18next to prevent initialization errors in tests + initReactI18next: { + type: "3rdParty", + init: jest.fn(), + }, +})) + // Mock the vscode API jest.mock("@/utils/vscode", () => ({ vscode: { @@ -28,7 +40,7 @@ jest.mock("@src/context/ExtensionStateContext", () => ({ apiKey: "test-api-key", // Add relevant fields apiModelId: "claude-3-opus-20240229", // Add relevant fields } as ProviderSettings, // Optional: Add type assertion if ProviderSettings is imported - currentTaskItem: null, + currentTaskItem: { id: "test-task-id" }, }), })) @@ -79,4 +91,25 @@ describe("TaskHeader", () => { renderTaskHeader({ totalCost: NaN }) expect(screen.queryByText(/\$/)).not.toBeInTheDocument() }) + + it("should render the condense context button", () => { + renderTaskHeader() + expect(screen.getByTitle("chat:task.condenseContext")).toBeInTheDocument() + }) + + it("should call handleCondenseContext when condense context button is clicked", () => { + const handleCondenseContext = jest.fn() + renderTaskHeader({ handleCondenseContext }) + const condenseButton = screen.getByTitle("chat:task.condenseContext") + fireEvent.click(condenseButton) + expect(handleCondenseContext).toHaveBeenCalledWith("test-task-id") + }) + + it("should disable the condense context button when buttonsDisabled is true", () => { + const handleCondenseContext = jest.fn() + renderTaskHeader({ buttonsDisabled: true, handleCondenseContext }) + const condenseButton = screen.getByTitle("chat:task.condenseContext") + fireEvent.click(condenseButton) + expect(handleCondenseContext).not.toHaveBeenCalled() + }) }) From aa6265462e0f505067d48113cfe116ae600f42d6 Mon Sep 17 00:00:00 2001 From: xyOz Date: Thu, 29 May 2025 02:58:50 +0100 Subject: [PATCH 049/104] @directory not respecting .rooIgnore Fix (#4075) * Issue fixed. * Update src/core/mentions/index.ts Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * Update index.ts --------- Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> --- src/core/mentions/index.ts | 74 +++++++++++++------ .../mentions/processUserContentMentions.ts | 24 +++++- src/core/task/Task.ts | 4 + 3 files changed, 77 insertions(+), 25 deletions(-) diff --git a/src/core/mentions/index.ts b/src/core/mentions/index.ts index d53a3ff3ed..8ae4f7f131 100644 --- a/src/core/mentions/index.ts +++ b/src/core/mentions/index.ts @@ -17,6 +17,8 @@ import { UrlContentFetcher } from "../../services/browser/UrlContentFetcher" import { FileContextTracker } from "../context-tracking/FileContextTracker" +import { RooIgnoreController } from "../ignore/RooIgnoreController" + export async function openMention(mention?: string): Promise { if (!mention) { return @@ -50,6 +52,8 @@ export async function parseMentions( cwd: string, urlContentFetcher: UrlContentFetcher, fileContextTracker?: FileContextTracker, + rooIgnoreController?: RooIgnoreController, + showRooIgnoredFiles: boolean = true, ): Promise { const mentions: Set = new Set() let parsedText = text.replace(mentionRegexGlobal, (match, mention) => { @@ -102,12 +106,11 @@ export async function parseMentions( } else if (mention.startsWith("/")) { const mentionPath = mention.slice(1) try { - const content = await getFileOrFolderContent(mentionPath, cwd) + const content = await getFileOrFolderContent(mentionPath, cwd, rooIgnoreController, showRooIgnoredFiles) if (mention.endsWith("/")) { parsedText += `\n\n\n${content}\n` } else { parsedText += `\n\n\n${content}\n` - // Track that this file was mentioned and its content was included if (fileContextTracker) { await fileContextTracker.trackFileContext(mentionPath, "file_mentioned") } @@ -161,8 +164,12 @@ export async function parseMentions( return parsedText } -async function getFileOrFolderContent(mentionPath: string, cwd: string): Promise { - // Unescape spaces in the path before resolving it +async function getFileOrFolderContent( + mentionPath: string, + cwd: string, + rooIgnoreController?: any, + showRooIgnoredFiles: boolean = true, +): Promise { const unescapedPath = unescapeSpaces(mentionPath) const absPath = path.resolve(cwd, unescapedPath) @@ -170,6 +177,9 @@ async function getFileOrFolderContent(mentionPath: string, cwd: string): Promise const stats = await fs.stat(absPath) if (stats.isFile()) { + if (rooIgnoreController && !rooIgnoreController.validateAccess(absPath)) { + return `(File ${mentionPath} is ignored by .rooignore)` + } try { const content = await extractTextFromFile(absPath) return content @@ -180,33 +190,51 @@ async function getFileOrFolderContent(mentionPath: string, cwd: string): Promise const entries = await fs.readdir(absPath, { withFileTypes: true }) let folderContent = "" const fileContentPromises: Promise[] = [] - entries.forEach((entry, index) => { + const LOCK_SYMBOL = "🔒" + + for (let index = 0; index < entries.length; index++) { + const entry = entries[index] const isLast = index === entries.length - 1 const linePrefix = isLast ? "└── " : "├── " + const entryPath = path.join(absPath, entry.name) + + let isIgnored = false + if (rooIgnoreController) { + isIgnored = !rooIgnoreController.validateAccess(entryPath) + } + + if (isIgnored && !showRooIgnoredFiles) { + continue + } + + const displayName = isIgnored ? `${LOCK_SYMBOL} ${entry.name}` : entry.name + if (entry.isFile()) { - folderContent += `${linePrefix}${entry.name}\n` - const filePath = path.join(mentionPath, entry.name) - const absoluteFilePath = path.resolve(absPath, entry.name) - fileContentPromises.push( - (async () => { - try { - const isBinary = await isBinaryFile(absoluteFilePath).catch(() => false) - if (isBinary) { + folderContent += `${linePrefix}${displayName}\n` + if (!isIgnored) { + const filePath = path.join(mentionPath, entry.name) + const absoluteFilePath = path.resolve(absPath, entry.name) + fileContentPromises.push( + (async () => { + try { + const isBinary = await isBinaryFile(absoluteFilePath).catch(() => false) + if (isBinary) { + return undefined + } + const content = await extractTextFromFile(absoluteFilePath) + return `\n${content}\n` + } catch (error) { return undefined } - const content = await extractTextFromFile(absoluteFilePath) - return `\n${content}\n` - } catch (error) { - return undefined - } - })(), - ) + })(), + ) + } } else if (entry.isDirectory()) { - folderContent += `${linePrefix}${entry.name}/\n` + folderContent += `${linePrefix}${displayName}/\n` } else { - folderContent += `${linePrefix}${entry.name}\n` + folderContent += `${linePrefix}${displayName}\n` } - }) + } const fileContents = (await Promise.all(fileContentPromises)).filter((content) => content) return `${folderContent}\n${fileContents.join("\n\n")}`.trim() } else { diff --git a/src/core/mentions/processUserContentMentions.ts b/src/core/mentions/processUserContentMentions.ts index 2b69486a86..3f131a1c05 100644 --- a/src/core/mentions/processUserContentMentions.ts +++ b/src/core/mentions/processUserContentMentions.ts @@ -11,11 +11,15 @@ export async function processUserContentMentions({ cwd, urlContentFetcher, fileContextTracker, + rooIgnoreController, + showRooIgnoredFiles = true, }: { userContent: Anthropic.Messages.ContentBlockParam[] cwd: string urlContentFetcher: UrlContentFetcher fileContextTracker: FileContextTracker + rooIgnoreController?: any + showRooIgnoredFiles?: boolean }) { // Process userContent array, which contains various block types: // TextBlockParam, ImageBlockParam, ToolUseBlockParam, and ToolResultBlockParam. @@ -35,7 +39,14 @@ export async function processUserContentMentions({ if (shouldProcessMentions(block.text)) { return { ...block, - text: await parseMentions(block.text, cwd, urlContentFetcher, fileContextTracker), + text: await parseMentions( + block.text, + cwd, + urlContentFetcher, + fileContextTracker, + rooIgnoreController, + showRooIgnoredFiles, + ), } } @@ -45,7 +56,14 @@ export async function processUserContentMentions({ if (shouldProcessMentions(block.content)) { return { ...block, - content: await parseMentions(block.content, cwd, urlContentFetcher, fileContextTracker), + content: await parseMentions( + block.content, + cwd, + urlContentFetcher, + fileContextTracker, + rooIgnoreController, + showRooIgnoredFiles, + ), } } @@ -61,6 +79,8 @@ export async function processUserContentMentions({ cwd, urlContentFetcher, fileContextTracker, + rooIgnoreController, + showRooIgnoredFiles, ), } } diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 667c1ba3ba..32a34098bc 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -1107,11 +1107,15 @@ export class Task extends EventEmitter { }), ) + const { showRooIgnoredFiles = true } = (await this.providerRef.deref()?.getState()) ?? {} + const parsedUserContent = await processUserContentMentions({ userContent, cwd: this.cwd, urlContentFetcher: this.urlContentFetcher, fileContextTracker: this.fileContextTracker, + rooIgnoreController: this.rooIgnoreController, + showRooIgnoredFiles, }) const environmentDetails = await getEnvironmentDetails(this, includeFileDetails) From 4cf240f3db8d18cd5645720ee97c0fc6c2b35a4d Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Wed, 28 May 2025 22:36:19 -0400 Subject: [PATCH 050/104] Check rooignore for insert_content and search_and_replace (#4094) --- src/core/tools/insertContentTool.ts | 8 ++++++++ src/core/tools/searchAndReplaceTool.ts | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/src/core/tools/insertContentTool.ts b/src/core/tools/insertContentTool.ts index bcb217326a..910bed5fe4 100644 --- a/src/core/tools/insertContentTool.ts +++ b/src/core/tools/insertContentTool.ts @@ -58,6 +58,14 @@ export async function insertContentTool( return } + const accessAllowed = cline.rooIgnoreController?.validateAccess(relPath) + + if (!accessAllowed) { + await cline.say("rooignore_error", relPath) + pushToolResult(formatResponse.toolError(formatResponse.rooIgnoreError(relPath))) + return + } + const absolutePath = path.resolve(cline.cwd, relPath) const fileExists = await fileExistsAtPath(absolutePath) diff --git a/src/core/tools/searchAndReplaceTool.ts b/src/core/tools/searchAndReplaceTool.ts index 417e2046df..de98fcafea 100644 --- a/src/core/tools/searchAndReplaceTool.ts +++ b/src/core/tools/searchAndReplaceTool.ts @@ -115,6 +115,14 @@ export async function searchAndReplaceTool( endLine: endLine, } + const accessAllowed = cline.rooIgnoreController?.validateAccess(validRelPath) + + if (!accessAllowed) { + await cline.say("rooignore_error", validRelPath) + pushToolResult(formatResponse.toolError(formatResponse.rooIgnoreError(validRelPath))) + return + } + const absolutePath = path.resolve(cline.cwd, validRelPath) const fileExists = await fileExistsAtPath(absolutePath) From 7820b7517ab210925b0604444bb71aaba2482005 Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Wed, 28 May 2025 20:43:30 -0700 Subject: [PATCH 051/104] Roo Code Cloud (#4069) Co-authored-by: John Richmond <5629+jr@users.noreply.github.com> --- .changeset/swift-carrots-doubt.md | 5 + .env.sample | 4 + .github/workflows/code-qa.yml | 7 - .github/workflows/codeql.yml | 2 +- .github/workflows/marketplace-publish.yml | 1 + .github/workflows/nightly-publish.yml | 2 - packages/cloud/eslint.config.mjs | 4 + packages/cloud/package.json | 25 + packages/cloud/src/AuthService.ts | 395 ++++ packages/cloud/src/CloudService.ts | 157 ++ packages/cloud/src/Config.ts | 2 + .../cloud/src/RefreshTimer.ts | 2 +- packages/cloud/src/RooCodeTelemetryClient.ts | 87 + packages/cloud/src/SettingsService.ts | 137 ++ packages/cloud/src/TelemetryClient.ts | 87 + packages/cloud/src/__mocks__/vscode.ts | 50 + .../cloud/src/__tests__/CloudService.test.ts | 238 +++ .../cloud/src/__tests__/RefreshTimer.test.ts | 44 +- .../__tests__/RooCodeTelemetryClient.test.ts | 250 +++ .../src/__tests__/TelemetryClient.test.ts | 250 +++ packages/cloud/src/index.ts | 1 + packages/cloud/src/types.ts | 6 + packages/cloud/tsconfig.json | 5 + packages/cloud/vitest.config.ts | 13 + .../config-typescript/vscode-library.json | 12 + packages/telemetry/eslint.config.mjs | 4 + packages/telemetry/package.json | 25 + .../telemetry/src}/BaseTelemetryClient.ts | 10 +- .../telemetry/src}/PostHogTelemetryClient.ts | 14 +- .../telemetry/src}/TelemetryService.ts | 63 +- .../__tests__/PostHogTelemetryClient.test.ts | 88 +- packages/telemetry/src/index.ts | 3 + packages/telemetry/tsconfig.json | 5 + packages/telemetry/vitest.config.ts | 8 + packages/types/npm/package.json | 2 +- packages/types/src/cloud.ts | 49 + packages/types/src/index.ts | 1 + packages/types/src/telemetry.ts | 57 +- packages/types/src/vscode.ts | 1 + pnpm-lock.yaml | 65 +- src/activate/handleUri.ts | 9 + src/activate/registerCommands.ts | 25 +- .../presentAssistantMessage.ts | 7 +- src/core/checkpoints/index.ts | 9 +- src/core/condense/__tests__/index.test.ts | 23 +- src/core/condense/index.ts | 26 +- src/core/config/ContextProxy.ts | 8 +- src/core/config/ProviderSettingsManager.ts | 7 +- .../config/__tests__/importExport.test.ts | 5 + src/core/config/importExport.ts | 5 +- .../__tests__/sliding-window.test.ts | 1765 +++++++++-------- src/core/sliding-window/index.ts | 6 +- src/core/task/Task.ts | 56 +- src/core/task/__tests__/Task.test.ts | 12 +- src/core/tools/applyDiffTool.ts | 5 +- src/core/tools/attemptCompletionTool.ts | 9 +- src/core/tools/executeCommandTool.ts | 4 +- src/core/webview/ClineProvider.ts | 76 +- .../webview/__tests__/ClineProvider.test.ts | 16 +- src/core/webview/webviewMessageHandler.ts | 50 +- src/esbuild.mjs | 6 - src/extension.ts | 52 +- src/i18n/locales/ca/common.json | 1 + src/i18n/locales/de/common.json | 1 + src/i18n/locales/en/common.json | 1 + src/i18n/locales/es/common.json | 1 + src/i18n/locales/fr/common.json | 1 + src/i18n/locales/hi/common.json | 1 + src/i18n/locales/it/common.json | 1 + src/i18n/locales/ja/common.json | 1 + src/i18n/locales/ko/common.json | 1 + src/i18n/locales/nl/common.json | 1 + src/i18n/locales/pl/common.json | 1 + src/i18n/locales/pt-BR/common.json | 1 + src/i18n/locales/ru/common.json | 1 + src/i18n/locales/tr/common.json | 1 + src/i18n/locales/vi/common.json | 1 + src/i18n/locales/zh-CN/common.json | 1 + src/i18n/locales/zh-TW/common.json | 1 + src/package.json | 28 +- src/package.nls.ca.json | 3 +- src/package.nls.de.json | 3 +- src/package.nls.es.json | 3 +- src/package.nls.fr.json | 3 +- src/package.nls.hi.json | 3 +- src/package.nls.it.json | 3 +- src/package.nls.ja.json | 3 +- src/package.nls.json | 3 +- src/package.nls.ko.json | 3 +- src/package.nls.nl.json | 3 +- src/package.nls.pl.json | 3 +- src/package.nls.pt-BR.json | 3 +- src/package.nls.ru.json | 3 +- src/package.nls.tr.json | 3 +- src/package.nls.vi.json | 3 +- src/package.nls.zh-CN.json | 3 +- src/package.nls.zh-TW.json | 3 +- src/services/telemetry/index.ts | 2 - src/services/telemetry/types.ts | 19 - src/shared/ExtensionMessage.ts | 9 + src/shared/ProfileValidator.ts | 93 + src/shared/WebviewMessage.ts | 3 + src/shared/__tests__/ProfileValidator.test.ts | 346 ++++ src/vitest.config.ts | 5 - webview-ui/src/App.tsx | 11 +- .../src/components/account/AccountView.tsx | 77 + webview-ui/src/components/chat/ChatView.tsx | 20 +- .../chat/ProfileViolationWarning.tsx | 17 + .../components/settings/ApiConfigManager.tsx | 75 +- .../src/components/settings/ApiOptions.tsx | 46 +- .../src/components/settings/ModelPicker.tsx | 12 +- .../settings/__tests__/ModelPicker.test.tsx | 1 + .../components/settings/providers/Glama.tsx | 12 +- .../components/settings/providers/LiteLLM.tsx | 6 +- .../settings/providers/OpenAICompatible.tsx | 10 +- .../settings/providers/OpenRouter.tsx | 5 +- .../settings/providers/Requesty.tsx | 5 +- .../components/settings/providers/Unbound.tsx | 11 +- .../__tests__/organizationFilters.test.ts | 121 ++ .../settings/utils/organizationFilters.ts | 53 + .../src/context/ExtensionStateContext.tsx | 18 +- .../__tests__/ExtensionStateContext.test.tsx | 1 + webview-ui/src/i18n/locales/ca/account.json | 10 + webview-ui/src/i18n/locales/ca/chat.json | 1 + webview-ui/src/i18n/locales/ca/settings.json | 5 +- webview-ui/src/i18n/locales/de/account.json | 10 + webview-ui/src/i18n/locales/de/chat.json | 1 + webview-ui/src/i18n/locales/de/settings.json | 5 +- webview-ui/src/i18n/locales/en/account.json | 10 + webview-ui/src/i18n/locales/en/chat.json | 1 + webview-ui/src/i18n/locales/en/settings.json | 5 +- webview-ui/src/i18n/locales/es/account.json | 10 + webview-ui/src/i18n/locales/es/chat.json | 1 + webview-ui/src/i18n/locales/es/settings.json | 5 +- webview-ui/src/i18n/locales/fr/account.json | 10 + webview-ui/src/i18n/locales/fr/chat.json | 1 + webview-ui/src/i18n/locales/fr/settings.json | 5 +- webview-ui/src/i18n/locales/hi/account.json | 10 + webview-ui/src/i18n/locales/hi/chat.json | 1 + webview-ui/src/i18n/locales/hi/settings.json | 5 +- webview-ui/src/i18n/locales/it/account.json | 10 + webview-ui/src/i18n/locales/it/chat.json | 1 + webview-ui/src/i18n/locales/it/settings.json | 5 +- webview-ui/src/i18n/locales/ja/account.json | 10 + webview-ui/src/i18n/locales/ja/chat.json | 1 + webview-ui/src/i18n/locales/ja/settings.json | 5 +- webview-ui/src/i18n/locales/ko/account.json | 10 + webview-ui/src/i18n/locales/ko/chat.json | 1 + webview-ui/src/i18n/locales/ko/settings.json | 5 +- webview-ui/src/i18n/locales/nl/account.json | 10 + webview-ui/src/i18n/locales/nl/chat.json | 1 + webview-ui/src/i18n/locales/nl/settings.json | 5 +- webview-ui/src/i18n/locales/pl/account.json | 10 + webview-ui/src/i18n/locales/pl/chat.json | 1 + webview-ui/src/i18n/locales/pl/settings.json | 5 +- .../src/i18n/locales/pt-BR/account.json | 10 + webview-ui/src/i18n/locales/pt-BR/chat.json | 1 + .../src/i18n/locales/pt-BR/settings.json | 5 +- webview-ui/src/i18n/locales/ru/account.json | 10 + webview-ui/src/i18n/locales/ru/chat.json | 1 + webview-ui/src/i18n/locales/ru/settings.json | 5 +- webview-ui/src/i18n/locales/tr/account.json | 10 + webview-ui/src/i18n/locales/tr/chat.json | 1 + webview-ui/src/i18n/locales/tr/settings.json | 5 +- webview-ui/src/i18n/locales/vi/account.json | 10 + webview-ui/src/i18n/locales/vi/chat.json | 1 + webview-ui/src/i18n/locales/vi/settings.json | 5 +- .../src/i18n/locales/zh-CN/account.json | 10 + webview-ui/src/i18n/locales/zh-CN/chat.json | 1 + .../src/i18n/locales/zh-CN/settings.json | 5 +- .../src/i18n/locales/zh-TW/account.json | 10 + webview-ui/src/i18n/locales/zh-TW/chat.json | 1 + .../src/i18n/locales/zh-TW/settings.json | 5 +- webview-ui/src/utils/validate.ts | 78 +- 174 files changed, 4549 insertions(+), 1237 deletions(-) create mode 100644 .changeset/swift-carrots-doubt.md create mode 100644 packages/cloud/eslint.config.mjs create mode 100644 packages/cloud/package.json create mode 100644 packages/cloud/src/AuthService.ts create mode 100644 packages/cloud/src/CloudService.ts create mode 100644 packages/cloud/src/Config.ts rename src/utils/refresh-timer.ts => packages/cloud/src/RefreshTimer.ts (99%) create mode 100644 packages/cloud/src/RooCodeTelemetryClient.ts create mode 100644 packages/cloud/src/SettingsService.ts create mode 100644 packages/cloud/src/TelemetryClient.ts create mode 100644 packages/cloud/src/__mocks__/vscode.ts create mode 100644 packages/cloud/src/__tests__/CloudService.test.ts rename src/utils/__tests__/refresh-timer.test.ts => packages/cloud/src/__tests__/RefreshTimer.test.ts (86%) create mode 100644 packages/cloud/src/__tests__/RooCodeTelemetryClient.test.ts create mode 100644 packages/cloud/src/__tests__/TelemetryClient.test.ts create mode 100644 packages/cloud/src/index.ts create mode 100644 packages/cloud/src/types.ts create mode 100644 packages/cloud/tsconfig.json create mode 100644 packages/cloud/vitest.config.ts create mode 100644 packages/config-typescript/vscode-library.json create mode 100644 packages/telemetry/eslint.config.mjs create mode 100644 packages/telemetry/package.json rename {src/services/telemetry/clients => packages/telemetry/src}/BaseTelemetryClient.ts (90%) rename {src/services/telemetry/clients => packages/telemetry/src}/PostHogTelemetryClient.ts (86%) rename {src/services/telemetry => packages/telemetry/src}/TelemetryService.ts (84%) rename {src/services/telemetry/clients => packages/telemetry/src}/__tests__/PostHogTelemetryClient.test.ts (71%) create mode 100644 packages/telemetry/src/index.ts create mode 100644 packages/telemetry/tsconfig.json create mode 100644 packages/telemetry/vitest.config.ts create mode 100644 packages/types/src/cloud.ts delete mode 100644 src/services/telemetry/index.ts delete mode 100644 src/services/telemetry/types.ts create mode 100644 src/shared/ProfileValidator.ts create mode 100644 src/shared/__tests__/ProfileValidator.test.ts create mode 100644 webview-ui/src/components/account/AccountView.tsx create mode 100644 webview-ui/src/components/chat/ProfileViolationWarning.tsx create mode 100644 webview-ui/src/components/settings/utils/__tests__/organizationFilters.test.ts create mode 100644 webview-ui/src/components/settings/utils/organizationFilters.ts create mode 100644 webview-ui/src/i18n/locales/ca/account.json create mode 100644 webview-ui/src/i18n/locales/de/account.json create mode 100644 webview-ui/src/i18n/locales/en/account.json create mode 100644 webview-ui/src/i18n/locales/es/account.json create mode 100644 webview-ui/src/i18n/locales/fr/account.json create mode 100644 webview-ui/src/i18n/locales/hi/account.json create mode 100644 webview-ui/src/i18n/locales/it/account.json create mode 100644 webview-ui/src/i18n/locales/ja/account.json create mode 100644 webview-ui/src/i18n/locales/ko/account.json create mode 100644 webview-ui/src/i18n/locales/nl/account.json create mode 100644 webview-ui/src/i18n/locales/pl/account.json create mode 100644 webview-ui/src/i18n/locales/pt-BR/account.json create mode 100644 webview-ui/src/i18n/locales/ru/account.json create mode 100644 webview-ui/src/i18n/locales/tr/account.json create mode 100644 webview-ui/src/i18n/locales/vi/account.json create mode 100644 webview-ui/src/i18n/locales/zh-CN/account.json create mode 100644 webview-ui/src/i18n/locales/zh-TW/account.json diff --git a/.changeset/swift-carrots-doubt.md b/.changeset/swift-carrots-doubt.md new file mode 100644 index 0000000000..567ed0e109 --- /dev/null +++ b/.changeset/swift-carrots-doubt.md @@ -0,0 +1,5 @@ +--- +"roo-cline": patch +--- + +Roo Code Cloud diff --git a/.env.sample b/.env.sample index 4d6c24ac72..d89ef72792 100644 --- a/.env.sample +++ b/.env.sample @@ -1 +1,5 @@ POSTHOG_API_KEY=key-goes-here + +# Roo Code Cloud / Local Development +CLERK_BASE_URL=https://epic-chamois-85.clerk.accounts.dev +ROO_CODE_API_URL=http://localhost:3000 diff --git a/.github/workflows/code-qa.yml b/.github/workflows/code-qa.yml index 271ecc1f28..1ca5d8151a 100644 --- a/.github/workflows/code-qa.yml +++ b/.github/workflows/code-qa.yml @@ -133,10 +133,3 @@ jobs: - name: Run integration tests working-directory: apps/vscode-e2e run: xvfb-run -a pnpm test:ci - - qa: - needs: [check-translations, knip, compile, platform-unit-test, integration-test] - runs-on: ubuntu-latest - steps: - - name: NO-OP - run: echo "All tests passed." diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 71e7fb27e4..0784c8cbad 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,4 +1,4 @@ -name: "CodeQL Advanced" +name: CodeQL Advanced on: push: diff --git a/.github/workflows/marketplace-publish.yml b/.github/workflows/marketplace-publish.yml index 6e2dc09a01..bea1ecfa6d 100644 --- a/.github/workflows/marketplace-publish.yml +++ b/.github/workflows/marketplace-publish.yml @@ -1,4 +1,5 @@ name: Publish Extension + on: pull_request: types: [closed] diff --git a/.github/workflows/nightly-publish.yml b/.github/workflows/nightly-publish.yml index 5c28052426..14bb0212b1 100644 --- a/.github/workflows/nightly-publish.yml +++ b/.github/workflows/nightly-publish.yml @@ -1,8 +1,6 @@ name: Nightly Publish on: - # push: - # branches: [main] workflow_run: workflows: ["Code QA Roo Code"] types: diff --git a/packages/cloud/eslint.config.mjs b/packages/cloud/eslint.config.mjs new file mode 100644 index 0000000000..694bf73664 --- /dev/null +++ b/packages/cloud/eslint.config.mjs @@ -0,0 +1,4 @@ +import { config } from "@roo-code/config-eslint/base" + +/** @type {import("eslint").Linter.Config} */ +export default [...config] diff --git a/packages/cloud/package.json b/packages/cloud/package.json new file mode 100644 index 0000000000..62eff7b694 --- /dev/null +++ b/packages/cloud/package.json @@ -0,0 +1,25 @@ +{ + "name": "@roo-code/cloud", + "description": "Roo Code Cloud VSCode integration.", + "private": true, + "type": "module", + "exports": "./src/index.ts", + "scripts": { + "lint": "eslint src --ext=ts --max-warnings=0", + "check-types": "tsc --noEmit", + "test": "vitest --globals --run", + "clean": "rimraf dist .turbo" + }, + "dependencies": { + "@roo-code/telemetry": "workspace:^", + "@roo-code/types": "workspace:^", + "axios": "^1.7.4" + }, + "devDependencies": { + "@roo-code/config-eslint": "workspace:^", + "@roo-code/config-typescript": "workspace:^", + "@types/node": "^22.15.20", + "@types/vscode": "^1.84.0", + "vitest": "^3.1.3" + } +} diff --git a/packages/cloud/src/AuthService.ts b/packages/cloud/src/AuthService.ts new file mode 100644 index 0000000000..85954a68aa --- /dev/null +++ b/packages/cloud/src/AuthService.ts @@ -0,0 +1,395 @@ +import crypto from "crypto" +import EventEmitter from "events" + +import axios from "axios" +import * as vscode from "vscode" + +import type { CloudUserInfo } from "@roo-code/types" + +import { CloudServiceCallbacks } from "./types" +import { getClerkBaseUrl, getRooCodeApiUrl } from "./Config" +import { RefreshTimer } from "./RefreshTimer" + +export interface AuthServiceEvents { + "active-session": [data: { previousState: AuthState }] + "logged-out": [data: { previousState: AuthState }] +} + +const CLIENT_TOKEN_KEY = "clerk-client-token" +const SESSION_ID_KEY = "clerk-session-id" +const AUTH_STATE_KEY = "clerk-auth-state" + +type AuthState = "initializing" | "logged-out" | "active-session" | "inactive-session" + +export class AuthService extends EventEmitter { + private context: vscode.ExtensionContext + private userChanged: CloudServiceCallbacks["userChanged"] + private timer: RefreshTimer + private state: AuthState = "initializing" + + private clientToken: string | null = null + private sessionToken: string | null = null + private sessionId: string | null = null + + constructor(context: vscode.ExtensionContext, userChanged: CloudServiceCallbacks["userChanged"]) { + super() + + this.context = context + this.userChanged = userChanged + + this.timer = new RefreshTimer({ + callback: async () => { + await this.refreshSession() + return true + }, + successInterval: 50_000, + initialBackoffMs: 1_000, + maxBackoffMs: 300_000, + }) + } + + /** + * Initialize the auth state + * + * This method loads tokens from storage and determines the current auth state. + * It also starts the refresh timer if we have an active session. + */ + public async initialize(): Promise { + if (this.state !== "initializing") { + console.log("[auth] initialize() called after already initialized") + return + } + + try { + this.clientToken = (await this.context.secrets.get(CLIENT_TOKEN_KEY)) || null + this.sessionId = this.context.globalState.get(SESSION_ID_KEY) || null + + // Determine initial state. + if (!this.clientToken || !this.sessionId) { + // TODO: it may be possible to get a new session with the client, + // but the obvious Clerk endpoints don't support that. + const previousState = this.state + this.state = "logged-out" + this.emit("logged-out", { previousState }) + } else { + this.state = "inactive-session" + this.timer.start() + } + + console.log(`[auth] Initialized with state: ${this.state}`) + } catch (error) { + console.error(`[auth] Error initializing AuthService: ${error}`) + this.state = "logged-out" + } + } + + /** + * Start the login process + * + * This method initiates the authentication flow by generating a state parameter + * and opening the browser to the authorization URL. + */ + public async login(): Promise { + try { + // Generate a cryptographically random state parameter. + const state = crypto.randomBytes(16).toString("hex") + await this.context.globalState.update(AUTH_STATE_KEY, state) + const uri = vscode.Uri.parse(`${getRooCodeApiUrl()}/extension/sign-in?state=${state}`) + await vscode.env.openExternal(uri) + } catch (error) { + console.error(`[auth] Error initiating Roo Code Cloud auth: ${error}`) + throw new Error(`Failed to initiate Roo Code Cloud authentication: ${error}`) + } + } + + /** + * Handle the callback from Roo Code Cloud + * + * This method is called when the user is redirected back to the extension + * after authenticating with Roo Code Cloud. + * + * @param code The authorization code from the callback + * @param state The state parameter from the callback + */ + public async handleCallback(code: string | null, state: string | null): Promise { + if (!code || !state) { + vscode.window.showInformationMessage("Invalid Roo Code Cloud sign in url") + return + } + + try { + // Validate state parameter to prevent CSRF attacks. + const storedState = this.context.globalState.get(AUTH_STATE_KEY) + + if (state !== storedState) { + console.log("[auth] State mismatch in callback") + throw new Error("Invalid state parameter. Authentication request may have been tampered with.") + } + + const { clientToken, sessionToken, sessionId } = await this.clerkSignIn(code) + + await this.context.secrets.store(CLIENT_TOKEN_KEY, clientToken) + await this.context.globalState.update(SESSION_ID_KEY, sessionId) + + this.clientToken = clientToken + this.sessionId = sessionId + this.sessionToken = sessionToken + + const previousState = this.state + this.state = "active-session" + this.emit("active-session", { previousState }) + this.timer.start() + + if (this.userChanged) { + this.getUserInfo().then(this.userChanged) + } + + vscode.window.showInformationMessage("Successfully authenticated with Roo Code Cloud") + console.log("[auth] Successfully authenticated with Roo Code Cloud") + } catch (error) { + console.log(`[auth] Error handling Roo Code Cloud callback: ${error}`) + const previousState = this.state + this.state = "logged-out" + this.emit("logged-out", { previousState }) + throw new Error(`Failed to handle Roo Code Cloud callback: ${error}`) + } + } + + /** + * Log out + * + * This method removes all stored tokens and stops the refresh timer. + */ + public async logout(): Promise { + try { + this.timer.stop() + + await this.context.secrets.delete(CLIENT_TOKEN_KEY) + await this.context.globalState.update(SESSION_ID_KEY, undefined) + await this.context.globalState.update(AUTH_STATE_KEY, undefined) + + const oldClientToken = this.clientToken + const oldSessionId = this.sessionId + + this.clientToken = null + this.sessionToken = null + this.sessionId = null + const previousState = this.state + this.state = "logged-out" + this.emit("logged-out", { previousState }) + + if (oldClientToken && oldSessionId) { + await this.clerkLogout(oldClientToken, oldSessionId) + } + + if (this.userChanged) { + this.getUserInfo().then(this.userChanged) + } + + vscode.window.showInformationMessage("Logged out from Roo Code Cloud") + console.log("[auth] Logged out from Roo Code Cloud") + } catch (error) { + console.log(`[auth] Error logging out from Roo Code Cloud: ${error}`) + throw new Error(`Failed to log out from Roo Code Cloud: ${error}`) + } + } + + public getState(): AuthState { + return this.state + } + + public getSessionToken(): string | undefined { + if (this.state === "active-session" && this.sessionToken) { + return this.sessionToken + } + + return + } + + /** + * Check if the user is authenticated + * + * @returns True if the user is authenticated (has an active or inactive session) + */ + public isAuthenticated(): boolean { + return this.state === "active-session" || this.state === "inactive-session" + } + + public hasActiveSession(): boolean { + return this.state === "active-session" + } + + /** + * Refresh the session + * + * This method refreshes the session token using the client token. + */ + private async refreshSession() { + if (!this.sessionId || !this.clientToken) { + console.log("[auth] Cannot refresh session: missing session ID or token") + this.state = "inactive-session" + return + } + + const previousState = this.state + this.sessionToken = await this.clerkCreateSessionToken() + this.state = "active-session" + + if (previousState !== "active-session") { + this.emit("active-session", { previousState }) + + if (this.userChanged) { + this.getUserInfo().then(this.userChanged) + } + } + } + + /** + * Extract user information from the ID token + * + * @returns User information from ID token claims or null if no ID token available + */ + public async getUserInfo(): Promise { + if (!this.clientToken) { + return undefined + } + + return await this.clerkMe() + } + + private async clerkSignIn( + ticket: string, + ): Promise<{ clientToken: string; sessionToken: string; sessionId: string }> { + const formData = new URLSearchParams() + formData.append("strategy", "ticket") + formData.append("ticket", ticket) + + const response = await axios.post(`${getClerkBaseUrl()}/v1/client/sign_ins`, formData, { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + "User-Agent": this.userAgent(), + }, + }) + + // 3. Extract the client token from the Authorization header. + const clientToken = response.headers.authorization + + if (!clientToken) { + throw new Error("No authorization header found in the response") + } + + // 4. Find the session using created_session_id and extract the JWT. + const createdSessionId = response.data?.response?.created_session_id + + if (!createdSessionId) { + throw new Error("No session ID found in the response") + } + + // Find the session in the client sessions array. + const session = response.data?.client?.sessions?.find((s: { id: string }) => s.id === createdSessionId) + + if (!session) { + throw new Error("Session not found in the response") + } + + // Extract the session token (JWT) and store it. + const sessionToken = session.last_active_token?.jwt + + if (!sessionToken) { + throw new Error("Session does not have a token") + } + + return { clientToken, sessionToken, sessionId: session.id } + } + + private async clerkCreateSessionToken(): Promise { + const formData = new URLSearchParams() + formData.append("_is_native", "1") + + const response = await axios.post( + `${getClerkBaseUrl()}/v1/client/sessions/${this.sessionId}/tokens`, + formData, + { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + Authorization: `Bearer ${this.clientToken}`, + "User-Agent": this.userAgent(), + }, + }, + ) + + const sessionToken = response.data?.jwt + + if (!sessionToken) { + throw new Error("No JWT found in refresh response") + } + + return sessionToken + } + + private async clerkMe(): Promise { + const response = await axios.get(`${getClerkBaseUrl()}/v1/me`, { + headers: { + Authorization: `Bearer ${this.clientToken}`, + "User-Agent": this.userAgent(), + }, + }) + + const userData = response.data?.response + + if (!userData) { + throw new Error("No response user data") + } + + const userInfo: CloudUserInfo = {} + + userInfo.name = `${userData?.first_name} ${userData?.last_name}` + const primaryEmailAddressId = userData?.primary_email_address_id + const emailAddresses = userData?.email_addresses + + if (primaryEmailAddressId && emailAddresses) { + userInfo.email = emailAddresses.find( + (email: { id: string }) => primaryEmailAddressId === email?.id, + )?.email_address + } + + userInfo.picture = userData?.image_url + return userInfo + } + + private async clerkLogout(clientToken: string, sessionId: string): Promise { + const formData = new URLSearchParams() + formData.append("_is_native", "1") + + await axios.post(`${getClerkBaseUrl()}/v1/client/sessions/${sessionId}/remove`, formData, { + headers: { + Authorization: `Bearer ${clientToken}`, + "User-Agent": this.userAgent(), + }, + }) + } + + private userAgent(): string { + return `Roo-Code ${this.context.extension?.packageJSON?.version}` + } + + private static _instance: AuthService | null = null + + static get instance() { + if (!this._instance) { + throw new Error("AuthService not initialized") + } + + return this._instance + } + + static async createInstance(context: vscode.ExtensionContext, userChanged: CloudServiceCallbacks["userChanged"]) { + if (this._instance) { + throw new Error("AuthService instance already created") + } + + this._instance = new AuthService(context, userChanged) + await this._instance.initialize() + return this._instance + } +} diff --git a/packages/cloud/src/CloudService.ts b/packages/cloud/src/CloudService.ts new file mode 100644 index 0000000000..72cbb70b22 --- /dev/null +++ b/packages/cloud/src/CloudService.ts @@ -0,0 +1,157 @@ +import * as vscode from "vscode" + +import type { CloudUserInfo, TelemetryEvent, OrganizationAllowList } from "@roo-code/types" +import { TelemetryService } from "@roo-code/telemetry" + +import { CloudServiceCallbacks } from "./types" +import { AuthService } from "./AuthService" +import { SettingsService } from "./SettingsService" +import { TelemetryClient } from "./TelemetryClient" + +export class CloudService { + private static _instance: CloudService | null = null + + private context: vscode.ExtensionContext + private callbacks: CloudServiceCallbacks + private authService: AuthService | null = null + private settingsService: SettingsService | null = null + private telemetryClient: TelemetryClient | null = null + private isInitialized = false + + private constructor(context: vscode.ExtensionContext, callbacks: CloudServiceCallbacks) { + this.context = context + this.callbacks = callbacks + } + + public async initialize(): Promise { + if (this.isInitialized) { + return + } + + try { + this.authService = await AuthService.createInstance(this.context, (userInfo) => { + this.callbacks.userChanged?.(userInfo) + }) + + this.settingsService = await SettingsService.createInstance(this.context, () => + this.callbacks.settingsChanged?.(), + ) + + this.telemetryClient = new TelemetryClient(this.authService) + + try { + TelemetryService.instance.register(this.telemetryClient) + } catch (error) { + console.warn("[CloudService] Failed to register TelemetryClient:", error) + } + + this.isInitialized = true + } catch (error) { + console.error("[CloudService] Failed to initialize:", error) + throw new Error(`Failed to initialize CloudService: ${error}`) + } + } + + // AuthService + + public async login(): Promise { + this.ensureInitialized() + return this.authService!.login() + } + + public async logout(): Promise { + this.ensureInitialized() + return this.authService!.logout() + } + + public isAuthenticated(): boolean { + this.ensureInitialized() + return this.authService!.isAuthenticated() + } + + public hasActiveSession(): boolean { + this.ensureInitialized() + return this.authService!.hasActiveSession() + } + + public async getUserInfo(): Promise { + this.ensureInitialized() + return this.authService!.getUserInfo() + } + + public getAuthState(): string { + this.ensureInitialized() + return this.authService!.getState() + } + + public async handleAuthCallback(code: string | null, state: string | null): Promise { + this.ensureInitialized() + return this.authService!.handleCallback(code, state) + } + + // SettingsService + + public getAllowList(): OrganizationAllowList { + this.ensureInitialized() + return this.settingsService!.getAllowList() + } + + // TelemetryClient + + public captureEvent(event: TelemetryEvent): void { + this.ensureInitialized() + this.telemetryClient!.capture(event) + } + + // Lifecycle + + public dispose(): void { + if (this.settingsService) { + this.settingsService.dispose() + } + + this.isInitialized = false + } + + private ensureInitialized(): void { + if (!this.isInitialized || !this.authService || !this.settingsService || !this.telemetryClient) { + throw new Error("CloudService not initialized.") + } + } + + static get instance(): CloudService { + if (!this._instance) { + throw new Error("CloudService not initialized") + } + + return this._instance + } + + static async createInstance( + context: vscode.ExtensionContext, + callbacks: CloudServiceCallbacks = {}, + ): Promise { + if (this._instance) { + throw new Error("CloudService instance already created") + } + + this._instance = new CloudService(context, callbacks) + await this._instance.initialize() + return this._instance + } + + static hasInstance(): boolean { + return this._instance !== null && this._instance.isInitialized + } + + static resetInstance(): void { + if (this._instance) { + this._instance.dispose() + this._instance = null + } + } + + static isEnabled(): boolean { + return !!this._instance?.isAuthenticated() + } +} diff --git a/packages/cloud/src/Config.ts b/packages/cloud/src/Config.ts new file mode 100644 index 0000000000..0205e5b0e3 --- /dev/null +++ b/packages/cloud/src/Config.ts @@ -0,0 +1,2 @@ +export const getClerkBaseUrl = () => process.env.CLERK_BASE_URL || "https://clerk.roocode.com" +export const getRooCodeApiUrl = () => process.env.ROO_CODE_API_URL || "https://app.roocode.com" diff --git a/src/utils/refresh-timer.ts b/packages/cloud/src/RefreshTimer.ts similarity index 99% rename from src/utils/refresh-timer.ts rename to packages/cloud/src/RefreshTimer.ts index 3138031665..e7294222d7 100644 --- a/src/utils/refresh-timer.ts +++ b/packages/cloud/src/RefreshTimer.ts @@ -146,7 +146,7 @@ export class RefreshTimer { const result = await this.callback() this.scheduleNextAttempt(result) - } catch (error) { + } catch (_error) { // Treat errors as failed attempts this.scheduleNextAttempt(false) } diff --git a/packages/cloud/src/RooCodeTelemetryClient.ts b/packages/cloud/src/RooCodeTelemetryClient.ts new file mode 100644 index 0000000000..661c5179b3 --- /dev/null +++ b/packages/cloud/src/RooCodeTelemetryClient.ts @@ -0,0 +1,87 @@ +import { TelemetryEventName, type TelemetryEvent, rooCodeTelemetryEventSchema } from "@roo-code/types" +import { BaseTelemetryClient } from "@roo-code/telemetry" + +import { getRooCodeApiUrl } from "./Config" +import { AuthService } from "./AuthService" + +export class RooCodeTelemetryClient extends BaseTelemetryClient { + constructor( + private authService: AuthService, + debug = false, + ) { + super( + { + type: "exclude", + events: [TelemetryEventName.TASK_CONVERSATION_MESSAGE], + }, + debug, + ) + } + + private async fetch(path: string, options: RequestInit) { + if (!this.authService.isAuthenticated()) { + return + } + + const token = this.authService.getSessionToken() + + if (!token) { + console.error(`[RooCodeTelemetryClient#fetch] Unauthorized: No session token available.`) + return + } + + const response = await fetch(`${getRooCodeApiUrl()}/api/${path}`, { + ...options, + headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" }, + }) + + if (!response.ok) { + console.error( + `[RooCodeTelemetryClient#fetch] ${options.method} ${path} -> ${response.status} ${response.statusText}`, + ) + } + } + + public override async capture(event: TelemetryEvent) { + if (!this.isTelemetryEnabled() || !this.isEventCapturable(event.event)) { + if (this.debug) { + console.info(`[RooCodeTelemetryClient#capture] Skipping event: ${event.event}`) + } + + return + } + + const payload = { + type: event.event, + properties: await this.getEventProperties(event), + } + + if (this.debug) { + console.info(`[RooCodeTelemetryClient#capture] ${JSON.stringify(payload)}`) + } + + const result = rooCodeTelemetryEventSchema.safeParse(payload) + + if (!result.success) { + console.error( + `[RooCodeTelemetryClient#capture] Invalid telemetry event: ${result.error.message} - ${JSON.stringify(payload)}`, + ) + + return + } + + try { + await this.fetch(`events`, { method: "POST", body: JSON.stringify(result.data) }) + } catch (error) { + console.error(`[RooCodeTelemetryClient#capture] Error sending telemetry event: ${error}`) + } + } + + public override updateTelemetryState(_didUserOptIn: boolean) {} + + public override isTelemetryEnabled(): boolean { + return true + } + + public override async shutdown() {} +} diff --git a/packages/cloud/src/SettingsService.ts b/packages/cloud/src/SettingsService.ts new file mode 100644 index 0000000000..516654e19d --- /dev/null +++ b/packages/cloud/src/SettingsService.ts @@ -0,0 +1,137 @@ +import * as vscode from "vscode" + +import { + ORGANIZATION_ALLOW_ALL, + OrganizationAllowList, + OrganizationSettings, + organizationSettingsSchema, +} from "@roo-code/types" + +import { getRooCodeApiUrl } from "./Config" +import { AuthService } from "./AuthService" +import { RefreshTimer } from "./RefreshTimer" + +const ORGANIZATION_SETTINGS_CACHE_KEY = "organization-settings" + +export class SettingsService { + private static _instance: SettingsService | null = null + + private context: vscode.ExtensionContext + private authService: AuthService + private settings: OrganizationSettings | undefined = undefined + private timer: RefreshTimer + + private constructor(context: vscode.ExtensionContext, authService: AuthService, callback: () => void) { + this.context = context + this.authService = authService + + this.timer = new RefreshTimer({ + callback: async () => { + await this.fetchSettings(callback) + return true + }, + successInterval: 30000, + initialBackoffMs: 1000, + maxBackoffMs: 30000, + }) + } + + public initialize(): void { + this.loadCachedSettings() + + this.authService.on("active-session", () => { + this.timer.start() + }) + + this.authService.on("logged-out", () => { + this.timer.stop() + this.removeSettings() + }) + + if (this.authService.hasActiveSession()) { + this.timer.start() + } + } + + private async fetchSettings(callback: () => void): Promise { + const token = this.authService.getSessionToken() + + if (!token) { + return + } + + try { + const response = await fetch(`${getRooCodeApiUrl()}/api/organization-settings`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + + if (!response.ok) { + console.error(`Failed to fetch organization settings: ${response.status} ${response.statusText}`) + return + } + + const data = await response.json() + const result = organizationSettingsSchema.safeParse(data) + + if (!result.success) { + console.error("Invalid organization settings format:", result.error) + return + } + + const newSettings = result.data + + if (!this.settings || this.settings.version !== newSettings.version) { + this.settings = newSettings + await this.cacheSettings() + callback() + } + } catch (error) { + console.error("Error fetching organization settings:", error) + } + } + + private async cacheSettings(): Promise { + await this.context.globalState.update(ORGANIZATION_SETTINGS_CACHE_KEY, this.settings) + } + + private loadCachedSettings(): void { + this.settings = this.context.globalState.get(ORGANIZATION_SETTINGS_CACHE_KEY) + } + + public getAllowList(): OrganizationAllowList { + return this.settings?.allowList || ORGANIZATION_ALLOW_ALL + } + + public getSettings(): OrganizationSettings | undefined { + return this.settings + } + + public async removeSettings(): Promise { + this.settings = undefined + await this.cacheSettings() + } + + public dispose(): void { + this.timer.stop() + } + + static get instance() { + if (!this._instance) { + throw new Error("SettingsService not initialized") + } + + return this._instance + } + + static async createInstance(context: vscode.ExtensionContext, callback: () => void) { + if (this._instance) { + throw new Error("SettingsService instance already created") + } + + this._instance = new SettingsService(context, AuthService.instance, callback) + this._instance.initialize() + return this._instance + } +} diff --git a/packages/cloud/src/TelemetryClient.ts b/packages/cloud/src/TelemetryClient.ts new file mode 100644 index 0000000000..6db8b1096d --- /dev/null +++ b/packages/cloud/src/TelemetryClient.ts @@ -0,0 +1,87 @@ +import { TelemetryEventName, type TelemetryEvent, rooCodeTelemetryEventSchema } from "@roo-code/types" +import { BaseTelemetryClient } from "@roo-code/telemetry" + +import { getRooCodeApiUrl } from "./Config" +import { AuthService } from "./AuthService" + +export class TelemetryClient extends BaseTelemetryClient { + constructor( + private authService: AuthService, + debug = false, + ) { + super( + { + type: "exclude", + events: [TelemetryEventName.TASK_CONVERSATION_MESSAGE], + }, + debug, + ) + } + + private async fetch(path: string, options: RequestInit) { + if (!this.authService.isAuthenticated()) { + return + } + + const token = this.authService.getSessionToken() + + if (!token) { + console.error(`[TelemetryClient#fetch] Unauthorized: No session token available.`) + return + } + + const response = await fetch(`${getRooCodeApiUrl()}/api/${path}`, { + ...options, + headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" }, + }) + + if (!response.ok) { + console.error( + `[TelemetryClient#fetch] ${options.method} ${path} -> ${response.status} ${response.statusText}`, + ) + } + } + + public override async capture(event: TelemetryEvent) { + if (!this.isTelemetryEnabled() || !this.isEventCapturable(event.event)) { + if (this.debug) { + console.info(`[TelemetryClient#capture] Skipping event: ${event.event}`) + } + + return + } + + const payload = { + type: event.event, + properties: await this.getEventProperties(event), + } + + if (this.debug) { + console.info(`[TelemetryClient#capture] ${JSON.stringify(payload)}`) + } + + const result = rooCodeTelemetryEventSchema.safeParse(payload) + + if (!result.success) { + console.error( + `[TelemetryClient#capture] Invalid telemetry event: ${result.error.message} - ${JSON.stringify(payload)}`, + ) + + return + } + + try { + await this.fetch(`events`, { method: "POST", body: JSON.stringify(result.data) }) + } catch (error) { + console.error(`[TelemetryClient#capture] Error sending telemetry event: ${error}`) + } + } + + public override updateTelemetryState(_didUserOptIn: boolean) {} + + public override isTelemetryEnabled(): boolean { + return true + } + + public override async shutdown() {} +} diff --git a/packages/cloud/src/__mocks__/vscode.ts b/packages/cloud/src/__mocks__/vscode.ts new file mode 100644 index 0000000000..df636967a1 --- /dev/null +++ b/packages/cloud/src/__mocks__/vscode.ts @@ -0,0 +1,50 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { vi } from "vitest" + +export const window = { + showInformationMessage: vi.fn(), + showErrorMessage: vi.fn(), +} + +export const env = { + openExternal: vi.fn(), +} + +export const Uri = { + parse: vi.fn((uri: string) => ({ toString: () => uri })), +} + +export interface ExtensionContext { + secrets: { + get: (key: string) => Promise + store: (key: string, value: string) => Promise + delete: (key: string) => Promise + } + globalState: { + get: (key: string) => T | undefined + update: (key: string, value: any) => Promise + } + extension?: { + packageJSON?: { + version?: string + } + } +} + +// Mock implementation for tests +export const mockExtensionContext: ExtensionContext = { + secrets: { + get: vi.fn().mockResolvedValue(undefined), + store: vi.fn().mockResolvedValue(undefined), + delete: vi.fn().mockResolvedValue(undefined), + }, + globalState: { + get: vi.fn().mockReturnValue(undefined), + update: vi.fn().mockResolvedValue(undefined), + }, + extension: { + packageJSON: { + version: "1.0.0", + }, + }, +} diff --git a/packages/cloud/src/__tests__/CloudService.test.ts b/packages/cloud/src/__tests__/CloudService.test.ts new file mode 100644 index 0000000000..8b34ee21c1 --- /dev/null +++ b/packages/cloud/src/__tests__/CloudService.test.ts @@ -0,0 +1,238 @@ +// npx vitest run src/__tests__/CloudService.test.ts + +import * as vscode from "vscode" + +import { CloudService } from "../CloudService" +import { AuthService } from "../AuthService" +import { SettingsService } from "../SettingsService" +import { TelemetryService } from "@roo-code/telemetry" +import { CloudServiceCallbacks } from "../types" + +vi.mock("vscode", () => ({ + ExtensionContext: vi.fn(), + window: { + showInformationMessage: vi.fn(), + showErrorMessage: vi.fn(), + }, + env: { + openExternal: vi.fn(), + }, + Uri: { + parse: vi.fn(), + }, +})) + +vi.mock("@roo-code/telemetry") + +vi.mock("../AuthService") + +vi.mock("../SettingsService") + +describe("CloudService", () => { + let mockContext: vscode.ExtensionContext + let mockAuthService: { + initialize: ReturnType + login: ReturnType + logout: ReturnType + isAuthenticated: ReturnType + hasActiveSession: ReturnType + getUserInfo: ReturnType + getState: ReturnType + getSessionToken: ReturnType + handleCallback: ReturnType + on: ReturnType + off: ReturnType + once: ReturnType + emit: ReturnType + } + let mockSettingsService: { + initialize: ReturnType + getSettings: ReturnType + getAllowList: ReturnType + dispose: ReturnType + } + let mockTelemetryService: { + hasInstance: ReturnType + instance: { + register: ReturnType + } + } + + beforeEach(() => { + CloudService.resetInstance() + + mockContext = { + secrets: { + get: vi.fn(), + store: vi.fn(), + delete: vi.fn(), + }, + globalState: { + get: vi.fn(), + update: vi.fn(), + }, + extension: { + packageJSON: { + version: "1.0.0", + }, + }, + } as unknown as vscode.ExtensionContext + + mockAuthService = { + initialize: vi.fn(), + login: vi.fn(), + logout: vi.fn(), + isAuthenticated: vi.fn().mockReturnValue(false), + hasActiveSession: vi.fn().mockReturnValue(false), + getUserInfo: vi.fn(), + getState: vi.fn().mockReturnValue("logged-out"), + getSessionToken: vi.fn(), + handleCallback: vi.fn(), + on: vi.fn(), + off: vi.fn(), + once: vi.fn(), + emit: vi.fn(), + } + + mockSettingsService = { + initialize: vi.fn(), + getSettings: vi.fn(), + getAllowList: vi.fn(), + dispose: vi.fn(), + } + + mockTelemetryService = { + hasInstance: vi.fn().mockReturnValue(true), + instance: { + register: vi.fn(), + }, + } + + vi.mocked(AuthService.createInstance).mockResolvedValue(mockAuthService as unknown as AuthService) + Object.defineProperty(AuthService, "instance", { get: () => mockAuthService, configurable: true }) + + vi.mocked(SettingsService.createInstance).mockResolvedValue(mockSettingsService as unknown as SettingsService) + Object.defineProperty(SettingsService, "instance", { get: () => mockSettingsService, configurable: true }) + + vi.mocked(TelemetryService.hasInstance).mockReturnValue(true) + Object.defineProperty(TelemetryService, "instance", { + get: () => mockTelemetryService.instance, + configurable: true, + }) + }) + + afterEach(() => { + vi.clearAllMocks() + CloudService.resetInstance() + }) + + describe("createInstance", () => { + it("should create and initialize CloudService instance", async () => { + const callbacks = { userChanged: vi.fn(), settingsChanged: vi.fn() } + const cloudService = await CloudService.createInstance(mockContext, callbacks) + + expect(cloudService).toBeInstanceOf(CloudService) + expect(AuthService.createInstance).toHaveBeenCalledWith(mockContext, expect.any(Function)) + expect(SettingsService.createInstance).toHaveBeenCalledWith(mockContext, expect.any(Function)) + }) + + it("should throw error if instance already exists", async () => { + await CloudService.createInstance(mockContext) + + await expect(CloudService.createInstance(mockContext)).rejects.toThrow( + "CloudService instance already created", + ) + }) + }) + + describe("authentication methods", () => { + let cloudService: CloudService + let callbacks: CloudServiceCallbacks + + beforeEach(async () => { + callbacks = { userChanged: vi.fn(), settingsChanged: vi.fn() } + cloudService = await CloudService.createInstance(mockContext, callbacks) + }) + + it("should delegate login to AuthService", async () => { + await cloudService.login() + expect(mockAuthService.login).toHaveBeenCalled() + }) + + it("should delegate logout to AuthService", async () => { + await cloudService.logout() + expect(mockAuthService.logout).toHaveBeenCalled() + }) + + it("should delegate isAuthenticated to AuthService", () => { + const result = cloudService.isAuthenticated() + expect(mockAuthService.isAuthenticated).toHaveBeenCalled() + expect(result).toBe(false) + }) + + it("should delegate hasActiveSession to AuthService", () => { + const result = cloudService.hasActiveSession() + expect(mockAuthService.hasActiveSession).toHaveBeenCalled() + expect(result).toBe(false) + }) + + it("should delegate getUserInfo to AuthService", async () => { + await cloudService.getUserInfo() + expect(mockAuthService.getUserInfo).toHaveBeenCalled() + }) + + it("should delegate getAuthState to AuthService", () => { + const result = cloudService.getAuthState() + expect(mockAuthService.getState).toHaveBeenCalled() + expect(result).toBe("logged-out") + }) + + it("should delegate handleAuthCallback to AuthService", async () => { + await cloudService.handleAuthCallback("code", "state") + expect(mockAuthService.handleCallback).toHaveBeenCalledWith("code", "state") + }) + }) + + describe("organization settings methods", () => { + let cloudService: CloudService + + beforeEach(async () => { + cloudService = await CloudService.createInstance(mockContext) + }) + + it("should delegate getAllowList to SettingsService", () => { + cloudService.getAllowList() + expect(mockSettingsService.getAllowList).toHaveBeenCalled() + }) + }) + + describe("error handling", () => { + it("should throw error when accessing methods before initialization", () => { + expect(() => CloudService.instance.login()).toThrow("CloudService not initialized") + }) + + it("should throw error when accessing instance before creation", () => { + expect(() => CloudService.instance).toThrow("CloudService not initialized") + }) + }) + + describe("hasInstance", () => { + it("should return false when no instance exists", () => { + expect(CloudService.hasInstance()).toBe(false) + }) + + it("should return true when instance exists and is initialized", async () => { + await CloudService.createInstance(mockContext) + expect(CloudService.hasInstance()).toBe(true) + }) + }) + + describe("dispose", () => { + it("should dispose of all services and clean up", async () => { + const cloudService = await CloudService.createInstance(mockContext) + cloudService.dispose() + + expect(mockSettingsService.dispose).toHaveBeenCalled() + }) + }) +}) diff --git a/src/utils/__tests__/refresh-timer.test.ts b/packages/cloud/src/__tests__/RefreshTimer.test.ts similarity index 86% rename from src/utils/__tests__/refresh-timer.test.ts rename to packages/cloud/src/__tests__/RefreshTimer.test.ts index 11911494f6..f8b306b716 100644 --- a/src/utils/__tests__/refresh-timer.test.ts +++ b/packages/cloud/src/__tests__/RefreshTimer.test.ts @@ -1,27 +1,27 @@ -import { RefreshTimer } from "../refresh-timer" +// npx vitest run --globals src/__tests__/RefreshTimer.test.ts -// Mock timers -jest.useFakeTimers() +import { Mock } from "vitest" + +import { RefreshTimer } from "../RefreshTimer" + +vi.useFakeTimers() describe("RefreshTimer", () => { - let mockCallback: jest.Mock + let mockCallback: Mock let refreshTimer: RefreshTimer beforeEach(() => { - // Reset mocks before each test - mockCallback = jest.fn() - - // Default mock implementation returns success + mockCallback = vi.fn() mockCallback.mockResolvedValue(true) }) afterEach(() => { - // Clean up after each test if (refreshTimer) { refreshTimer.stop() } - jest.clearAllTimers() - jest.clearAllMocks() + + vi.clearAllTimers() + vi.clearAllMocks() }) it("should execute callback immediately when started", () => { @@ -50,7 +50,7 @@ describe("RefreshTimer", () => { expect(mockCallback).toHaveBeenCalledTimes(1) // Fast-forward 50 seconds - jest.advanceTimersByTime(50000) + vi.advanceTimersByTime(50000) // Callback should be called again expect(mockCallback).toHaveBeenCalledTimes(2) @@ -72,7 +72,7 @@ describe("RefreshTimer", () => { expect(mockCallback).toHaveBeenCalledTimes(1) // Fast-forward 1 second - jest.advanceTimersByTime(1000) + vi.advanceTimersByTime(1000) // Callback should be called again expect(mockCallback).toHaveBeenCalledTimes(2) @@ -81,7 +81,7 @@ describe("RefreshTimer", () => { await Promise.resolve() // Fast-forward 2 seconds - jest.advanceTimersByTime(2000) + vi.advanceTimersByTime(2000) // Callback should be called again expect(mockCallback).toHaveBeenCalledTimes(3) @@ -103,13 +103,13 @@ describe("RefreshTimer", () => { // Fast-forward through multiple failures to reach max backoff await Promise.resolve() // First attempt - jest.advanceTimersByTime(1000) + vi.advanceTimersByTime(1000) await Promise.resolve() // Second attempt (backoff = 2000ms) - jest.advanceTimersByTime(2000) + vi.advanceTimersByTime(2000) await Promise.resolve() // Third attempt (backoff = 4000ms) - jest.advanceTimersByTime(4000) + vi.advanceTimersByTime(4000) await Promise.resolve() // Fourth attempt (backoff would be 8000ms but max is 5000ms) @@ -132,13 +132,13 @@ describe("RefreshTimer", () => { await Promise.resolve() // Fast-forward 1 second - jest.advanceTimersByTime(1000) + vi.advanceTimersByTime(1000) // Second attempt (succeeds) await Promise.resolve() // Fast-forward 5 seconds - jest.advanceTimersByTime(5000) + vi.advanceTimersByTime(5000) // Third attempt (fails) await Promise.resolve() @@ -173,7 +173,7 @@ describe("RefreshTimer", () => { refreshTimer.stop() // Fast-forward a long time - jest.advanceTimersByTime(1000000) + vi.advanceTimersByTime(1000000) // Callback should only have been called once (the initial call) expect(mockCallback).toHaveBeenCalledTimes(1) @@ -191,10 +191,10 @@ describe("RefreshTimer", () => { // Fast-forward through a few failures await Promise.resolve() - jest.advanceTimersByTime(1000) + vi.advanceTimersByTime(1000) await Promise.resolve() - jest.advanceTimersByTime(2000) + vi.advanceTimersByTime(2000) // Reset the timer refreshTimer.reset() diff --git a/packages/cloud/src/__tests__/RooCodeTelemetryClient.test.ts b/packages/cloud/src/__tests__/RooCodeTelemetryClient.test.ts new file mode 100644 index 0000000000..da8915af9f --- /dev/null +++ b/packages/cloud/src/__tests__/RooCodeTelemetryClient.test.ts @@ -0,0 +1,250 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +// npx vitest run src/__tests__/RooCodeTelemetryClient.test.ts + +import { describe, it, expect, beforeEach, afterEach, vi } from "vitest" + +import { type TelemetryPropertiesProvider, TelemetryEventName } from "@roo-code/types" + +import { RooCodeTelemetryClient } from "../RooCodeTelemetryClient" + +const mockFetch = vi.fn() +global.fetch = mockFetch as any + +describe("RooCodeTelemetryClient", () => { + const getPrivateProperty = (instance: any, propertyName: string): T => { + return instance[propertyName] + } + + let mockAuthService: any + + beforeEach(() => { + vi.clearAllMocks() + + // Create a mock AuthService instead of using the singleton + mockAuthService = { + getSessionToken: vi.fn().mockReturnValue("mock-token"), + getState: vi.fn().mockReturnValue("active-session"), + isAuthenticated: vi.fn().mockReturnValue(true), + hasActiveSession: vi.fn().mockReturnValue(true), + } + + mockFetch.mockResolvedValue({ + ok: true, + json: vi.fn().mockResolvedValue({}), + }) + + vi.spyOn(console, "info").mockImplementation(() => {}) + vi.spyOn(console, "error").mockImplementation(() => {}) + }) + + afterEach(() => { + vi.restoreAllMocks() + }) + + describe("isEventCapturable", () => { + it("should return true for events not in exclude list", () => { + const client = new RooCodeTelemetryClient(mockAuthService) + + const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>( + client, + "isEventCapturable", + ).bind(client) + + expect(isEventCapturable(TelemetryEventName.TASK_CREATED)).toBe(true) + expect(isEventCapturable(TelemetryEventName.LLM_COMPLETION)).toBe(true) + expect(isEventCapturable(TelemetryEventName.MODE_SWITCH)).toBe(true) + expect(isEventCapturable(TelemetryEventName.TOOL_USED)).toBe(true) + }) + + it("should return false for events in exclude list", () => { + const client = new RooCodeTelemetryClient(mockAuthService) + + const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>( + client, + "isEventCapturable", + ).bind(client) + + expect(isEventCapturable(TelemetryEventName.TASK_CONVERSATION_MESSAGE)).toBe(false) + }) + }) + + describe("getEventProperties", () => { + it("should merge provider properties with event properties", async () => { + const client = new RooCodeTelemetryClient(mockAuthService) + + const mockProvider: TelemetryPropertiesProvider = { + getTelemetryProperties: vi.fn().mockResolvedValue({ + appVersion: "1.0.0", + vscodeVersion: "1.60.0", + platform: "darwin", + editorName: "vscode", + language: "en", + mode: "code", + }), + } + + client.setProvider(mockProvider) + + const getEventProperties = getPrivateProperty< + (event: { event: TelemetryEventName; properties?: Record }) => Promise> + >(client, "getEventProperties").bind(client) + + const result = await getEventProperties({ + event: TelemetryEventName.TASK_CREATED, + properties: { + customProp: "value", + mode: "override", // This should override the provider's mode. + }, + }) + + expect(result).toEqual({ + appVersion: "1.0.0", + vscodeVersion: "1.60.0", + platform: "darwin", + editorName: "vscode", + language: "en", + mode: "override", // Event property takes precedence. + customProp: "value", + }) + + expect(mockProvider.getTelemetryProperties).toHaveBeenCalledTimes(1) + }) + + it("should handle errors from provider gracefully", async () => { + const client = new RooCodeTelemetryClient(mockAuthService) + + const mockProvider: TelemetryPropertiesProvider = { + getTelemetryProperties: vi.fn().mockRejectedValue(new Error("Provider error")), + } + + const consoleErrorSpy = vi.spyOn(console, "error") + + client.setProvider(mockProvider) + + const getEventProperties = getPrivateProperty< + (event: { event: TelemetryEventName; properties?: Record }) => Promise> + >(client, "getEventProperties").bind(client) + + const result = await getEventProperties({ + event: TelemetryEventName.TASK_CREATED, + properties: { customProp: "value" }, + }) + + expect(result).toEqual({ customProp: "value" }) + expect(consoleErrorSpy).toHaveBeenCalledWith( + expect.stringContaining("Error getting telemetry properties: Provider error"), + ) + }) + + it("should return event properties when no provider is set", async () => { + const client = new RooCodeTelemetryClient(mockAuthService) + + const getEventProperties = getPrivateProperty< + (event: { event: TelemetryEventName; properties?: Record }) => Promise> + >(client, "getEventProperties").bind(client) + + const result = await getEventProperties({ + event: TelemetryEventName.TASK_CREATED, + properties: { customProp: "value" }, + }) + + expect(result).toEqual({ customProp: "value" }) + }) + }) + + describe("capture", () => { + it("should not capture events that are not capturable", async () => { + const client = new RooCodeTelemetryClient(mockAuthService) + + await client.capture({ + event: TelemetryEventName.TASK_CONVERSATION_MESSAGE, // In exclude list. + properties: { test: "value" }, + }) + + expect(mockFetch).not.toHaveBeenCalled() + }) + + it("should not send request when schema validation fails", async () => { + const client = new RooCodeTelemetryClient(mockAuthService) + + await client.capture({ + event: TelemetryEventName.TASK_CREATED, + properties: { test: "value" }, + }) + + expect(mockFetch).not.toHaveBeenCalled() + expect(console.error).toHaveBeenCalledWith(expect.stringContaining("Invalid telemetry event")) + }) + + it("should send request when event is capturable and validation passes", async () => { + const client = new RooCodeTelemetryClient(mockAuthService) + + const providerProperties = { + appVersion: "1.0.0", + vscodeVersion: "1.60.0", + platform: "darwin", + editorName: "vscode", + language: "en", + mode: "code", + } + + const eventProperties = { + taskId: "test-task-id", + } + + const mockValidatedData = { + type: TelemetryEventName.TASK_CREATED, + properties: { + ...providerProperties, + taskId: "test-task-id", + }, + } + + const mockProvider: TelemetryPropertiesProvider = { + getTelemetryProperties: vi.fn().mockResolvedValue(providerProperties), + } + + client.setProvider(mockProvider) + + await client.capture({ + event: TelemetryEventName.TASK_CREATED, + properties: eventProperties, + }) + + expect(mockFetch).toHaveBeenCalledWith( + "https://app.roocode.com/api/events", + expect.objectContaining({ + method: "POST", + body: JSON.stringify(mockValidatedData), + }), + ) + }) + + it("should handle fetch errors gracefully", async () => { + const client = new RooCodeTelemetryClient(mockAuthService) + + mockFetch.mockRejectedValue(new Error("Network error")) + + await expect( + client.capture({ + event: TelemetryEventName.TASK_CREATED, + properties: { test: "value" }, + }), + ).resolves.not.toThrow() + }) + }) + + describe("telemetry state methods", () => { + it("should always return true for isTelemetryEnabled", () => { + const client = new RooCodeTelemetryClient(mockAuthService) + expect(client.isTelemetryEnabled()).toBe(true) + }) + + it("should have empty implementations for updateTelemetryState and shutdown", async () => { + const client = new RooCodeTelemetryClient(mockAuthService) + client.updateTelemetryState(true) + await client.shutdown() + }) + }) +}) diff --git a/packages/cloud/src/__tests__/TelemetryClient.test.ts b/packages/cloud/src/__tests__/TelemetryClient.test.ts new file mode 100644 index 0000000000..fa008dbb34 --- /dev/null +++ b/packages/cloud/src/__tests__/TelemetryClient.test.ts @@ -0,0 +1,250 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +// npx vitest run src/__tests__/TelemetryClient.test.ts + +import { describe, it, expect, beforeEach, afterEach, vi } from "vitest" + +import { type TelemetryPropertiesProvider, TelemetryEventName } from "@roo-code/types" + +import { TelemetryClient } from "../TelemetryClient" + +const mockFetch = vi.fn() +global.fetch = mockFetch as any + +describe("TelemetryClient", () => { + const getPrivateProperty = (instance: any, propertyName: string): T => { + return instance[propertyName] + } + + let mockAuthService: any + + beforeEach(() => { + vi.clearAllMocks() + + // Create a mock AuthService instead of using the singleton + mockAuthService = { + getSessionToken: vi.fn().mockReturnValue("mock-token"), + getState: vi.fn().mockReturnValue("active-session"), + isAuthenticated: vi.fn().mockReturnValue(true), + hasActiveSession: vi.fn().mockReturnValue(true), + } + + mockFetch.mockResolvedValue({ + ok: true, + json: vi.fn().mockResolvedValue({}), + }) + + vi.spyOn(console, "info").mockImplementation(() => {}) + vi.spyOn(console, "error").mockImplementation(() => {}) + }) + + afterEach(() => { + vi.restoreAllMocks() + }) + + describe("isEventCapturable", () => { + it("should return true for events not in exclude list", () => { + const client = new TelemetryClient(mockAuthService) + + const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>( + client, + "isEventCapturable", + ).bind(client) + + expect(isEventCapturable(TelemetryEventName.TASK_CREATED)).toBe(true) + expect(isEventCapturable(TelemetryEventName.LLM_COMPLETION)).toBe(true) + expect(isEventCapturable(TelemetryEventName.MODE_SWITCH)).toBe(true) + expect(isEventCapturable(TelemetryEventName.TOOL_USED)).toBe(true) + }) + + it("should return false for events in exclude list", () => { + const client = new TelemetryClient(mockAuthService) + + const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>( + client, + "isEventCapturable", + ).bind(client) + + expect(isEventCapturable(TelemetryEventName.TASK_CONVERSATION_MESSAGE)).toBe(false) + }) + }) + + describe("getEventProperties", () => { + it("should merge provider properties with event properties", async () => { + const client = new TelemetryClient(mockAuthService) + + const mockProvider: TelemetryPropertiesProvider = { + getTelemetryProperties: vi.fn().mockResolvedValue({ + appVersion: "1.0.0", + vscodeVersion: "1.60.0", + platform: "darwin", + editorName: "vscode", + language: "en", + mode: "code", + }), + } + + client.setProvider(mockProvider) + + const getEventProperties = getPrivateProperty< + (event: { event: TelemetryEventName; properties?: Record }) => Promise> + >(client, "getEventProperties").bind(client) + + const result = await getEventProperties({ + event: TelemetryEventName.TASK_CREATED, + properties: { + customProp: "value", + mode: "override", // This should override the provider's mode. + }, + }) + + expect(result).toEqual({ + appVersion: "1.0.0", + vscodeVersion: "1.60.0", + platform: "darwin", + editorName: "vscode", + language: "en", + mode: "override", // Event property takes precedence. + customProp: "value", + }) + + expect(mockProvider.getTelemetryProperties).toHaveBeenCalledTimes(1) + }) + + it("should handle errors from provider gracefully", async () => { + const client = new TelemetryClient(mockAuthService) + + const mockProvider: TelemetryPropertiesProvider = { + getTelemetryProperties: vi.fn().mockRejectedValue(new Error("Provider error")), + } + + const consoleErrorSpy = vi.spyOn(console, "error") + + client.setProvider(mockProvider) + + const getEventProperties = getPrivateProperty< + (event: { event: TelemetryEventName; properties?: Record }) => Promise> + >(client, "getEventProperties").bind(client) + + const result = await getEventProperties({ + event: TelemetryEventName.TASK_CREATED, + properties: { customProp: "value" }, + }) + + expect(result).toEqual({ customProp: "value" }) + expect(consoleErrorSpy).toHaveBeenCalledWith( + expect.stringContaining("Error getting telemetry properties: Provider error"), + ) + }) + + it("should return event properties when no provider is set", async () => { + const client = new TelemetryClient(mockAuthService) + + const getEventProperties = getPrivateProperty< + (event: { event: TelemetryEventName; properties?: Record }) => Promise> + >(client, "getEventProperties").bind(client) + + const result = await getEventProperties({ + event: TelemetryEventName.TASK_CREATED, + properties: { customProp: "value" }, + }) + + expect(result).toEqual({ customProp: "value" }) + }) + }) + + describe("capture", () => { + it("should not capture events that are not capturable", async () => { + const client = new TelemetryClient(mockAuthService) + + await client.capture({ + event: TelemetryEventName.TASK_CONVERSATION_MESSAGE, // In exclude list. + properties: { test: "value" }, + }) + + expect(mockFetch).not.toHaveBeenCalled() + }) + + it("should not send request when schema validation fails", async () => { + const client = new TelemetryClient(mockAuthService) + + await client.capture({ + event: TelemetryEventName.TASK_CREATED, + properties: { test: "value" }, + }) + + expect(mockFetch).not.toHaveBeenCalled() + expect(console.error).toHaveBeenCalledWith(expect.stringContaining("Invalid telemetry event")) + }) + + it("should send request when event is capturable and validation passes", async () => { + const client = new TelemetryClient(mockAuthService) + + const providerProperties = { + appVersion: "1.0.0", + vscodeVersion: "1.60.0", + platform: "darwin", + editorName: "vscode", + language: "en", + mode: "code", + } + + const eventProperties = { + taskId: "test-task-id", + } + + const mockValidatedData = { + type: TelemetryEventName.TASK_CREATED, + properties: { + ...providerProperties, + taskId: "test-task-id", + }, + } + + const mockProvider: TelemetryPropertiesProvider = { + getTelemetryProperties: vi.fn().mockResolvedValue(providerProperties), + } + + client.setProvider(mockProvider) + + await client.capture({ + event: TelemetryEventName.TASK_CREATED, + properties: eventProperties, + }) + + expect(mockFetch).toHaveBeenCalledWith( + "https://app.roocode.com/api/events", + expect.objectContaining({ + method: "POST", + body: JSON.stringify(mockValidatedData), + }), + ) + }) + + it("should handle fetch errors gracefully", async () => { + const client = new TelemetryClient(mockAuthService) + + mockFetch.mockRejectedValue(new Error("Network error")) + + await expect( + client.capture({ + event: TelemetryEventName.TASK_CREATED, + properties: { test: "value" }, + }), + ).resolves.not.toThrow() + }) + }) + + describe("telemetry state methods", () => { + it("should always return true for isTelemetryEnabled", () => { + const client = new TelemetryClient(mockAuthService) + expect(client.isTelemetryEnabled()).toBe(true) + }) + + it("should have empty implementations for updateTelemetryState and shutdown", async () => { + const client = new TelemetryClient(mockAuthService) + client.updateTelemetryState(true) + await client.shutdown() + }) + }) +}) diff --git a/packages/cloud/src/index.ts b/packages/cloud/src/index.ts new file mode 100644 index 0000000000..07ea14c784 --- /dev/null +++ b/packages/cloud/src/index.ts @@ -0,0 +1 @@ +export * from "./CloudService" diff --git a/packages/cloud/src/types.ts b/packages/cloud/src/types.ts new file mode 100644 index 0000000000..9c467d9e31 --- /dev/null +++ b/packages/cloud/src/types.ts @@ -0,0 +1,6 @@ +import { CloudUserInfo } from "@roo-code/types" + +export interface CloudServiceCallbacks { + userChanged?: (userInfo: CloudUserInfo | undefined) => void + settingsChanged?: () => void +} diff --git a/packages/cloud/tsconfig.json b/packages/cloud/tsconfig.json new file mode 100644 index 0000000000..f599e2220d --- /dev/null +++ b/packages/cloud/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@roo-code/config-typescript/vscode-library.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/cloud/vitest.config.ts b/packages/cloud/vitest.config.ts new file mode 100644 index 0000000000..ff37ed3110 --- /dev/null +++ b/packages/cloud/vitest.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from "vitest/config" + +export default defineConfig({ + test: { + globals: true, + environment: "node", + }, + resolve: { + alias: { + vscode: new URL("./src/__mocks__/vscode.ts", import.meta.url).pathname, + }, + }, +}) diff --git a/packages/config-typescript/vscode-library.json b/packages/config-typescript/vscode-library.json new file mode 100644 index 0000000000..bc09b3db6d --- /dev/null +++ b/packages/config-typescript/vscode-library.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "./base.json", + "compilerOptions": { + "types": ["vitest/globals"], + "outDir": "dist", + "module": "esnext", + "moduleResolution": "Bundler", + "noUncheckedIndexedAccess": false, + "useUnknownInCatchVariables": false + } +} diff --git a/packages/telemetry/eslint.config.mjs b/packages/telemetry/eslint.config.mjs new file mode 100644 index 0000000000..694bf73664 --- /dev/null +++ b/packages/telemetry/eslint.config.mjs @@ -0,0 +1,4 @@ +import { config } from "@roo-code/config-eslint/base" + +/** @type {import("eslint").Linter.Config} */ +export default [...config] diff --git a/packages/telemetry/package.json b/packages/telemetry/package.json new file mode 100644 index 0000000000..229e676415 --- /dev/null +++ b/packages/telemetry/package.json @@ -0,0 +1,25 @@ +{ + "name": "@roo-code/telemetry", + "description": "Roo Code telemetry service and clients.", + "private": true, + "type": "module", + "exports": "./src/index.ts", + "scripts": { + "lint": "eslint src --ext=ts --max-warnings=0", + "check-types": "tsc --noEmit", + "test": "vitest --globals --run", + "clean": "rimraf dist .turbo" + }, + "dependencies": { + "@roo-code/types": "workspace:^", + "posthog-node": "^4.7.0", + "zod": "^3.24.2" + }, + "devDependencies": { + "@roo-code/config-eslint": "workspace:^", + "@roo-code/config-typescript": "workspace:^", + "@types/node": "^22.15.20", + "@types/vscode": "^1.84.0", + "vitest": "^3.1.3" + } +} diff --git a/src/services/telemetry/clients/BaseTelemetryClient.ts b/packages/telemetry/src/BaseTelemetryClient.ts similarity index 90% rename from src/services/telemetry/clients/BaseTelemetryClient.ts rename to packages/telemetry/src/BaseTelemetryClient.ts index 24a486a2ea..ab8ab56f59 100644 --- a/src/services/telemetry/clients/BaseTelemetryClient.ts +++ b/packages/telemetry/src/BaseTelemetryClient.ts @@ -1,6 +1,10 @@ -import { TelemetryEvent, TelemetryEventName } from "@roo-code/types" - -import { TelemetryClient, TelemetryPropertiesProvider, TelemetryEventSubscription } from "../types" +import { + TelemetryEvent, + TelemetryEventName, + TelemetryClient, + TelemetryPropertiesProvider, + TelemetryEventSubscription, +} from "@roo-code/types" export abstract class BaseTelemetryClient implements TelemetryClient { protected providerRef: WeakRef | null = null diff --git a/src/services/telemetry/clients/PostHogTelemetryClient.ts b/packages/telemetry/src/PostHogTelemetryClient.ts similarity index 86% rename from src/services/telemetry/clients/PostHogTelemetryClient.ts rename to packages/telemetry/src/PostHogTelemetryClient.ts index b554d962e3..243176ed45 100644 --- a/src/services/telemetry/clients/PostHogTelemetryClient.ts +++ b/packages/telemetry/src/PostHogTelemetryClient.ts @@ -14,11 +14,11 @@ export class PostHogTelemetryClient extends BaseTelemetryClient { private client: PostHog private distinctId: string = vscode.env.machineId - private constructor(debug = false) { + constructor(debug = false) { super( { type: "exclude", - events: [TelemetryEventName.LLM_COMPLETION], + events: [TelemetryEventName.TASK_MESSAGE, TelemetryEventName.LLM_COMPLETION], }, debug, ) @@ -75,14 +75,4 @@ export class PostHogTelemetryClient extends BaseTelemetryClient { public override async shutdown(): Promise { await this.client.shutdown() } - - private static _instance: PostHogTelemetryClient | null = null - - public static getInstance(): PostHogTelemetryClient { - if (!PostHogTelemetryClient._instance) { - PostHogTelemetryClient._instance = new PostHogTelemetryClient() - } - - return PostHogTelemetryClient._instance - } } diff --git a/src/services/telemetry/TelemetryService.ts b/packages/telemetry/src/TelemetryService.ts similarity index 84% rename from src/services/telemetry/TelemetryService.ts rename to packages/telemetry/src/TelemetryService.ts index cc1248f1b7..4f2427d998 100644 --- a/src/services/telemetry/TelemetryService.ts +++ b/packages/telemetry/src/TelemetryService.ts @@ -1,38 +1,17 @@ -import * as vscode from "vscode" import { ZodError } from "zod" -import { TelemetryEventName } from "@roo-code/types" - -import { logger } from "../../utils/logging" - -import { PostHogTelemetryClient } from "./clients/PostHogTelemetryClient" -import { type TelemetryClient, type TelemetryPropertiesProvider } from "./types" +import { type TelemetryClient, type TelemetryPropertiesProvider, TelemetryEventName } from "@roo-code/types" /** * TelemetryService wrapper class that defers initialization. * This ensures that we only create the various clients after environment * variables are loaded. */ -class TelemetryService { - private clients: TelemetryClient[] = [] - private initialized = false +export class TelemetryService { + constructor(private clients: TelemetryClient[]) {} - /** - * Initialize the telemetry client. This should be called after environment - * variables are loaded. - */ - public async initialize(context: vscode.ExtensionContext): Promise { - if (this.initialized) { - return - } - - this.initialized = true - - try { - this.clients.push(PostHogTelemetryClient.getInstance()) - } catch (error) { - console.warn("Failed to initialize telemetry service:", error) - } + public register(client: TelemetryClient): void { + this.clients.push(client) } /** @@ -44,8 +23,6 @@ class TelemetryService { if (this.isReady) { this.clients.forEach((client) => client.setProvider(provider)) } - - logger.debug("TelemetryService: ClineProvider reference set") } /** @@ -54,7 +31,7 @@ class TelemetryService { * @returns Whether the service is ready to use */ private get isReady(): boolean { - return this.initialized && this.clients.length > 0 + return this.clients.length > 0 } /** @@ -74,7 +51,8 @@ class TelemetryService { * @param eventName The event name to capture * @param properties The event properties */ - public captureEvent(eventName: TelemetryEventName, properties?: any): void { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public captureEvent(eventName: TelemetryEventName, properties?: Record): void { if (!this.isReady) { return } @@ -197,6 +175,27 @@ class TelemetryService { this.clients.forEach((client) => client.shutdown()) } -} -export const telemetryService = new TelemetryService() + private static _instance: TelemetryService | null = null + + static createInstance(clients: TelemetryClient[] = []) { + if (this._instance) { + throw new Error("TelemetryService instance already created") + } + + this._instance = new TelemetryService(clients) + return this._instance + } + + static get instance() { + if (!this._instance) { + throw new Error("TelemetryService not initialized") + } + + return this._instance + } + + static hasInstance(): boolean { + return this._instance !== null + } +} diff --git a/src/services/telemetry/clients/__tests__/PostHogTelemetryClient.test.ts b/packages/telemetry/src/__tests__/PostHogTelemetryClient.test.ts similarity index 71% rename from src/services/telemetry/clients/__tests__/PostHogTelemetryClient.test.ts rename to packages/telemetry/src/__tests__/PostHogTelemetryClient.test.ts index 89bb16e81a..50d7f5be88 100644 --- a/src/services/telemetry/clients/__tests__/PostHogTelemetryClient.test.ts +++ b/packages/telemetry/src/__tests__/PostHogTelemetryClient.test.ts @@ -1,21 +1,23 @@ -// npx jest src/services/telemetry/clients/__tests__/PostHogTelemetryClient.test.ts +/* eslint-disable @typescript-eslint/no-explicit-any */ +// npx vitest run src/__tests__/PostHogTelemetryClient.test.ts + +import { describe, it, expect, beforeEach, vi } from "vitest" import * as vscode from "vscode" import { PostHog } from "posthog-node" -import { TelemetryEventName } from "@roo-code/types" +import { type TelemetryPropertiesProvider, TelemetryEventName } from "@roo-code/types" -import { TelemetryPropertiesProvider } from "../../types" import { PostHogTelemetryClient } from "../PostHogTelemetryClient" -jest.mock("posthog-node") +vi.mock("posthog-node") -jest.mock("vscode", () => ({ +vi.mock("vscode", () => ({ env: { machineId: "test-machine-id", }, workspace: { - getConfiguration: jest.fn(), + getConfiguration: vi.fn(), }, })) @@ -24,37 +26,29 @@ describe("PostHogTelemetryClient", () => { return instance[propertyName] } - let mockPostHogClient: jest.Mocked + let mockPostHogClient: any beforeEach(() => { - jest.clearAllMocks() + vi.clearAllMocks() mockPostHogClient = { - capture: jest.fn(), - optIn: jest.fn(), - optOut: jest.fn(), - shutdown: jest.fn().mockResolvedValue(undefined), - } as unknown as jest.Mocked - ;(PostHog as unknown as jest.Mock).mockImplementation(() => mockPostHogClient) + capture: vi.fn(), + optIn: vi.fn(), + optOut: vi.fn(), + shutdown: vi.fn().mockResolvedValue(undefined), + } + ;(PostHog as any).mockImplementation(() => mockPostHogClient) - // @ts-ignore - Accessing private static property for testing + // @ts-expect-error - Accessing private static property for testing PostHogTelemetryClient._instance = undefined - ;(vscode.workspace.getConfiguration as jest.Mock).mockReturnValue({ - get: jest.fn().mockReturnValue("all"), - }) - }) - - describe("getInstance", () => { - it("should return the same instance when called multiple times", () => { - const instance1 = PostHogTelemetryClient.getInstance() - const instance2 = PostHogTelemetryClient.getInstance() - expect(instance1).toBe(instance2) + ;(vscode.workspace.getConfiguration as any).mockReturnValue({ + get: vi.fn().mockReturnValue("all"), }) }) describe("isEventCapturable", () => { it("should return true for events not in exclude list", () => { - const client = PostHogTelemetryClient.getInstance() + const client = new PostHogTelemetryClient() const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>( client, @@ -66,7 +60,7 @@ describe("PostHogTelemetryClient", () => { }) it("should return false for events in exclude list", () => { - const client = PostHogTelemetryClient.getInstance() + const client = new PostHogTelemetryClient() const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>( client, @@ -79,10 +73,10 @@ describe("PostHogTelemetryClient", () => { describe("getEventProperties", () => { it("should merge provider properties with event properties", async () => { - const client = PostHogTelemetryClient.getInstance() + const client = new PostHogTelemetryClient() const mockProvider: TelemetryPropertiesProvider = { - getTelemetryProperties: jest.fn().mockResolvedValue({ + getTelemetryProperties: vi.fn().mockResolvedValue({ appVersion: "1.0.0", vscodeVersion: "1.60.0", platform: "darwin", @@ -120,13 +114,13 @@ describe("PostHogTelemetryClient", () => { }) it("should handle errors from provider gracefully", async () => { - const client = PostHogTelemetryClient.getInstance() + const client = new PostHogTelemetryClient() const mockProvider: TelemetryPropertiesProvider = { - getTelemetryProperties: jest.fn().mockRejectedValue(new Error("Provider error")), + getTelemetryProperties: vi.fn().mockRejectedValue(new Error("Provider error")), } - const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation() + const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {}) client.setProvider(mockProvider) const getEventProperties = getPrivateProperty< @@ -147,7 +141,7 @@ describe("PostHogTelemetryClient", () => { }) it("should return event properties when no provider is set", async () => { - const client = PostHogTelemetryClient.getInstance() + const client = new PostHogTelemetryClient() const getEventProperties = getPrivateProperty< (event: { event: TelemetryEventName; properties?: Record }) => Promise> @@ -164,7 +158,7 @@ describe("PostHogTelemetryClient", () => { describe("capture", () => { it("should not capture events when telemetry is disabled", async () => { - const client = PostHogTelemetryClient.getInstance() + const client = new PostHogTelemetryClient() client.updateTelemetryState(false) await client.capture({ @@ -176,7 +170,7 @@ describe("PostHogTelemetryClient", () => { }) it("should not capture events that are not capturable", async () => { - const client = PostHogTelemetryClient.getInstance() + const client = new PostHogTelemetryClient() client.updateTelemetryState(true) await client.capture({ @@ -188,11 +182,11 @@ describe("PostHogTelemetryClient", () => { }) it("should capture events when telemetry is enabled and event is capturable", async () => { - const client = PostHogTelemetryClient.getInstance() + const client = new PostHogTelemetryClient() client.updateTelemetryState(true) const mockProvider: TelemetryPropertiesProvider = { - getTelemetryProperties: jest.fn().mockResolvedValue({ + getTelemetryProperties: vi.fn().mockResolvedValue({ appVersion: "1.0.0", vscodeVersion: "1.60.0", platform: "darwin", @@ -222,10 +216,10 @@ describe("PostHogTelemetryClient", () => { describe("updateTelemetryState", () => { it("should enable telemetry when user opts in and global telemetry is enabled", () => { - const client = PostHogTelemetryClient.getInstance() + const client = new PostHogTelemetryClient() - ;(vscode.workspace.getConfiguration as jest.Mock).mockReturnValue({ - get: jest.fn().mockReturnValue("all"), + ;(vscode.workspace.getConfiguration as any).mockReturnValue({ + get: vi.fn().mockReturnValue("all"), }) client.updateTelemetryState(true) @@ -235,10 +229,10 @@ describe("PostHogTelemetryClient", () => { }) it("should disable telemetry when user opts out", () => { - const client = PostHogTelemetryClient.getInstance() + const client = new PostHogTelemetryClient() - ;(vscode.workspace.getConfiguration as jest.Mock).mockReturnValue({ - get: jest.fn().mockReturnValue("all"), + ;(vscode.workspace.getConfiguration as any).mockReturnValue({ + get: vi.fn().mockReturnValue("all"), }) client.updateTelemetryState(false) @@ -248,10 +242,10 @@ describe("PostHogTelemetryClient", () => { }) it("should disable telemetry when global telemetry is disabled, regardless of user opt-in", () => { - const client = PostHogTelemetryClient.getInstance() + const client = new PostHogTelemetryClient() - ;(vscode.workspace.getConfiguration as jest.Mock).mockReturnValue({ - get: jest.fn().mockReturnValue("off"), + ;(vscode.workspace.getConfiguration as any).mockReturnValue({ + get: vi.fn().mockReturnValue("off"), }) client.updateTelemetryState(true) @@ -262,7 +256,7 @@ describe("PostHogTelemetryClient", () => { describe("shutdown", () => { it("should call shutdown on the PostHog client", async () => { - const client = PostHogTelemetryClient.getInstance() + const client = new PostHogTelemetryClient() await client.shutdown() expect(mockPostHogClient.shutdown).toHaveBeenCalled() }) diff --git a/packages/telemetry/src/index.ts b/packages/telemetry/src/index.ts new file mode 100644 index 0000000000..8795ad46a2 --- /dev/null +++ b/packages/telemetry/src/index.ts @@ -0,0 +1,3 @@ +export * from "./BaseTelemetryClient" +export * from "./PostHogTelemetryClient" +export * from "./TelemetryService" diff --git a/packages/telemetry/tsconfig.json b/packages/telemetry/tsconfig.json new file mode 100644 index 0000000000..f599e2220d --- /dev/null +++ b/packages/telemetry/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@roo-code/config-typescript/vscode-library.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/telemetry/vitest.config.ts b/packages/telemetry/vitest.config.ts new file mode 100644 index 0000000000..f749203bfc --- /dev/null +++ b/packages/telemetry/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vitest/config" + +export default defineConfig({ + test: { + globals: true, + environment: "node", + }, +}) diff --git a/packages/types/npm/package.json b/packages/types/npm/package.json index a1e46de317..013b894afb 100644 --- a/packages/types/npm/package.json +++ b/packages/types/npm/package.json @@ -1,6 +1,6 @@ { "name": "@roo-code/types", - "version": "1.19.0", + "version": "1.22.0", "description": "TypeScript type definitions for Roo Code.", "publishConfig": { "access": "public", diff --git a/packages/types/src/cloud.ts b/packages/types/src/cloud.ts new file mode 100644 index 0000000000..207b510116 --- /dev/null +++ b/packages/types/src/cloud.ts @@ -0,0 +1,49 @@ +import { z } from "zod" + +export interface CloudUserInfo { + name?: string + email?: string + picture?: string +} + +/** + * Organization Allow List + */ + +export const organizationAllowListSchema = z.object({ + allowAll: z.boolean(), + providers: z.record( + z.object({ + allowAll: z.boolean(), + models: z.array(z.string()).optional(), + }), + ), +}) + +export type OrganizationAllowList = z.infer + +export const ORGANIZATION_ALLOW_ALL: OrganizationAllowList = { + allowAll: true, + providers: {}, +} as const + +/** + * Organization Settings + */ + +export const organizationSettingsSchema = z.object({ + version: z.number(), + defaultSettings: z + .object({ + enableCheckpoints: z.boolean().optional(), + maxOpenTabsContext: z.number().optional(), + maxWorkspaceFiles: z.number().optional(), + showRooIgnoredFiles: z.boolean().optional(), + maxReadFileLine: z.number().optional(), + fuzzyMatchThreshold: z.number().optional(), + }) + .optional(), + allowList: organizationAllowListSchema, +}) + +export type OrganizationSettings = z.infer diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 8b49dc1d62..8b919d4a30 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,5 +1,6 @@ export * from "./api.js" export * from "./codebase-index.js" +export * from "./cloud.js" export * from "./experiment.js" export * from "./global-settings.js" export * from "./history.js" diff --git a/packages/types/src/telemetry.ts b/packages/types/src/telemetry.ts index 78e996f766..967101f85d 100644 --- a/packages/types/src/telemetry.ts +++ b/packages/types/src/telemetry.ts @@ -1,6 +1,7 @@ import { z } from "zod" import { providerNames } from "./provider-settings.js" +import { clineMessageSchema } from "./message.js" /** * TelemetrySetting @@ -20,6 +21,7 @@ export enum TelemetryEventName { TASK_CREATED = "Task Created", TASK_RESTARTED = "Task Reopened", TASK_COMPLETED = "Task Completed", + TASK_MESSAGE = "Task Message", TASK_CONVERSATION_MESSAGE = "Conversation Message", LLM_COMPLETION = "LLM Completion", MODE_SWITCH = "Mode Switched", @@ -87,14 +89,6 @@ export type TelemetryEvent = { * RooCodeTelemetryEvent */ -const completionPropertiesSchema = z.object({ - inputTokens: z.number(), - outputTokens: z.number(), - cacheReadTokens: z.number().optional(), - cacheWriteTokens: z.number().optional(), - cost: z.number().optional(), -}) - export const rooCodeTelemetryEventSchema = z.discriminatedUnion("type", [ z.object({ type: z.enum([ @@ -116,19 +110,56 @@ export const rooCodeTelemetryEventSchema = z.discriminatedUnion("type", [ TelemetryEventName.SHELL_INTEGRATION_ERROR, TelemetryEventName.CONSECUTIVE_MISTAKE_ERROR, ]), + properties: telemetryPropertiesSchema, + }), + z.object({ + type: z.literal(TelemetryEventName.TASK_MESSAGE), properties: z.object({ - ...appPropertiesSchema.shape, - ...taskPropertiesSchema.shape, + taskId: z.string(), + message: clineMessageSchema, }), }), z.object({ type: z.literal(TelemetryEventName.LLM_COMPLETION), properties: z.object({ - ...appPropertiesSchema.shape, - ...taskPropertiesSchema.shape, - ...completionPropertiesSchema.shape, + ...telemetryPropertiesSchema.shape, + inputTokens: z.number(), + outputTokens: z.number(), + cacheReadTokens: z.number().optional(), + cacheWriteTokens: z.number().optional(), + cost: z.number().optional(), }), }), ]) export type RooCodeTelemetryEvent = z.infer + +/** + * TelemetryEventSubscription + */ + +export type TelemetryEventSubscription = + | { type: "include"; events: TelemetryEventName[] } + | { type: "exclude"; events: TelemetryEventName[] } + +/** + * TelemetryPropertiesProvider + */ + +export interface TelemetryPropertiesProvider { + getTelemetryProperties(): Promise +} + +/** + * TelemetryClient + */ + +export interface TelemetryClient { + subscription?: TelemetryEventSubscription + + setProvider(provider: TelemetryPropertiesProvider): void + capture(options: TelemetryEvent): Promise + updateTelemetryState(didUserOptIn: boolean): void + isTelemetryEnabled(): boolean + shutdown(): Promise +} diff --git a/packages/types/src/vscode.ts b/packages/types/src/vscode.ts index f12b71cdf7..5dfe1a6397 100644 --- a/packages/types/src/vscode.ts +++ b/packages/types/src/vscode.ts @@ -34,6 +34,7 @@ export const commandIds = [ "mcpButtonClicked", "historyButtonClicked", "popoutButtonClicked", + "accountButtonClicked", "settingsButtonClicked", "openInNewTab", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9340d50d2f..2f15e13c7a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -118,6 +118,34 @@ importers: specifier: ^3.1.3 version: 3.1.3(@types/debug@4.1.12)(@types/node@22.15.20)(jiti@2.4.2)(jsdom@20.0.3)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0) + packages/cloud: + dependencies: + '@roo-code/telemetry': + specifier: workspace:^ + version: link:../telemetry + '@roo-code/types': + specifier: workspace:^ + version: link:../types + axios: + specifier: ^1.7.4 + version: 1.9.0 + devDependencies: + '@roo-code/config-eslint': + specifier: workspace:^ + version: link:../config-eslint + '@roo-code/config-typescript': + specifier: workspace:^ + version: link:../config-typescript + '@types/node': + specifier: ^22.15.20 + version: 22.15.20 + '@types/vscode': + specifier: ^1.84.0 + version: 1.100.0 + vitest: + specifier: ^3.1.3 + version: 3.1.3(@types/debug@4.1.12)(@types/node@22.15.20)(jiti@2.4.2)(jsdom@20.0.3)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0) + packages/config-eslint: devDependencies: '@eslint/js': @@ -153,6 +181,34 @@ importers: packages/config-typescript: {} + packages/telemetry: + dependencies: + '@roo-code/types': + specifier: workspace:^ + version: link:../types + posthog-node: + specifier: ^4.7.0 + version: 4.17.2 + zod: + specifier: ^3.24.2 + version: 3.24.4 + devDependencies: + '@roo-code/config-eslint': + specifier: workspace:^ + version: link:../config-eslint + '@roo-code/config-typescript': + specifier: workspace:^ + version: link:../config-typescript + '@types/node': + specifier: ^22.15.20 + version: 22.15.20 + '@types/vscode': + specifier: ^1.84.0 + version: 1.100.0 + vitest: + specifier: ^3.1.3 + version: 3.1.3(@types/debug@4.1.12)(@types/node@22.15.20)(jiti@2.4.2)(jsdom@20.0.3)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.8.0) + packages/types: dependencies: zod: @@ -204,6 +260,12 @@ importers: '@qdrant/js-client-rest': specifier: ^1.14.0 version: 1.14.0(typescript@5.8.3) + '@roo-code/cloud': + specifier: workspace:^ + version: link:../packages/cloud + '@roo-code/telemetry': + specifier: workspace:^ + version: link:../packages/telemetry '@roo-code/types': specifier: workspace:^ version: link:../packages/types @@ -300,9 +362,6 @@ importers: pkce-challenge: specifier: ^4.1.0 version: 4.1.0 - posthog-node: - specifier: ^4.7.0 - version: 4.17.2 pretty-bytes: specifier: ^6.1.1 version: 6.1.1 diff --git a/src/activate/handleUri.ts b/src/activate/handleUri.ts index 96a24fe6fa..106bcdb311 100644 --- a/src/activate/handleUri.ts +++ b/src/activate/handleUri.ts @@ -1,11 +1,14 @@ import * as vscode from "vscode" +import { CloudService } from "@roo-code/cloud" + import { ClineProvider } from "../core/webview/ClineProvider" export const handleUri = async (uri: vscode.Uri) => { const path = uri.path const query = new URLSearchParams(uri.query.replace(/\+/g, "%2B")) const visibleProvider = ClineProvider.getVisibleInstance() + if (!visibleProvider) { return } @@ -32,6 +35,12 @@ export const handleUri = async (uri: vscode.Uri) => { } break } + case "/auth/clerk/callback": { + const code = query.get("code") + const state = query.get("state") + await CloudService.instance.handleAuthCallback(code, state) + break + } default: break } diff --git a/src/activate/registerCommands.ts b/src/activate/registerCommands.ts index cd76b11f96..3f575b74cb 100644 --- a/src/activate/registerCommands.ts +++ b/src/activate/registerCommands.ts @@ -2,12 +2,12 @@ import * as vscode from "vscode" import delay from "delay" import type { CommandId } from "@roo-code/types" +import { TelemetryService } from "@roo-code/telemetry" import { Package } from "../shared/package" import { getCommand } from "../utils/commands" import { ClineProvider } from "../core/webview/ClineProvider" import { ContextProxy } from "../core/config/ContextProxy" -import { telemetryService } from "../services/telemetry/TelemetryService" import { registerHumanRelayCallback, unregisterHumanRelayCallback, handleHumanRelayResponse } from "./humanRelay" import { handleNewTask } from "./handleTask" @@ -70,6 +70,17 @@ export const registerCommands = (options: RegisterCommandOptions) => { const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOptions): Record => ({ activationCompleted: () => {}, + accountButtonClicked: () => { + const visibleProvider = getVisibleProviderOrLog(outputChannel) + + if (!visibleProvider) { + return + } + + TelemetryService.instance.captureTitleButtonClicked("account") + + visibleProvider.postMessageToWebview({ type: "action", action: "accountButtonClicked" }) + }, plusButtonClicked: async () => { const visibleProvider = getVisibleProviderOrLog(outputChannel) @@ -77,7 +88,7 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt return } - telemetryService.captureTitleButtonClicked("plus") + TelemetryService.instance.captureTitleButtonClicked("plus") await visibleProvider.removeClineFromStack() await visibleProvider.postStateToWebview() @@ -90,7 +101,7 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt return } - telemetryService.captureTitleButtonClicked("mcp") + TelemetryService.instance.captureTitleButtonClicked("mcp") visibleProvider.postMessageToWebview({ type: "action", action: "mcpButtonClicked" }) }, @@ -101,12 +112,12 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt return } - telemetryService.captureTitleButtonClicked("prompts") + TelemetryService.instance.captureTitleButtonClicked("prompts") visibleProvider.postMessageToWebview({ type: "action", action: "promptsButtonClicked" }) }, popoutButtonClicked: () => { - telemetryService.captureTitleButtonClicked("popout") + TelemetryService.instance.captureTitleButtonClicked("popout") return openClineInNewTab({ context, outputChannel }) }, @@ -118,7 +129,7 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt return } - telemetryService.captureTitleButtonClicked("settings") + TelemetryService.instance.captureTitleButtonClicked("settings") visibleProvider.postMessageToWebview({ type: "action", action: "settingsButtonClicked" }) // Also explicitly post the visibility message to trigger scroll reliably @@ -131,7 +142,7 @@ const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOpt return } - telemetryService.captureTitleButtonClicked("history") + TelemetryService.instance.captureTitleButtonClicked("history") visibleProvider.postMessageToWebview({ type: "action", action: "historyButtonClicked" }) }, diff --git a/src/core/assistant-message/presentAssistantMessage.ts b/src/core/assistant-message/presentAssistantMessage.ts index 77c510889b..6f283eb0f3 100644 --- a/src/core/assistant-message/presentAssistantMessage.ts +++ b/src/core/assistant-message/presentAssistantMessage.ts @@ -2,12 +2,11 @@ import cloneDeep from "clone-deep" import { serializeError } from "serialize-error" import type { ToolName, ClineAsk, ToolProgressStatus } from "@roo-code/types" +import { TelemetryService } from "@roo-code/telemetry" import { defaultModeSlug, getModeBySlug } from "../../shared/modes" import type { ToolParamName, ToolResponse } from "../../shared/tools" -import { telemetryService } from "../../services/telemetry/TelemetryService" - import { fetchInstructionsTool } from "../tools/fetchInstructionsTool" import { listFilesTool } from "../tools/listFilesTool" import { readFileTool } from "../tools/readFileTool" @@ -320,7 +319,7 @@ export async function presentAssistantMessage(cline: Task) { if (!block.partial) { cline.recordToolUsage(block.name) - telemetryService.captureToolUsage(cline.taskId, block.name) + TelemetryService.instance.captureToolUsage(cline.taskId, block.name) } // Validate tool use before execution. @@ -368,7 +367,7 @@ export async function presentAssistantMessage(cline: Task) { await cline.say("user_feedback", text, images) // Track tool repetition in telemetry. - telemetryService.captureConsecutiveMistakeError(cline.taskId) + TelemetryService.instance.captureConsecutiveMistakeError(cline.taskId) } // Return tool result message about the repetition diff --git a/src/core/checkpoints/index.ts b/src/core/checkpoints/index.ts index 68b25b1256..b811b40c48 100644 --- a/src/core/checkpoints/index.ts +++ b/src/core/checkpoints/index.ts @@ -1,6 +1,8 @@ import pWaitFor from "p-wait-for" import * as vscode from "vscode" +import { TelemetryService } from "@roo-code/telemetry" + import { Task } from "../task/Task" import { getWorkspacePath } from "../../utils/path" @@ -10,7 +12,6 @@ import { getApiMetrics } from "../../shared/getApiMetrics" import { DIFF_VIEW_URI_SCHEME } from "../../integrations/editor/DiffViewProvider" -import { telemetryService } from "../../services/telemetry/TelemetryService" import { CheckpointServiceOptions, RepoPerTaskCheckpointService } from "../../services/checkpoints" export function getCheckpointService(cline: Task) { @@ -166,7 +167,7 @@ export async function checkpointSave(cline: Task, force = false) { return } - telemetryService.captureCheckpointCreated(cline.taskId) + TelemetryService.instance.captureCheckpointCreated(cline.taskId) // Start the checkpoint process in the background. return service.saveCheckpoint(`Task: ${cline.taskId}, Time: ${Date.now()}`, { allowEmpty: force }).catch((err) => { @@ -198,7 +199,7 @@ export async function checkpointRestore(cline: Task, { ts, commitHash, mode }: C try { await service.restoreCheckpoint(commitHash) - telemetryService.captureCheckpointRestored(cline.taskId) + TelemetryService.instance.captureCheckpointRestored(cline.taskId) await provider?.postMessageToWebview({ type: "currentCheckpointUpdated", text: commitHash }) if (mode === "restore") { @@ -256,7 +257,7 @@ export async function checkpointDiff(cline: Task, { ts, previousCommitHash, comm return } - telemetryService.captureCheckpointDiffed(cline.taskId) + TelemetryService.instance.captureCheckpointDiffed(cline.taskId) if (!previousCommitHash && mode === "checkpoint") { const previousCheckpoint = cline.clineMessages diff --git a/src/core/condense/__tests__/index.test.ts b/src/core/condense/__tests__/index.test.ts index e3b613f903..81994f3f5b 100644 --- a/src/core/condense/__tests__/index.test.ts +++ b/src/core/condense/__tests__/index.test.ts @@ -1,18 +1,23 @@ +// npx jest core/condense/__tests__/index.test.ts + import { describe, expect, it, jest, beforeEach } from "@jest/globals" + +import { TelemetryService } from "@roo-code/telemetry" + import { ApiHandler } from "../../../api" import { ApiMessage } from "../../task-persistence/apiMessages" import { maybeRemoveImageBlocks } from "../../../api/transform/image-cleaning" import { summarizeConversation, getMessagesSinceLastSummary, N_MESSAGES_TO_KEEP } from "../index" -import { telemetryService } from "../../../services/telemetry/TelemetryService" -// Mock dependencies jest.mock("../../../api/transform/image-cleaning", () => ({ maybeRemoveImageBlocks: jest.fn((messages: ApiMessage[], _apiHandler: ApiHandler) => [...messages]), })) -jest.mock("../../../services/telemetry/TelemetryService", () => ({ - telemetryService: { - captureContextCondensed: jest.fn(), +jest.mock("@roo-code/telemetry", () => ({ + TelemetryService: { + instance: { + captureContextCondensed: jest.fn(), + }, }, })) @@ -524,7 +529,7 @@ describe("summarizeConversation with custom settings", () => { jest.clearAllMocks() // Reset telemetry mock - ;(telemetryService.captureContextCondensed as jest.Mock).mockClear() + ;(TelemetryService.instance.captureContextCondensed as jest.Mock).mockClear() // Setup mock API handlers mockMainApiHandler = { @@ -729,7 +734,7 @@ describe("summarizeConversation with custom settings", () => { ) // Verify telemetry was called with custom prompt flag - expect(telemetryService.captureContextCondensed).toHaveBeenCalledWith( + expect(TelemetryService.instance.captureContextCondensed).toHaveBeenCalledWith( taskId, false, true, // usedCustomPrompt @@ -753,7 +758,7 @@ describe("summarizeConversation with custom settings", () => { ) // Verify telemetry was called with custom API handler flag - expect(telemetryService.captureContextCondensed).toHaveBeenCalledWith( + expect(TelemetryService.instance.captureContextCondensed).toHaveBeenCalledWith( taskId, false, false, // usedCustomPrompt @@ -777,7 +782,7 @@ describe("summarizeConversation with custom settings", () => { ) // Verify telemetry was called with both flags - expect(telemetryService.captureContextCondensed).toHaveBeenCalledWith( + expect(TelemetryService.instance.captureContextCondensed).toHaveBeenCalledWith( taskId, true, // isAutomaticTrigger true, // usedCustomPrompt diff --git a/src/core/condense/index.ts b/src/core/condense/index.ts index 58a81f3d22..07c52713ef 100644 --- a/src/core/condense/index.ts +++ b/src/core/condense/index.ts @@ -1,9 +1,11 @@ import Anthropic from "@anthropic-ai/sdk" + +import { TelemetryService } from "@roo-code/telemetry" + import { t } from "../../i18n" import { ApiHandler } from "../../api" import { ApiMessage } from "../task-persistence/apiMessages" import { maybeRemoveImageBlocks } from "../../api/transform/image-cleaning" -import { telemetryService } from "../../services/telemetry/TelemetryService" export const N_MESSAGES_TO_KEEP = 3 @@ -88,14 +90,16 @@ export async function summarizeConversation( customCondensingPrompt?: string, condensingApiHandler?: ApiHandler, ): Promise { - telemetryService.captureContextCondensed( + TelemetryService.instance.captureContextCondensed( taskId, isAutomaticTrigger ?? false, !!customCondensingPrompt?.trim(), !!condensingApiHandler, ) + const response: SummarizeResponse = { messages, cost: 0, summary: "" } const messagesToSummarize = getMessagesSinceLastSummary(messages.slice(0, -N_MESSAGES_TO_KEEP)) + if (messagesToSummarize.length <= 1) { const error = messages.length <= N_MESSAGES_TO_KEEP + 1 @@ -103,20 +107,25 @@ export async function summarizeConversation( : t("common:errors.condensed_recently") return { ...response, error } } + const keepMessages = messages.slice(-N_MESSAGES_TO_KEEP) // Check if there's a recent summary in the messages we're keeping const recentSummaryExists = keepMessages.some((message) => message.isSummary) + if (recentSummaryExists) { const error = t("common:errors.condensed_recently") return { ...response, error } } + const finalRequestMessage: Anthropic.MessageParam = { role: "user", content: "Summarize the conversation so far, as described in the prompt instructions.", } + const requestMessages = maybeRemoveImageBlocks([...messagesToSummarize, finalRequestMessage], apiHandler).map( ({ role, content }) => ({ role, content }), ) + // Note: this doesn't need to be a stream, consider using something like apiHandler.completePrompt // Use custom prompt if provided and non-empty, otherwise use the default SUMMARY_PROMPT const promptToUse = customCondensingPrompt?.trim() ? customCondensingPrompt.trim() : SUMMARY_PROMPT @@ -129,7 +138,9 @@ export async function summarizeConversation( console.warn( "Chosen API handler for condensing does not support message creation or is invalid, falling back to main apiHandler.", ) + handlerToUse = apiHandler // Fallback to the main, presumably valid, apiHandler + // Ensure the main apiHandler itself is valid before this point or add another check. if (!handlerToUse || typeof handlerToUse.createMessage !== "function") { // This case should ideally not happen if main apiHandler is always valid. @@ -142,9 +153,11 @@ export async function summarizeConversation( } const stream = handlerToUse.createMessage(promptToUse, requestMessages) + let summary = "" let cost = 0 let outputTokens = 0 + for await (const chunk of stream) { if (chunk.type === "text") { summary += chunk.text @@ -154,28 +167,35 @@ export async function summarizeConversation( outputTokens = chunk.outputTokens ?? 0 } } + summary = summary.trim() + if (summary.length === 0) { const error = t("common:errors.condense_failed") return { ...response, cost, error } } + const summaryMessage: ApiMessage = { role: "assistant", content: summary, ts: keepMessages[0].ts, isSummary: true, } + const newMessages = [...messages.slice(0, -N_MESSAGES_TO_KEEP), summaryMessage, ...keepMessages] // Count the tokens in the context for the next API request // We only estimate the tokens in summaryMesage if outputTokens is 0, otherwise we use outputTokens const systemPromptMessage: ApiMessage = { role: "user", content: systemPrompt } + const contextMessages = outputTokens ? [systemPromptMessage, ...keepMessages] : [systemPromptMessage, summaryMessage, ...keepMessages] + const contextBlocks = contextMessages.flatMap((message) => typeof message.content === "string" ? [{ text: message.content, type: "text" as const }] : message.content, ) + const newContextTokens = outputTokens + (await apiHandler.countTokens(contextBlocks)) if (newContextTokens >= prevContextTokens) { const error = t("common:errors.condense_context_grew") @@ -187,9 +207,11 @@ export async function summarizeConversation( /* Returns the list of all messages since the last summary message, including the summary. Returns all messages if there is no summary. */ export function getMessagesSinceLastSummary(messages: ApiMessage[]): ApiMessage[] { let lastSummaryIndexReverse = [...messages].reverse().findIndex((message) => message.isSummary) + if (lastSummaryIndexReverse === -1) { return messages } + const lastSummaryIndex = messages.length - lastSummaryIndexReverse - 1 return messages.slice(lastSummaryIndex) } diff --git a/src/core/config/ContextProxy.ts b/src/core/config/ContextProxy.ts index 874ed719f2..c4324fbb13 100644 --- a/src/core/config/ContextProxy.ts +++ b/src/core/config/ContextProxy.ts @@ -15,9 +15,9 @@ import { globalSettingsSchema, isSecretStateKey, } from "@roo-code/types" +import { TelemetryService } from "@roo-code/telemetry" import { logger } from "../../utils/logging" -import { telemetryService } from "../../services/telemetry/TelemetryService" type GlobalStateKey = keyof GlobalState type SecretStateKey = keyof SecretState @@ -162,7 +162,7 @@ export class ContextProxy { return globalSettingsSchema.parse(values) } catch (error) { if (error instanceof ZodError) { - telemetryService.captureSchemaValidationError({ schemaName: "GlobalSettings", error }) + TelemetryService.instance.captureSchemaValidationError({ schemaName: "GlobalSettings", error }) } return GLOBAL_SETTINGS_KEYS.reduce((acc, key) => ({ ...acc, [key]: values[key] }), {} as GlobalSettings) @@ -180,7 +180,7 @@ export class ContextProxy { return providerSettingsSchema.parse(values) } catch (error) { if (error instanceof ZodError) { - telemetryService.captureSchemaValidationError({ schemaName: "ProviderSettings", error }) + TelemetryService.instance.captureSchemaValidationError({ schemaName: "ProviderSettings", error }) } return PROVIDER_SETTINGS_KEYS.reduce((acc, key) => ({ ...acc, [key]: values[key] }), {} as ProviderSettings) @@ -248,7 +248,7 @@ export class ContextProxy { return Object.fromEntries(Object.entries(globalSettings).filter(([_, value]) => value !== undefined)) } catch (error) { if (error instanceof ZodError) { - telemetryService.captureSchemaValidationError({ schemaName: "GlobalSettings", error }) + TelemetryService.instance.captureSchemaValidationError({ schemaName: "GlobalSettings", error }) } return undefined diff --git a/src/core/config/ProviderSettingsManager.ts b/src/core/config/ProviderSettingsManager.ts index d4f2715318..32c0135d3b 100644 --- a/src/core/config/ProviderSettingsManager.ts +++ b/src/core/config/ProviderSettingsManager.ts @@ -6,9 +6,9 @@ import { providerSettingsSchema, providerSettingsSchemaDiscriminated, } from "@roo-code/types" +import { TelemetryService } from "@roo-code/telemetry" import { Mode, modes } from "../../shared/modes" -import { telemetryService } from "../../services/telemetry/TelemetryService" const providerSettingsWithIdSchema = providerSettingsSchema.extend({ id: z.string().optional() }) const discriminatedProviderSettingsWithIdSchema = providerSettingsSchemaDiscriminated.and( @@ -469,7 +469,10 @@ export class ProviderSettingsManager { } } catch (error) { if (error instanceof ZodError) { - telemetryService.captureSchemaValidationError({ schemaName: "ProviderProfiles", error }) + TelemetryService.instance.captureSchemaValidationError({ + schemaName: "ProviderProfiles", + error, + }) } throw new Error(`Failed to read provider profiles from secrets: ${error}`) diff --git a/src/core/config/__tests__/importExport.test.ts b/src/core/config/__tests__/importExport.test.ts index 40def4ebcd..89ac7dfef6 100644 --- a/src/core/config/__tests__/importExport.test.ts +++ b/src/core/config/__tests__/importExport.test.ts @@ -6,6 +6,7 @@ import * as path from "path" import * as vscode from "vscode" import type { ProviderName } from "@roo-code/types" +import { TelemetryService } from "@roo-code/telemetry" import { importSettings, exportSettings } from "../importExport" import { ProviderSettingsManager } from "../ProviderSettingsManager" @@ -41,6 +42,10 @@ describe("importExport", () => { beforeEach(() => { jest.clearAllMocks() + if (!TelemetryService.hasInstance()) { + TelemetryService.createInstance([]) + } + mockProviderSettingsManager = { export: jest.fn(), import: jest.fn(), diff --git a/src/core/config/importExport.ts b/src/core/config/importExport.ts index b9caef727e..4830a5f987 100644 --- a/src/core/config/importExport.ts +++ b/src/core/config/importExport.ts @@ -6,8 +6,7 @@ import * as vscode from "vscode" import { z, ZodError } from "zod" import { globalSettingsSchema } from "@roo-code/types" - -import { telemetryService } from "../../services/telemetry/TelemetryService" +import { TelemetryService } from "@roo-code/telemetry" import { ProviderSettingsManager, providerProfilesSchema } from "./ProviderSettingsManager" import { ContextProxy } from "./ContextProxy" @@ -84,7 +83,7 @@ export const importSettings = async ({ providerSettingsManager, contextProxy, cu if (e instanceof ZodError) { error = e.issues.map((issue) => `[${issue.path.join(".")}]: ${issue.message}`).join("\n") - telemetryService.captureSchemaValidationError({ schemaName: "ImportExport", error: e }) + TelemetryService.instance.captureSchemaValidationError({ schemaName: "ImportExport", error: e }) } else if (e instanceof Error) { error = e.message } diff --git a/src/core/sliding-window/__tests__/sliding-window.test.ts b/src/core/sliding-window/__tests__/sliding-window.test.ts index e99e2ed61f..a26ad6b53e 100644 --- a/src/core/sliding-window/__tests__/sliding-window.test.ts +++ b/src/core/sliding-window/__tests__/sliding-window.test.ts @@ -3,6 +3,7 @@ import { Anthropic } from "@anthropic-ai/sdk" import type { ModelInfo } from "@roo-code/types" +import { TelemetryService } from "@roo-code/telemetry" import { BaseProvider } from "../../../api/providers/base-provider" import { ApiMessage } from "../../task-persistence/apiMessages" @@ -41,28 +42,200 @@ class MockApiHandler extends BaseProvider { const mockApiHandler = new MockApiHandler() const taskId = "test-task-id" -/** - * Tests for the truncateConversation function - */ -describe("truncateConversation", () => { - it("should retain the first message", () => { - const messages: ApiMessage[] = [ - { role: "user", content: "First message" }, - { role: "assistant", content: "Second message" }, - { role: "user", content: "Third message" }, - ] +describe("Sliding Window", () => { + beforeEach(() => { + if (!TelemetryService.hasInstance()) { + TelemetryService.createInstance([]) + } + }) + /** + * Tests for the truncateConversation function + */ + describe("truncateConversation", () => { + it("should retain the first message", () => { + const messages: ApiMessage[] = [ + { role: "user", content: "First message" }, + { role: "assistant", content: "Second message" }, + { role: "user", content: "Third message" }, + ] - const result = truncateConversation(messages, 0.5, taskId) + const result = truncateConversation(messages, 0.5, taskId) - // With 2 messages after the first, 0.5 fraction means remove 1 message - // But 1 is odd, so it rounds down to 0 (to make it even) - expect(result.length).toBe(3) // First message + 2 remaining messages - expect(result[0]).toEqual(messages[0]) - expect(result[1]).toEqual(messages[1]) - expect(result[2]).toEqual(messages[2]) + // With 2 messages after the first, 0.5 fraction means remove 1 message + // But 1 is odd, so it rounds down to 0 (to make it even) + expect(result.length).toBe(3) // First message + 2 remaining messages + expect(result[0]).toEqual(messages[0]) + expect(result[1]).toEqual(messages[1]) + expect(result[2]).toEqual(messages[2]) + }) + + it("should remove the specified fraction of messages (rounded to even number)", () => { + const messages: ApiMessage[] = [ + { role: "user", content: "First message" }, + { role: "assistant", content: "Second message" }, + { role: "user", content: "Third message" }, + { role: "assistant", content: "Fourth message" }, + { role: "user", content: "Fifth message" }, + ] + + // 4 messages excluding first, 0.5 fraction = 2 messages to remove + // 2 is already even, so no rounding needed + const result = truncateConversation(messages, 0.5, taskId) + + expect(result.length).toBe(3) + expect(result[0]).toEqual(messages[0]) + expect(result[1]).toEqual(messages[3]) + expect(result[2]).toEqual(messages[4]) + }) + + it("should round to an even number of messages to remove", () => { + const messages: ApiMessage[] = [ + { role: "user", content: "First message" }, + { role: "assistant", content: "Second message" }, + { role: "user", content: "Third message" }, + { role: "assistant", content: "Fourth message" }, + { role: "user", content: "Fifth message" }, + { role: "assistant", content: "Sixth message" }, + { role: "user", content: "Seventh message" }, + ] + + // 6 messages excluding first, 0.3 fraction = 1.8 messages to remove + // 1.8 rounds down to 1, then to 0 to make it even + const result = truncateConversation(messages, 0.3, taskId) + + expect(result.length).toBe(7) // No messages removed + expect(result).toEqual(messages) + }) + + it("should handle edge case with fracToRemove = 0", () => { + const messages: ApiMessage[] = [ + { role: "user", content: "First message" }, + { role: "assistant", content: "Second message" }, + { role: "user", content: "Third message" }, + ] + + const result = truncateConversation(messages, 0, taskId) + + expect(result).toEqual(messages) + }) + + it("should handle edge case with fracToRemove = 1", () => { + const messages: ApiMessage[] = [ + { role: "user", content: "First message" }, + { role: "assistant", content: "Second message" }, + { role: "user", content: "Third message" }, + { role: "assistant", content: "Fourth message" }, + ] + + // 3 messages excluding first, 1.0 fraction = 3 messages to remove + // But 3 is odd, so it rounds down to 2 to make it even + const result = truncateConversation(messages, 1, taskId) + + expect(result.length).toBe(2) + expect(result[0]).toEqual(messages[0]) + expect(result[1]).toEqual(messages[3]) + }) }) - it("should remove the specified fraction of messages (rounded to even number)", () => { + /** + * Tests for the estimateTokenCount function + */ + describe("estimateTokenCount", () => { + it("should return 0 for empty or undefined content", async () => { + expect(await estimateTokenCount([], mockApiHandler)).toBe(0) + // @ts-ignore - Testing with undefined + expect(await estimateTokenCount(undefined, mockApiHandler)).toBe(0) + }) + + it("should estimate tokens for text blocks", async () => { + const content: Array = [ + { type: "text", text: "This is a text block with 36 characters" }, + ] + + // With tiktoken, the exact token count may differ from character-based estimation + // Instead of expecting an exact number, we verify it's a reasonable positive number + const result = await estimateTokenCount(content, mockApiHandler) + expect(result).toBeGreaterThan(0) + + // We can also verify that longer text results in more tokens + const longerContent: Array = [ + { + type: "text", + text: "This is a longer text block with significantly more characters to encode into tokens", + }, + ] + const longerResult = await estimateTokenCount(longerContent, mockApiHandler) + expect(longerResult).toBeGreaterThan(result) + }) + + it("should estimate tokens for image blocks based on data size", async () => { + // Small image + const smallImage: Array = [ + { type: "image", source: { type: "base64", media_type: "image/jpeg", data: "small_dummy_data" } }, + ] + // Larger image with more data + const largerImage: Array = [ + { type: "image", source: { type: "base64", media_type: "image/png", data: "X".repeat(1000) } }, + ] + + // Verify the token count scales with the size of the image data + const smallImageTokens = await estimateTokenCount(smallImage, mockApiHandler) + const largerImageTokens = await estimateTokenCount(largerImage, mockApiHandler) + + // Small image should have some tokens + expect(smallImageTokens).toBeGreaterThan(0) + + // Larger image should have proportionally more tokens + expect(largerImageTokens).toBeGreaterThan(smallImageTokens) + + // Verify the larger image calculation matches our formula including the 50% fudge factor + expect(largerImageTokens).toBe(48) + }) + + it("should estimate tokens for mixed content blocks", async () => { + const content: Array = [ + { type: "text", text: "A text block with 30 characters" }, + { type: "image", source: { type: "base64", media_type: "image/jpeg", data: "dummy_data" } }, + { type: "text", text: "Another text with 24 chars" }, + ] + + // We know image tokens calculation should be consistent + const imageTokens = Math.ceil(Math.sqrt("dummy_data".length)) * 1.5 + + // With tiktoken, we can't predict exact text token counts, + // but we can verify the total is greater than just the image tokens + const result = await estimateTokenCount(content, mockApiHandler) + expect(result).toBeGreaterThan(imageTokens) + + // Also test against a version with only the image to verify text adds tokens + const imageOnlyContent: Array = [ + { type: "image", source: { type: "base64", media_type: "image/jpeg", data: "dummy_data" } }, + ] + const imageOnlyResult = await estimateTokenCount(imageOnlyContent, mockApiHandler) + expect(result).toBeGreaterThan(imageOnlyResult) + }) + + it("should handle empty text blocks", async () => { + const content: Array = [{ type: "text", text: "" }] + expect(await estimateTokenCount(content, mockApiHandler)).toBe(0) + }) + + it("should handle plain string messages", async () => { + const content = "This is a plain text message" + expect(await estimateTokenCount([{ type: "text", text: content }], mockApiHandler)).toBeGreaterThan(0) + }) + }) + + /** + * Tests for the truncateConversationIfNeeded function + */ + describe("truncateConversationIfNeeded", () => { + const createModelInfo = (contextWindow: number, maxTokens?: number): ModelInfo => ({ + contextWindow, + supportsPromptCache: true, + maxTokens, + }) + const messages: ApiMessage[] = [ { role: "user", content: "First message" }, { role: "assistant", content: "Second message" }, @@ -71,856 +244,746 @@ describe("truncateConversation", () => { { role: "user", content: "Fifth message" }, ] - // 4 messages excluding first, 0.5 fraction = 2 messages to remove - // 2 is already even, so no rounding needed - const result = truncateConversation(messages, 0.5, taskId) + it("should not truncate if tokens are below max tokens threshold", async () => { + const modelInfo = createModelInfo(100000, 30000) + const dynamicBuffer = modelInfo.contextWindow * TOKEN_BUFFER_PERCENTAGE // 10000 + const totalTokens = 70000 - dynamicBuffer - 1 // Just below threshold - buffer - expect(result.length).toBe(3) - expect(result[0]).toEqual(messages[0]) - expect(result[1]).toEqual(messages[3]) - expect(result[2]).toEqual(messages[4]) + // Create messages with very small content in the last one to avoid token overflow + const messagesWithSmallContent = [ + ...messages.slice(0, -1), + { ...messages[messages.length - 1], content: "" }, + ] + + const result = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens, + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: false, + autoCondenseContextPercent: 100, + systemPrompt: "System prompt", + taskId, + }) + + // Check the new return type + expect(result).toEqual({ + messages: messagesWithSmallContent, + summary: "", + cost: 0, + prevContextTokens: totalTokens, + }) + }) + + it("should truncate if tokens are above max tokens threshold", async () => { + const modelInfo = createModelInfo(100000, 30000) + const totalTokens = 70001 // Above threshold + + // Create messages with very small content in the last one to avoid token overflow + const messagesWithSmallContent = [ + ...messages.slice(0, -1), + { ...messages[messages.length - 1], content: "" }, + ] + + // When truncating, always uses 0.5 fraction + // With 4 messages after the first, 0.5 fraction means remove 2 messages + const expectedMessages = [ + messagesWithSmallContent[0], + messagesWithSmallContent[3], + messagesWithSmallContent[4], + ] + + const result = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens, + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: false, + autoCondenseContextPercent: 100, + systemPrompt: "System prompt", + taskId, + }) + + expect(result).toEqual({ + messages: expectedMessages, + summary: "", + cost: 0, + prevContextTokens: totalTokens, + }) + }) + + it("should work with non-prompt caching models the same as prompt caching models", async () => { + // The implementation no longer differentiates between prompt caching and non-prompt caching models + const modelInfo1 = createModelInfo(100000, 30000) + const modelInfo2 = createModelInfo(100000, 30000) + + // Create messages with very small content in the last one to avoid token overflow + const messagesWithSmallContent = [ + ...messages.slice(0, -1), + { ...messages[messages.length - 1], content: "" }, + ] + + // Test below threshold + const belowThreshold = 69999 + const result1 = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens: belowThreshold, + contextWindow: modelInfo1.contextWindow, + maxTokens: modelInfo1.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: false, + autoCondenseContextPercent: 100, + systemPrompt: "System prompt", + taskId, + }) + + const result2 = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens: belowThreshold, + contextWindow: modelInfo2.contextWindow, + maxTokens: modelInfo2.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: false, + autoCondenseContextPercent: 100, + systemPrompt: "System prompt", + taskId, + }) + + expect(result1.messages).toEqual(result2.messages) + expect(result1.summary).toEqual(result2.summary) + expect(result1.cost).toEqual(result2.cost) + expect(result1.prevContextTokens).toEqual(result2.prevContextTokens) + + // Test above threshold + const aboveThreshold = 70001 + const result3 = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens: aboveThreshold, + contextWindow: modelInfo1.contextWindow, + maxTokens: modelInfo1.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: false, + autoCondenseContextPercent: 100, + systemPrompt: "System prompt", + taskId, + }) + + const result4 = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens: aboveThreshold, + contextWindow: modelInfo2.contextWindow, + maxTokens: modelInfo2.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: false, + autoCondenseContextPercent: 100, + systemPrompt: "System prompt", + taskId, + }) + + expect(result3.messages).toEqual(result4.messages) + expect(result3.summary).toEqual(result4.summary) + expect(result3.cost).toEqual(result4.cost) + expect(result3.prevContextTokens).toEqual(result4.prevContextTokens) + }) + + it("should consider incoming content when deciding to truncate", async () => { + const modelInfo = createModelInfo(100000, 30000) + const maxTokens = 30000 + const availableTokens = modelInfo.contextWindow - maxTokens + + // Test case 1: Small content that won't push us over the threshold + const smallContent = [{ type: "text" as const, text: "Small content" }] + const smallContentTokens = await estimateTokenCount(smallContent, mockApiHandler) + const messagesWithSmallContent: ApiMessage[] = [ + ...messages.slice(0, -1), + { role: messages[messages.length - 1].role, content: smallContent }, + ] + + // Set base tokens so total is well below threshold + buffer even with small content added + const dynamicBuffer = modelInfo.contextWindow * TOKEN_BUFFER_PERCENTAGE + const baseTokensForSmall = availableTokens - smallContentTokens - dynamicBuffer - 10 + const resultWithSmall = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens: baseTokensForSmall, + contextWindow: modelInfo.contextWindow, + maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: false, + autoCondenseContextPercent: 100, + systemPrompt: "System prompt", + taskId, + }) + expect(resultWithSmall).toEqual({ + messages: messagesWithSmallContent, + summary: "", + cost: 0, + prevContextTokens: baseTokensForSmall + smallContentTokens, + }) // No truncation + + // Test case 2: Large content that will push us over the threshold + const largeContent = [ + { + type: "text" as const, + text: "A very large incoming message that would consume a significant number of tokens and push us over the threshold", + }, + ] + const largeContentTokens = await estimateTokenCount(largeContent, mockApiHandler) + const messagesWithLargeContent: ApiMessage[] = [ + ...messages.slice(0, -1), + { role: messages[messages.length - 1].role, content: largeContent }, + ] + + // Set base tokens so we're just below threshold without content, but over with content + const baseTokensForLarge = availableTokens - Math.floor(largeContentTokens / 2) + const resultWithLarge = await truncateConversationIfNeeded({ + messages: messagesWithLargeContent, + totalTokens: baseTokensForLarge, + contextWindow: modelInfo.contextWindow, + maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: false, + autoCondenseContextPercent: 100, + systemPrompt: "System prompt", + taskId, + }) + expect(resultWithLarge.messages).not.toEqual(messagesWithLargeContent) // Should truncate + expect(resultWithLarge.summary).toBe("") + expect(resultWithLarge.cost).toBe(0) + expect(resultWithLarge.prevContextTokens).toBe(baseTokensForLarge + largeContentTokens) + + // Test case 3: Very large content that will definitely exceed threshold + const veryLargeContent = [{ type: "text" as const, text: "X".repeat(1000) }] + const veryLargeContentTokens = await estimateTokenCount(veryLargeContent, mockApiHandler) + const messagesWithVeryLargeContent: ApiMessage[] = [ + ...messages.slice(0, -1), + { role: messages[messages.length - 1].role, content: veryLargeContent }, + ] + + // Set base tokens so we're just below threshold without content + const baseTokensForVeryLarge = availableTokens - Math.floor(veryLargeContentTokens / 2) + const resultWithVeryLarge = await truncateConversationIfNeeded({ + messages: messagesWithVeryLargeContent, + totalTokens: baseTokensForVeryLarge, + contextWindow: modelInfo.contextWindow, + maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: false, + autoCondenseContextPercent: 100, + systemPrompt: "System prompt", + taskId, + }) + expect(resultWithVeryLarge.messages).not.toEqual(messagesWithVeryLargeContent) // Should truncate + expect(resultWithVeryLarge.summary).toBe("") + expect(resultWithVeryLarge.cost).toBe(0) + expect(resultWithVeryLarge.prevContextTokens).toBe(baseTokensForVeryLarge + veryLargeContentTokens) + }) + + it("should truncate if tokens are within TOKEN_BUFFER_PERCENTAGE of the threshold", async () => { + const modelInfo = createModelInfo(100000, 30000) + const dynamicBuffer = modelInfo.contextWindow * TOKEN_BUFFER_PERCENTAGE // 10% of 100000 = 10000 + const totalTokens = 70000 - dynamicBuffer + 1 // Just within the dynamic buffer of threshold (70000) + + // Create messages with very small content in the last one to avoid token overflow + const messagesWithSmallContent = [ + ...messages.slice(0, -1), + { ...messages[messages.length - 1], content: "" }, + ] + + // When truncating, always uses 0.5 fraction + // With 4 messages after the first, 0.5 fraction means remove 2 messages + const expectedResult = [ + messagesWithSmallContent[0], + messagesWithSmallContent[3], + messagesWithSmallContent[4], + ] + + const result = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens, + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: false, + autoCondenseContextPercent: 100, + systemPrompt: "System prompt", + taskId, + }) + expect(result).toEqual({ + messages: expectedResult, + summary: "", + cost: 0, + prevContextTokens: totalTokens, + }) + }) + + it("should use summarizeConversation when autoCondenseContext is true and tokens exceed threshold", async () => { + // Mock the summarizeConversation function + const mockSummary = "This is a summary of the conversation" + const mockCost = 0.05 + const mockSummarizeResponse: condenseModule.SummarizeResponse = { + messages: [ + { role: "user", content: "First message" }, + { role: "assistant", content: mockSummary, isSummary: true }, + { role: "user", content: "Last message" }, + ], + summary: mockSummary, + cost: mockCost, + newContextTokens: 100, + } + + const summarizeSpy = jest + .spyOn(condenseModule, "summarizeConversation") + .mockResolvedValue(mockSummarizeResponse) + + const modelInfo = createModelInfo(100000, 30000) + const totalTokens = 70001 // Above threshold + const messagesWithSmallContent = [ + ...messages.slice(0, -1), + { ...messages[messages.length - 1], content: "" }, + ] + + const result = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens, + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: true, + autoCondenseContextPercent: 100, + systemPrompt: "System prompt", + taskId, + }) + + // Verify summarizeConversation was called with the right parameters + expect(summarizeSpy).toHaveBeenCalledWith( + messagesWithSmallContent, + mockApiHandler, + "System prompt", + taskId, + 70001, + true, + undefined, // customCondensingPrompt + undefined, // condensingApiHandler + ) + + // Verify the result contains the summary information + expect(result).toMatchObject({ + messages: mockSummarizeResponse.messages, + summary: mockSummary, + cost: mockCost, + prevContextTokens: totalTokens, + }) + // newContextTokens might be present, but we don't need to verify its exact value + + // Clean up + summarizeSpy.mockRestore() + }) + + it("should fall back to truncateConversation when autoCondenseContext is true but summarization fails", async () => { + // Mock the summarizeConversation function to return an error + const mockSummarizeResponse: condenseModule.SummarizeResponse = { + messages: messages, // Original messages unchanged + summary: "", // Empty summary + cost: 0.01, + error: "Summarization failed", // Error indicates failure + } + + const summarizeSpy = jest + .spyOn(condenseModule, "summarizeConversation") + .mockResolvedValue(mockSummarizeResponse) + + const modelInfo = createModelInfo(100000, 30000) + const totalTokens = 70001 // Above threshold + const messagesWithSmallContent = [ + ...messages.slice(0, -1), + { ...messages[messages.length - 1], content: "" }, + ] + + // When truncating, always uses 0.5 fraction + // With 4 messages after the first, 0.5 fraction means remove 2 messages + const expectedMessages = [ + messagesWithSmallContent[0], + messagesWithSmallContent[3], + messagesWithSmallContent[4], + ] + + const result = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens, + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: true, + autoCondenseContextPercent: 100, + systemPrompt: "System prompt", + taskId, + }) + + // Verify summarizeConversation was called + expect(summarizeSpy).toHaveBeenCalled() + + // Verify it fell back to truncation + expect(result.messages).toEqual(expectedMessages) + expect(result.summary).toBe("") + expect(result.prevContextTokens).toBe(totalTokens) + // The cost might be different than expected, so we don't check it + + // Clean up + summarizeSpy.mockRestore() + }) + + it("should not call summarizeConversation when autoCondenseContext is false", async () => { + // Reset any previous mock calls + jest.clearAllMocks() + const summarizeSpy = jest.spyOn(condenseModule, "summarizeConversation") + + const modelInfo = createModelInfo(100000, 30000) + const totalTokens = 70001 // Above threshold + const messagesWithSmallContent = [ + ...messages.slice(0, -1), + { ...messages[messages.length - 1], content: "" }, + ] + + // When truncating, always uses 0.5 fraction + // With 4 messages after the first, 0.5 fraction means remove 2 messages + const expectedMessages = [ + messagesWithSmallContent[0], + messagesWithSmallContent[3], + messagesWithSmallContent[4], + ] + + const result = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens, + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: false, + autoCondenseContextPercent: 50, // This shouldn't matter since autoCondenseContext is false + systemPrompt: "System prompt", + taskId, + }) + + // Verify summarizeConversation was not called + expect(summarizeSpy).not.toHaveBeenCalled() + + // Verify it used truncation + expect(result).toEqual({ + messages: expectedMessages, + summary: "", + cost: 0, + prevContextTokens: totalTokens, + }) + + // Clean up + summarizeSpy.mockRestore() + }) + + it("should use summarizeConversation when autoCondenseContext is true and context percent exceeds threshold", async () => { + // Mock the summarizeConversation function + const mockSummary = "This is a summary of the conversation" + const mockCost = 0.05 + const mockSummarizeResponse: condenseModule.SummarizeResponse = { + messages: [ + { role: "user", content: "First message" }, + { role: "assistant", content: mockSummary, isSummary: true }, + { role: "user", content: "Last message" }, + ], + summary: mockSummary, + cost: mockCost, + newContextTokens: 100, + } + + const summarizeSpy = jest + .spyOn(condenseModule, "summarizeConversation") + .mockResolvedValue(mockSummarizeResponse) + + const modelInfo = createModelInfo(100000, 30000) + // Set tokens to be below the allowedTokens threshold but above the percentage threshold + const contextWindow = modelInfo.contextWindow + const totalTokens = 60000 // Below allowedTokens but 60% of context window + const messagesWithSmallContent = [ + ...messages.slice(0, -1), + { ...messages[messages.length - 1], content: "" }, + ] + + const result = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens, + contextWindow, + maxTokens: modelInfo.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: true, + autoCondenseContextPercent: 50, // Set threshold to 50% - our tokens are at 60% + systemPrompt: "System prompt", + taskId, + }) + + // Verify summarizeConversation was called with the right parameters + expect(summarizeSpy).toHaveBeenCalledWith( + messagesWithSmallContent, + mockApiHandler, + "System prompt", + taskId, + 60000, + true, + undefined, // customCondensingPrompt + undefined, // condensingApiHandler + ) + + // Verify the result contains the summary information + expect(result).toMatchObject({ + messages: mockSummarizeResponse.messages, + summary: mockSummary, + cost: mockCost, + prevContextTokens: totalTokens, + }) + + // Clean up + summarizeSpy.mockRestore() + }) + + it("should not use summarizeConversation when autoCondenseContext is true but context percent is below threshold", async () => { + // Reset any previous mock calls + jest.clearAllMocks() + const summarizeSpy = jest.spyOn(condenseModule, "summarizeConversation") + + const modelInfo = createModelInfo(100000, 30000) + // Set tokens to be below both the allowedTokens threshold and the percentage threshold + const contextWindow = modelInfo.contextWindow + const totalTokens = 40000 // 40% of context window + const messagesWithSmallContent = [ + ...messages.slice(0, -1), + { ...messages[messages.length - 1], content: "" }, + ] + + const result = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens, + contextWindow, + maxTokens: modelInfo.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: true, + autoCondenseContextPercent: 50, // Set threshold to 50% - our tokens are at 40% + systemPrompt: "System prompt", + taskId, + }) + + // Verify summarizeConversation was not called + expect(summarizeSpy).not.toHaveBeenCalled() + + // Verify no truncation or summarization occurred + expect(result).toEqual({ + messages: messagesWithSmallContent, + summary: "", + cost: 0, + prevContextTokens: totalTokens, + }) + + // Clean up + summarizeSpy.mockRestore() + }) }) - it("should round to an even number of messages to remove", () => { + /** + * Tests for the getMaxTokens function (private but tested through truncateConversationIfNeeded) + */ + describe("getMaxTokens", () => { + // We'll test this indirectly through truncateConversationIfNeeded + const createModelInfo = (contextWindow: number, maxTokens?: number): ModelInfo => ({ + contextWindow, + supportsPromptCache: true, // Not relevant for getMaxTokens + maxTokens, + }) + + // Reuse across tests for consistency const messages: ApiMessage[] = [ { role: "user", content: "First message" }, { role: "assistant", content: "Second message" }, { role: "user", content: "Third message" }, { role: "assistant", content: "Fourth message" }, { role: "user", content: "Fifth message" }, - { role: "assistant", content: "Sixth message" }, - { role: "user", content: "Seventh message" }, ] - // 6 messages excluding first, 0.3 fraction = 1.8 messages to remove - // 1.8 rounds down to 1, then to 0 to make it even - const result = truncateConversation(messages, 0.3, taskId) + it("should use maxTokens as buffer when specified", async () => { + const modelInfo = createModelInfo(100000, 50000) + // Max tokens = 100000 - 50000 = 50000 - expect(result.length).toBe(7) // No messages removed - expect(result).toEqual(messages) - }) + // Create messages with very small content in the last one to avoid token overflow + const messagesWithSmallContent = [ + ...messages.slice(0, -1), + { ...messages[messages.length - 1], content: "" }, + ] - it("should handle edge case with fracToRemove = 0", () => { - const messages: ApiMessage[] = [ - { role: "user", content: "First message" }, - { role: "assistant", content: "Second message" }, - { role: "user", content: "Third message" }, - ] + // Account for the dynamic buffer which is 10% of context window (10,000 tokens) + // Below max tokens and buffer - no truncation + const result1 = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens: 39999, // Well below threshold + dynamic buffer + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: false, + autoCondenseContextPercent: 100, + systemPrompt: "System prompt", + taskId, + }) + expect(result1).toEqual({ + messages: messagesWithSmallContent, + summary: "", + cost: 0, + prevContextTokens: 39999, + }) - const result = truncateConversation(messages, 0, taskId) + // Above max tokens - truncate + const result2 = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens: 50001, // Above threshold + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: false, + autoCondenseContextPercent: 100, + systemPrompt: "System prompt", + taskId, + }) + expect(result2.messages).not.toEqual(messagesWithSmallContent) + expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction + expect(result2.summary).toBe("") + expect(result2.cost).toBe(0) + expect(result2.prevContextTokens).toBe(50001) + }) - expect(result).toEqual(messages) - }) + it("should use 20% of context window as buffer when maxTokens is undefined", async () => { + const modelInfo = createModelInfo(100000, undefined) + // Max tokens = 100000 - (100000 * 0.2) = 80000 - it("should handle edge case with fracToRemove = 1", () => { - const messages: ApiMessage[] = [ - { role: "user", content: "First message" }, - { role: "assistant", content: "Second message" }, - { role: "user", content: "Third message" }, - { role: "assistant", content: "Fourth message" }, - ] + // Create messages with very small content in the last one to avoid token overflow + const messagesWithSmallContent = [ + ...messages.slice(0, -1), + { ...messages[messages.length - 1], content: "" }, + ] - // 3 messages excluding first, 1.0 fraction = 3 messages to remove - // But 3 is odd, so it rounds down to 2 to make it even - const result = truncateConversation(messages, 1, taskId) + // Account for the dynamic buffer which is 10% of context window (10,000 tokens) + // Below max tokens and buffer - no truncation + const result1 = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens: 69999, // Well below threshold + dynamic buffer + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: false, + autoCondenseContextPercent: 100, + systemPrompt: "System prompt", + taskId, + }) + expect(result1).toEqual({ + messages: messagesWithSmallContent, + summary: "", + cost: 0, + prevContextTokens: 69999, + }) - expect(result.length).toBe(2) - expect(result[0]).toEqual(messages[0]) - expect(result[1]).toEqual(messages[3]) - }) -}) - -/** - * Tests for the estimateTokenCount function - */ -describe("estimateTokenCount", () => { - it("should return 0 for empty or undefined content", async () => { - expect(await estimateTokenCount([], mockApiHandler)).toBe(0) - // @ts-ignore - Testing with undefined - expect(await estimateTokenCount(undefined, mockApiHandler)).toBe(0) - }) - - it("should estimate tokens for text blocks", async () => { - const content: Array = [ - { type: "text", text: "This is a text block with 36 characters" }, - ] - - // With tiktoken, the exact token count may differ from character-based estimation - // Instead of expecting an exact number, we verify it's a reasonable positive number - const result = await estimateTokenCount(content, mockApiHandler) - expect(result).toBeGreaterThan(0) - - // We can also verify that longer text results in more tokens - const longerContent: Array = [ - { - type: "text", - text: "This is a longer text block with significantly more characters to encode into tokens", - }, - ] - const longerResult = await estimateTokenCount(longerContent, mockApiHandler) - expect(longerResult).toBeGreaterThan(result) - }) - - it("should estimate tokens for image blocks based on data size", async () => { - // Small image - const smallImage: Array = [ - { type: "image", source: { type: "base64", media_type: "image/jpeg", data: "small_dummy_data" } }, - ] - // Larger image with more data - const largerImage: Array = [ - { type: "image", source: { type: "base64", media_type: "image/png", data: "X".repeat(1000) } }, - ] - - // Verify the token count scales with the size of the image data - const smallImageTokens = await estimateTokenCount(smallImage, mockApiHandler) - const largerImageTokens = await estimateTokenCount(largerImage, mockApiHandler) - - // Small image should have some tokens - expect(smallImageTokens).toBeGreaterThan(0) - - // Larger image should have proportionally more tokens - expect(largerImageTokens).toBeGreaterThan(smallImageTokens) - - // Verify the larger image calculation matches our formula including the 50% fudge factor - expect(largerImageTokens).toBe(48) - }) - - it("should estimate tokens for mixed content blocks", async () => { - const content: Array = [ - { type: "text", text: "A text block with 30 characters" }, - { type: "image", source: { type: "base64", media_type: "image/jpeg", data: "dummy_data" } }, - { type: "text", text: "Another text with 24 chars" }, - ] - - // We know image tokens calculation should be consistent - const imageTokens = Math.ceil(Math.sqrt("dummy_data".length)) * 1.5 - - // With tiktoken, we can't predict exact text token counts, - // but we can verify the total is greater than just the image tokens - const result = await estimateTokenCount(content, mockApiHandler) - expect(result).toBeGreaterThan(imageTokens) - - // Also test against a version with only the image to verify text adds tokens - const imageOnlyContent: Array = [ - { type: "image", source: { type: "base64", media_type: "image/jpeg", data: "dummy_data" } }, - ] - const imageOnlyResult = await estimateTokenCount(imageOnlyContent, mockApiHandler) - expect(result).toBeGreaterThan(imageOnlyResult) - }) - - it("should handle empty text blocks", async () => { - const content: Array = [{ type: "text", text: "" }] - expect(await estimateTokenCount(content, mockApiHandler)).toBe(0) - }) - - it("should handle plain string messages", async () => { - const content = "This is a plain text message" - expect(await estimateTokenCount([{ type: "text", text: content }], mockApiHandler)).toBeGreaterThan(0) - }) -}) - -/** - * Tests for the truncateConversationIfNeeded function - */ -describe("truncateConversationIfNeeded", () => { - const createModelInfo = (contextWindow: number, maxTokens?: number): ModelInfo => ({ - contextWindow, - supportsPromptCache: true, - maxTokens, - }) - - const messages: ApiMessage[] = [ - { role: "user", content: "First message" }, - { role: "assistant", content: "Second message" }, - { role: "user", content: "Third message" }, - { role: "assistant", content: "Fourth message" }, - { role: "user", content: "Fifth message" }, - ] - - it("should not truncate if tokens are below max tokens threshold", async () => { - const modelInfo = createModelInfo(100000, 30000) - const dynamicBuffer = modelInfo.contextWindow * TOKEN_BUFFER_PERCENTAGE // 10000 - const totalTokens = 70000 - dynamicBuffer - 1 // Just below threshold - buffer - - // Create messages with very small content in the last one to avoid token overflow - const messagesWithSmallContent = [...messages.slice(0, -1), { ...messages[messages.length - 1], content: "" }] - - const result = await truncateConversationIfNeeded({ - messages: messagesWithSmallContent, - totalTokens, - contextWindow: modelInfo.contextWindow, - maxTokens: modelInfo.maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: false, - autoCondenseContextPercent: 100, - systemPrompt: "System prompt", - taskId, - }) - - // Check the new return type - expect(result).toEqual({ - messages: messagesWithSmallContent, - summary: "", - cost: 0, - prevContextTokens: totalTokens, - }) - }) - - it("should truncate if tokens are above max tokens threshold", async () => { - const modelInfo = createModelInfo(100000, 30000) - const totalTokens = 70001 // Above threshold - - // Create messages with very small content in the last one to avoid token overflow - const messagesWithSmallContent = [...messages.slice(0, -1), { ...messages[messages.length - 1], content: "" }] - - // When truncating, always uses 0.5 fraction - // With 4 messages after the first, 0.5 fraction means remove 2 messages - const expectedMessages = [messagesWithSmallContent[0], messagesWithSmallContent[3], messagesWithSmallContent[4]] - - const result = await truncateConversationIfNeeded({ - messages: messagesWithSmallContent, - totalTokens, - contextWindow: modelInfo.contextWindow, - maxTokens: modelInfo.maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: false, - autoCondenseContextPercent: 100, - systemPrompt: "System prompt", - taskId, - }) - - expect(result).toEqual({ - messages: expectedMessages, - summary: "", - cost: 0, - prevContextTokens: totalTokens, - }) - }) - - it("should work with non-prompt caching models the same as prompt caching models", async () => { - // The implementation no longer differentiates between prompt caching and non-prompt caching models - const modelInfo1 = createModelInfo(100000, 30000) - const modelInfo2 = createModelInfo(100000, 30000) - - // Create messages with very small content in the last one to avoid token overflow - const messagesWithSmallContent = [...messages.slice(0, -1), { ...messages[messages.length - 1], content: "" }] - - // Test below threshold - const belowThreshold = 69999 - const result1 = await truncateConversationIfNeeded({ - messages: messagesWithSmallContent, - totalTokens: belowThreshold, - contextWindow: modelInfo1.contextWindow, - maxTokens: modelInfo1.maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: false, - autoCondenseContextPercent: 100, - systemPrompt: "System prompt", - taskId, - }) - - const result2 = await truncateConversationIfNeeded({ - messages: messagesWithSmallContent, - totalTokens: belowThreshold, - contextWindow: modelInfo2.contextWindow, - maxTokens: modelInfo2.maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: false, - autoCondenseContextPercent: 100, - systemPrompt: "System prompt", - taskId, - }) - - expect(result1.messages).toEqual(result2.messages) - expect(result1.summary).toEqual(result2.summary) - expect(result1.cost).toEqual(result2.cost) - expect(result1.prevContextTokens).toEqual(result2.prevContextTokens) - - // Test above threshold - const aboveThreshold = 70001 - const result3 = await truncateConversationIfNeeded({ - messages: messagesWithSmallContent, - totalTokens: aboveThreshold, - contextWindow: modelInfo1.contextWindow, - maxTokens: modelInfo1.maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: false, - autoCondenseContextPercent: 100, - systemPrompt: "System prompt", - taskId, - }) - - const result4 = await truncateConversationIfNeeded({ - messages: messagesWithSmallContent, - totalTokens: aboveThreshold, - contextWindow: modelInfo2.contextWindow, - maxTokens: modelInfo2.maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: false, - autoCondenseContextPercent: 100, - systemPrompt: "System prompt", - taskId, - }) - - expect(result3.messages).toEqual(result4.messages) - expect(result3.summary).toEqual(result4.summary) - expect(result3.cost).toEqual(result4.cost) - expect(result3.prevContextTokens).toEqual(result4.prevContextTokens) - }) - - it("should consider incoming content when deciding to truncate", async () => { - const modelInfo = createModelInfo(100000, 30000) - const maxTokens = 30000 - const availableTokens = modelInfo.contextWindow - maxTokens - - // Test case 1: Small content that won't push us over the threshold - const smallContent = [{ type: "text" as const, text: "Small content" }] - const smallContentTokens = await estimateTokenCount(smallContent, mockApiHandler) - const messagesWithSmallContent: ApiMessage[] = [ - ...messages.slice(0, -1), - { role: messages[messages.length - 1].role, content: smallContent }, - ] - - // Set base tokens so total is well below threshold + buffer even with small content added - const dynamicBuffer = modelInfo.contextWindow * TOKEN_BUFFER_PERCENTAGE - const baseTokensForSmall = availableTokens - smallContentTokens - dynamicBuffer - 10 - const resultWithSmall = await truncateConversationIfNeeded({ - messages: messagesWithSmallContent, - totalTokens: baseTokensForSmall, - contextWindow: modelInfo.contextWindow, - maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: false, - autoCondenseContextPercent: 100, - systemPrompt: "System prompt", - taskId, - }) - expect(resultWithSmall).toEqual({ - messages: messagesWithSmallContent, - summary: "", - cost: 0, - prevContextTokens: baseTokensForSmall + smallContentTokens, - }) // No truncation - - // Test case 2: Large content that will push us over the threshold - const largeContent = [ - { - type: "text" as const, - text: "A very large incoming message that would consume a significant number of tokens and push us over the threshold", - }, - ] - const largeContentTokens = await estimateTokenCount(largeContent, mockApiHandler) - const messagesWithLargeContent: ApiMessage[] = [ - ...messages.slice(0, -1), - { role: messages[messages.length - 1].role, content: largeContent }, - ] - - // Set base tokens so we're just below threshold without content, but over with content - const baseTokensForLarge = availableTokens - Math.floor(largeContentTokens / 2) - const resultWithLarge = await truncateConversationIfNeeded({ - messages: messagesWithLargeContent, - totalTokens: baseTokensForLarge, - contextWindow: modelInfo.contextWindow, - maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: false, - autoCondenseContextPercent: 100, - systemPrompt: "System prompt", - taskId, - }) - expect(resultWithLarge.messages).not.toEqual(messagesWithLargeContent) // Should truncate - expect(resultWithLarge.summary).toBe("") - expect(resultWithLarge.cost).toBe(0) - expect(resultWithLarge.prevContextTokens).toBe(baseTokensForLarge + largeContentTokens) - - // Test case 3: Very large content that will definitely exceed threshold - const veryLargeContent = [{ type: "text" as const, text: "X".repeat(1000) }] - const veryLargeContentTokens = await estimateTokenCount(veryLargeContent, mockApiHandler) - const messagesWithVeryLargeContent: ApiMessage[] = [ - ...messages.slice(0, -1), - { role: messages[messages.length - 1].role, content: veryLargeContent }, - ] - - // Set base tokens so we're just below threshold without content - const baseTokensForVeryLarge = availableTokens - Math.floor(veryLargeContentTokens / 2) - const resultWithVeryLarge = await truncateConversationIfNeeded({ - messages: messagesWithVeryLargeContent, - totalTokens: baseTokensForVeryLarge, - contextWindow: modelInfo.contextWindow, - maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: false, - autoCondenseContextPercent: 100, - systemPrompt: "System prompt", - taskId, - }) - expect(resultWithVeryLarge.messages).not.toEqual(messagesWithVeryLargeContent) // Should truncate - expect(resultWithVeryLarge.summary).toBe("") - expect(resultWithVeryLarge.cost).toBe(0) - expect(resultWithVeryLarge.prevContextTokens).toBe(baseTokensForVeryLarge + veryLargeContentTokens) - }) - - it("should truncate if tokens are within TOKEN_BUFFER_PERCENTAGE of the threshold", async () => { - const modelInfo = createModelInfo(100000, 30000) - const dynamicBuffer = modelInfo.contextWindow * TOKEN_BUFFER_PERCENTAGE // 10% of 100000 = 10000 - const totalTokens = 70000 - dynamicBuffer + 1 // Just within the dynamic buffer of threshold (70000) - - // Create messages with very small content in the last one to avoid token overflow - const messagesWithSmallContent = [...messages.slice(0, -1), { ...messages[messages.length - 1], content: "" }] - - // When truncating, always uses 0.5 fraction - // With 4 messages after the first, 0.5 fraction means remove 2 messages - const expectedResult = [messagesWithSmallContent[0], messagesWithSmallContent[3], messagesWithSmallContent[4]] - - const result = await truncateConversationIfNeeded({ - messages: messagesWithSmallContent, - totalTokens, - contextWindow: modelInfo.contextWindow, - maxTokens: modelInfo.maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: false, - autoCondenseContextPercent: 100, - systemPrompt: "System prompt", - taskId, - }) - expect(result).toEqual({ - messages: expectedResult, - summary: "", - cost: 0, - prevContextTokens: totalTokens, - }) - }) - - it("should use summarizeConversation when autoCondenseContext is true and tokens exceed threshold", async () => { - // Mock the summarizeConversation function - const mockSummary = "This is a summary of the conversation" - const mockCost = 0.05 - const mockSummarizeResponse: condenseModule.SummarizeResponse = { - messages: [ - { role: "user", content: "First message" }, - { role: "assistant", content: mockSummary, isSummary: true }, - { role: "user", content: "Last message" }, - ], - summary: mockSummary, - cost: mockCost, - newContextTokens: 100, - } - - const summarizeSpy = jest - .spyOn(condenseModule, "summarizeConversation") - .mockResolvedValue(mockSummarizeResponse) - - const modelInfo = createModelInfo(100000, 30000) - const totalTokens = 70001 // Above threshold - const messagesWithSmallContent = [...messages.slice(0, -1), { ...messages[messages.length - 1], content: "" }] - - const result = await truncateConversationIfNeeded({ - messages: messagesWithSmallContent, - totalTokens, - contextWindow: modelInfo.contextWindow, - maxTokens: modelInfo.maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: true, - autoCondenseContextPercent: 100, - systemPrompt: "System prompt", - taskId, - }) - - // Verify summarizeConversation was called with the right parameters - expect(summarizeSpy).toHaveBeenCalledWith( - messagesWithSmallContent, - mockApiHandler, - "System prompt", - taskId, - 70001, - true, - undefined, // customCondensingPrompt - undefined, // condensingApiHandler - ) - - // Verify the result contains the summary information - expect(result).toMatchObject({ - messages: mockSummarizeResponse.messages, - summary: mockSummary, - cost: mockCost, - prevContextTokens: totalTokens, - }) - // newContextTokens might be present, but we don't need to verify its exact value - - // Clean up - summarizeSpy.mockRestore() - }) - - it("should fall back to truncateConversation when autoCondenseContext is true but summarization fails", async () => { - // Mock the summarizeConversation function to return an error - const mockSummarizeResponse: condenseModule.SummarizeResponse = { - messages: messages, // Original messages unchanged - summary: "", // Empty summary - cost: 0.01, - error: "Summarization failed", // Error indicates failure - } - - const summarizeSpy = jest - .spyOn(condenseModule, "summarizeConversation") - .mockResolvedValue(mockSummarizeResponse) - - const modelInfo = createModelInfo(100000, 30000) - const totalTokens = 70001 // Above threshold - const messagesWithSmallContent = [...messages.slice(0, -1), { ...messages[messages.length - 1], content: "" }] - - // When truncating, always uses 0.5 fraction - // With 4 messages after the first, 0.5 fraction means remove 2 messages - const expectedMessages = [messagesWithSmallContent[0], messagesWithSmallContent[3], messagesWithSmallContent[4]] - - const result = await truncateConversationIfNeeded({ - messages: messagesWithSmallContent, - totalTokens, - contextWindow: modelInfo.contextWindow, - maxTokens: modelInfo.maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: true, - autoCondenseContextPercent: 100, - systemPrompt: "System prompt", - taskId, - }) - - // Verify summarizeConversation was called - expect(summarizeSpy).toHaveBeenCalled() - - // Verify it fell back to truncation - expect(result.messages).toEqual(expectedMessages) - expect(result.summary).toBe("") - expect(result.prevContextTokens).toBe(totalTokens) - // The cost might be different than expected, so we don't check it - - // Clean up - summarizeSpy.mockRestore() - }) - - it("should not call summarizeConversation when autoCondenseContext is false", async () => { - // Reset any previous mock calls - jest.clearAllMocks() - const summarizeSpy = jest.spyOn(condenseModule, "summarizeConversation") - - const modelInfo = createModelInfo(100000, 30000) - const totalTokens = 70001 // Above threshold - const messagesWithSmallContent = [...messages.slice(0, -1), { ...messages[messages.length - 1], content: "" }] - - // When truncating, always uses 0.5 fraction - // With 4 messages after the first, 0.5 fraction means remove 2 messages - const expectedMessages = [messagesWithSmallContent[0], messagesWithSmallContent[3], messagesWithSmallContent[4]] - - const result = await truncateConversationIfNeeded({ - messages: messagesWithSmallContent, - totalTokens, - contextWindow: modelInfo.contextWindow, - maxTokens: modelInfo.maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: false, - autoCondenseContextPercent: 50, // This shouldn't matter since autoCondenseContext is false - systemPrompt: "System prompt", - taskId, - }) - - // Verify summarizeConversation was not called - expect(summarizeSpy).not.toHaveBeenCalled() - - // Verify it used truncation - expect(result).toEqual({ - messages: expectedMessages, - summary: "", - cost: 0, - prevContextTokens: totalTokens, - }) - - // Clean up - summarizeSpy.mockRestore() - }) - - it("should use summarizeConversation when autoCondenseContext is true and context percent exceeds threshold", async () => { - // Mock the summarizeConversation function - const mockSummary = "This is a summary of the conversation" - const mockCost = 0.05 - const mockSummarizeResponse: condenseModule.SummarizeResponse = { - messages: [ - { role: "user", content: "First message" }, - { role: "assistant", content: mockSummary, isSummary: true }, - { role: "user", content: "Last message" }, - ], - summary: mockSummary, - cost: mockCost, - newContextTokens: 100, - } - - const summarizeSpy = jest - .spyOn(condenseModule, "summarizeConversation") - .mockResolvedValue(mockSummarizeResponse) - - const modelInfo = createModelInfo(100000, 30000) - // Set tokens to be below the allowedTokens threshold but above the percentage threshold - const contextWindow = modelInfo.contextWindow - const totalTokens = 60000 // Below allowedTokens but 60% of context window - const messagesWithSmallContent = [...messages.slice(0, -1), { ...messages[messages.length - 1], content: "" }] - - const result = await truncateConversationIfNeeded({ - messages: messagesWithSmallContent, - totalTokens, - contextWindow, - maxTokens: modelInfo.maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: true, - autoCondenseContextPercent: 50, // Set threshold to 50% - our tokens are at 60% - systemPrompt: "System prompt", - taskId, - }) - - // Verify summarizeConversation was called with the right parameters - expect(summarizeSpy).toHaveBeenCalledWith( - messagesWithSmallContent, - mockApiHandler, - "System prompt", - taskId, - 60000, - true, - undefined, // customCondensingPrompt - undefined, // condensingApiHandler - ) - - // Verify the result contains the summary information - expect(result).toMatchObject({ - messages: mockSummarizeResponse.messages, - summary: mockSummary, - cost: mockCost, - prevContextTokens: totalTokens, - }) - - // Clean up - summarizeSpy.mockRestore() - }) - - it("should not use summarizeConversation when autoCondenseContext is true but context percent is below threshold", async () => { - // Reset any previous mock calls - jest.clearAllMocks() - const summarizeSpy = jest.spyOn(condenseModule, "summarizeConversation") - - const modelInfo = createModelInfo(100000, 30000) - // Set tokens to be below both the allowedTokens threshold and the percentage threshold - const contextWindow = modelInfo.contextWindow - const totalTokens = 40000 // 40% of context window - const messagesWithSmallContent = [...messages.slice(0, -1), { ...messages[messages.length - 1], content: "" }] - - const result = await truncateConversationIfNeeded({ - messages: messagesWithSmallContent, - totalTokens, - contextWindow, - maxTokens: modelInfo.maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: true, - autoCondenseContextPercent: 50, // Set threshold to 50% - our tokens are at 40% - systemPrompt: "System prompt", - taskId, - }) - - // Verify summarizeConversation was not called - expect(summarizeSpy).not.toHaveBeenCalled() - - // Verify no truncation or summarization occurred - expect(result).toEqual({ - messages: messagesWithSmallContent, - summary: "", - cost: 0, - prevContextTokens: totalTokens, - }) - - // Clean up - summarizeSpy.mockRestore() - }) -}) - -/** - * Tests for the getMaxTokens function (private but tested through truncateConversationIfNeeded) - */ -describe("getMaxTokens", () => { - // We'll test this indirectly through truncateConversationIfNeeded - const createModelInfo = (contextWindow: number, maxTokens?: number): ModelInfo => ({ - contextWindow, - supportsPromptCache: true, // Not relevant for getMaxTokens - maxTokens, - }) - - // Reuse across tests for consistency - const messages: ApiMessage[] = [ - { role: "user", content: "First message" }, - { role: "assistant", content: "Second message" }, - { role: "user", content: "Third message" }, - { role: "assistant", content: "Fourth message" }, - { role: "user", content: "Fifth message" }, - ] - - it("should use maxTokens as buffer when specified", async () => { - const modelInfo = createModelInfo(100000, 50000) - // Max tokens = 100000 - 50000 = 50000 - - // Create messages with very small content in the last one to avoid token overflow - const messagesWithSmallContent = [...messages.slice(0, -1), { ...messages[messages.length - 1], content: "" }] - - // Account for the dynamic buffer which is 10% of context window (10,000 tokens) - // Below max tokens and buffer - no truncation - const result1 = await truncateConversationIfNeeded({ - messages: messagesWithSmallContent, - totalTokens: 39999, // Well below threshold + dynamic buffer - contextWindow: modelInfo.contextWindow, - maxTokens: modelInfo.maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: false, - autoCondenseContextPercent: 100, - systemPrompt: "System prompt", - taskId, - }) - expect(result1).toEqual({ - messages: messagesWithSmallContent, - summary: "", - cost: 0, - prevContextTokens: 39999, - }) - - // Above max tokens - truncate - const result2 = await truncateConversationIfNeeded({ - messages: messagesWithSmallContent, - totalTokens: 50001, // Above threshold - contextWindow: modelInfo.contextWindow, - maxTokens: modelInfo.maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: false, - autoCondenseContextPercent: 100, - systemPrompt: "System prompt", - taskId, - }) - expect(result2.messages).not.toEqual(messagesWithSmallContent) - expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction - expect(result2.summary).toBe("") - expect(result2.cost).toBe(0) - expect(result2.prevContextTokens).toBe(50001) - }) - - it("should use 20% of context window as buffer when maxTokens is undefined", async () => { - const modelInfo = createModelInfo(100000, undefined) - // Max tokens = 100000 - (100000 * 0.2) = 80000 - - // Create messages with very small content in the last one to avoid token overflow - const messagesWithSmallContent = [...messages.slice(0, -1), { ...messages[messages.length - 1], content: "" }] - - // Account for the dynamic buffer which is 10% of context window (10,000 tokens) - // Below max tokens and buffer - no truncation - const result1 = await truncateConversationIfNeeded({ - messages: messagesWithSmallContent, - totalTokens: 69999, // Well below threshold + dynamic buffer - contextWindow: modelInfo.contextWindow, - maxTokens: modelInfo.maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: false, - autoCondenseContextPercent: 100, - systemPrompt: "System prompt", - taskId, - }) - expect(result1).toEqual({ - messages: messagesWithSmallContent, - summary: "", - cost: 0, - prevContextTokens: 69999, - }) - - // Above max tokens - truncate - const result2 = await truncateConversationIfNeeded({ - messages: messagesWithSmallContent, - totalTokens: 80001, // Above threshold - contextWindow: modelInfo.contextWindow, - maxTokens: modelInfo.maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: false, - autoCondenseContextPercent: 100, - systemPrompt: "System prompt", - taskId, - }) - expect(result2.messages).not.toEqual(messagesWithSmallContent) - expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction - expect(result2.summary).toBe("") - expect(result2.cost).toBe(0) - expect(result2.prevContextTokens).toBe(80001) - }) - - it("should handle small context windows appropriately", async () => { - const modelInfo = createModelInfo(50000, 10000) - // Max tokens = 50000 - 10000 = 40000 - - // Create messages with very small content in the last one to avoid token overflow - const messagesWithSmallContent = [...messages.slice(0, -1), { ...messages[messages.length - 1], content: "" }] - - // Below max tokens and buffer - no truncation - const result1 = await truncateConversationIfNeeded({ - messages: messagesWithSmallContent, - totalTokens: 34999, // Well below threshold + buffer - contextWindow: modelInfo.contextWindow, - maxTokens: modelInfo.maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: false, - autoCondenseContextPercent: 100, - systemPrompt: "System prompt", - taskId, - }) - expect(result1.messages).toEqual(messagesWithSmallContent) - - // Above max tokens - truncate - const result2 = await truncateConversationIfNeeded({ - messages: messagesWithSmallContent, - totalTokens: 40001, // Above threshold - contextWindow: modelInfo.contextWindow, - maxTokens: modelInfo.maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: false, - autoCondenseContextPercent: 100, - systemPrompt: "System prompt", - taskId, - }) - expect(result2).not.toEqual(messagesWithSmallContent) - expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction - }) - - it("should handle large context windows appropriately", async () => { - const modelInfo = createModelInfo(200000, 30000) - // Max tokens = 200000 - 30000 = 170000 - - // Create messages with very small content in the last one to avoid token overflow - const messagesWithSmallContent = [...messages.slice(0, -1), { ...messages[messages.length - 1], content: "" }] - - // Account for the dynamic buffer which is 10% of context window (20,000 tokens for this test) - // Below max tokens and buffer - no truncation - const result1 = await truncateConversationIfNeeded({ - messages: messagesWithSmallContent, - totalTokens: 149999, // Well below threshold + dynamic buffer - contextWindow: modelInfo.contextWindow, - maxTokens: modelInfo.maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: false, - autoCondenseContextPercent: 100, - systemPrompt: "System prompt", - taskId, - }) - expect(result1.messages).toEqual(messagesWithSmallContent) - - // Above max tokens - truncate - const result2 = await truncateConversationIfNeeded({ - messages: messagesWithSmallContent, - totalTokens: 170001, // Above threshold - contextWindow: modelInfo.contextWindow, - maxTokens: modelInfo.maxTokens, - apiHandler: mockApiHandler, - autoCondenseContext: false, - autoCondenseContextPercent: 100, - systemPrompt: "System prompt", - taskId, - }) - expect(result2).not.toEqual(messagesWithSmallContent) - expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction + // Above max tokens - truncate + const result2 = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens: 80001, // Above threshold + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: false, + autoCondenseContextPercent: 100, + systemPrompt: "System prompt", + taskId, + }) + expect(result2.messages).not.toEqual(messagesWithSmallContent) + expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction + expect(result2.summary).toBe("") + expect(result2.cost).toBe(0) + expect(result2.prevContextTokens).toBe(80001) + }) + + it("should handle small context windows appropriately", async () => { + const modelInfo = createModelInfo(50000, 10000) + // Max tokens = 50000 - 10000 = 40000 + + // Create messages with very small content in the last one to avoid token overflow + const messagesWithSmallContent = [ + ...messages.slice(0, -1), + { ...messages[messages.length - 1], content: "" }, + ] + + // Below max tokens and buffer - no truncation + const result1 = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens: 34999, // Well below threshold + buffer + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: false, + autoCondenseContextPercent: 100, + systemPrompt: "System prompt", + taskId, + }) + expect(result1.messages).toEqual(messagesWithSmallContent) + + // Above max tokens - truncate + const result2 = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens: 40001, // Above threshold + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: false, + autoCondenseContextPercent: 100, + systemPrompt: "System prompt", + taskId, + }) + expect(result2).not.toEqual(messagesWithSmallContent) + expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction + }) + + it("should handle large context windows appropriately", async () => { + const modelInfo = createModelInfo(200000, 30000) + // Max tokens = 200000 - 30000 = 170000 + + // Create messages with very small content in the last one to avoid token overflow + const messagesWithSmallContent = [ + ...messages.slice(0, -1), + { ...messages[messages.length - 1], content: "" }, + ] + + // Account for the dynamic buffer which is 10% of context window (20,000 tokens for this test) + // Below max tokens and buffer - no truncation + const result1 = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens: 149999, // Well below threshold + dynamic buffer + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: false, + autoCondenseContextPercent: 100, + systemPrompt: "System prompt", + taskId, + }) + expect(result1.messages).toEqual(messagesWithSmallContent) + + // Above max tokens - truncate + const result2 = await truncateConversationIfNeeded({ + messages: messagesWithSmallContent, + totalTokens: 170001, // Above threshold + contextWindow: modelInfo.contextWindow, + maxTokens: modelInfo.maxTokens, + apiHandler: mockApiHandler, + autoCondenseContext: false, + autoCondenseContextPercent: 100, + systemPrompt: "System prompt", + taskId, + }) + expect(result2).not.toEqual(messagesWithSmallContent) + expect(result2.messages.length).toBe(3) // Truncated with 0.5 fraction + }) }) }) diff --git a/src/core/sliding-window/index.ts b/src/core/sliding-window/index.ts index 6a0b0f1b27..dc9eaf718d 100644 --- a/src/core/sliding-window/index.ts +++ b/src/core/sliding-window/index.ts @@ -1,8 +1,10 @@ import { Anthropic } from "@anthropic-ai/sdk" + +import { TelemetryService } from "@roo-code/telemetry" + import { ApiHandler } from "../../api" import { summarizeConversation, SummarizeResponse } from "../condense" import { ApiMessage } from "../task-persistence/apiMessages" -import { telemetryService } from "../../services/telemetry/TelemetryService" /** * Default percentage of the context window to use as a buffer when deciding when to truncate @@ -36,7 +38,7 @@ export async function estimateTokenCount( * @returns {ApiMessage[]} The truncated conversation messages. */ export function truncateConversation(messages: ApiMessage[], fracToRemove: number, taskId: string): ApiMessage[] { - telemetryService.captureSlidingWindowTruncation(taskId) + TelemetryService.instance.captureSlidingWindowTruncation(taskId) const truncatedMessages = [messages[0]] const rawMessagesToRemove = Math.floor((messages.length - 1) * fracToRemove) const messagesToRemove = rawMessagesToRemove - (rawMessagesToRemove % 2) diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 32a34098bc..ac3b1cb7d8 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -8,18 +8,21 @@ import delay from "delay" import pWaitFor from "p-wait-for" import { serializeError } from "serialize-error" -import type { - ProviderSettings, - TokenUsage, - ToolUsage, - ToolName, - ContextCondense, - ClineAsk, - ClineMessage, - ClineSay, - ToolProgressStatus, - HistoryItem, +import { + type ProviderSettings, + type TokenUsage, + type ToolUsage, + type ToolName, + type ContextCondense, + type ClineAsk, + type ClineMessage, + type ClineSay, + type ToolProgressStatus, + type HistoryItem, + TelemetryEventName, } from "@roo-code/types" +import { TelemetryService } from "@roo-code/telemetry" +import { CloudService } from "@roo-code/cloud" // api import { ApiHandler, ApiHandlerCreateMessageMetadata, buildApiHandler } from "../../api" @@ -41,7 +44,6 @@ import { UrlContentFetcher } from "../../services/browser/UrlContentFetcher" import { BrowserSession } from "../../services/browser/BrowserSession" import { McpHub } from "../../services/mcp/McpHub" import { McpServerManager } from "../../services/mcp/McpServerManager" -import { telemetryService } from "../../services/telemetry/TelemetryService" import { RepoPerTaskCheckpointService } from "../../services/checkpoints" // integrations @@ -243,9 +245,9 @@ export class Task extends EventEmitter { this.taskNumber = taskNumber if (historyItem) { - telemetryService.captureTaskRestarted(this.taskId) + TelemetryService.instance.captureTaskRestarted(this.taskId) } else { - telemetryService.captureTaskCreated(this.taskId) + TelemetryService.instance.captureTaskCreated(this.taskId) } this.diffStrategy = new MultiSearchReplaceDiffStrategy(this.fuzzyMatchThreshold) @@ -321,6 +323,15 @@ export class Task extends EventEmitter { await this.providerRef.deref()?.postStateToWebview() this.emit("message", { action: "created", message }) await this.saveClineMessages() + + const shouldCaptureMessage = message.partial !== true && CloudService.isEnabled() + + if (shouldCaptureMessage) { + CloudService.instance.captureEvent({ + event: TelemetryEventName.TASK_MESSAGE, + properties: { taskId: this.taskId, message }, + }) + } } public async overwriteClineMessages(newMessages: ClineMessage[]) { @@ -331,6 +342,15 @@ export class Task extends EventEmitter { private async updateClineMessage(partialMessage: ClineMessage) { await this.providerRef.deref()?.postMessageToWebview({ type: "partialMessage", partialMessage }) this.emit("message", { action: "updated", message: partialMessage }) + + const shouldCaptureMessage = partialMessage.partial !== true && CloudService.isEnabled() + + if (shouldCaptureMessage) { + CloudService.instance.captureEvent({ + event: TelemetryEventName.TASK_MESSAGE, + properties: { taskId: this.taskId, message: partialMessage }, + }) + } } private async saveClineMessages() { @@ -1066,7 +1086,7 @@ export class Task extends EventEmitter { await this.say("user_feedback", text, images) // Track consecutive mistake errors in telemetry. - telemetryService.captureConsecutiveMistakeError(this.taskId) + TelemetryService.instance.captureConsecutiveMistakeError(this.taskId) } this.consecutiveMistakeCount = 0 @@ -1125,7 +1145,7 @@ export class Task extends EventEmitter { const finalUserContent = [...parsedUserContent, { type: "text" as const, text: environmentDetails }] await this.addToApiConversationHistory({ role: "user", content: finalUserContent }) - telemetryService.captureConversationMessage(this.taskId, "user") + TelemetryService.instance.captureConversationMessage(this.taskId, "user") // Since we sent off a placeholder api_req_started message to update the // webview while waiting to actually start the API request (to load @@ -1345,7 +1365,7 @@ export class Task extends EventEmitter { cacheReadTokens > 0 || typeof totalCost !== "undefined" ) { - telemetryService.captureLlmCompletion(this.taskId, { + TelemetryService.instance.captureLlmCompletion(this.taskId, { inputTokens, outputTokens, cacheWriteTokens, @@ -1399,7 +1419,7 @@ export class Task extends EventEmitter { content: [{ type: "text", text: assistantMessage }], }) - telemetryService.captureConversationMessage(this.taskId, "assistant") + TelemetryService.instance.captureConversationMessage(this.taskId, "assistant") // NOTE: This comment is here for future reference - this was a // workaround for `userMessageContent` not getting set to true. diff --git a/src/core/task/__tests__/Task.test.ts b/src/core/task/__tests__/Task.test.ts index 79641b56f1..8ed57ffcb3 100644 --- a/src/core/task/__tests__/Task.test.ts +++ b/src/core/task/__tests__/Task.test.ts @@ -1,4 +1,4 @@ -// npx jest src/core/task/__tests__/Task.test.ts +// npx jest core/task/__tests__/Task.test.ts import * as os from "os" import * as path from "path" @@ -7,6 +7,7 @@ import * as vscode from "vscode" import { Anthropic } from "@anthropic-ai/sdk" import type { GlobalState, ProviderSettings, ModelInfo } from "@roo-code/types" +import { TelemetryService } from "@roo-code/telemetry" import { Task } from "../Task" import { ClineProvider } from "../../webview/ClineProvider" @@ -126,10 +127,9 @@ jest.mock("../../environment/getEnvironmentDetails", () => ({ getEnvironmentDetails: jest.fn().mockResolvedValue(""), })) -// Mock RooIgnoreController jest.mock("../../ignore/RooIgnoreController") -// Mock storagePathManager to prevent dynamic import issues +// Mock storagePathManager to prevent dynamic import issues. jest.mock("../../../utils/storage", () => ({ getTaskDirectoryPath: jest .fn() @@ -139,14 +139,12 @@ jest.mock("../../../utils/storage", () => ({ .mockImplementation((globalStoragePath) => Promise.resolve(`${globalStoragePath}/settings`)), })) -// Mock fileExistsAtPath jest.mock("../../../utils/fs", () => ({ fileExistsAtPath: jest.fn().mockImplementation((filePath) => { return filePath.includes("ui_messages.json") || filePath.includes("api_conversation_history.json") }), })) -// Mock fs/promises const mockMessages = [ { ts: Date.now(), @@ -163,6 +161,10 @@ describe("Cline", () => { let mockExtensionContext: vscode.ExtensionContext beforeEach(() => { + if (!TelemetryService.hasInstance()) { + TelemetryService.createInstance([]) + } + // Setup mock extension context const storageUri = { fsPath: path.join(os.tmpdir(), "test-storage"), diff --git a/src/core/tools/applyDiffTool.ts b/src/core/tools/applyDiffTool.ts index 19d17c81c4..2c637bc219 100644 --- a/src/core/tools/applyDiffTool.ts +++ b/src/core/tools/applyDiffTool.ts @@ -1,6 +1,8 @@ import path from "path" import fs from "fs/promises" +import { TelemetryService } from "@roo-code/telemetry" + import { ClineSayTool } from "../../shared/ExtensionMessage" import { getReadablePath } from "../../utils/path" import { Task } from "../task/Task" @@ -9,7 +11,6 @@ import { formatResponse } from "../prompts/responses" import { fileExistsAtPath } from "../../utils/fs" import { addLineNumbers } from "../../integrations/misc/extract-text" import { RecordSource } from "../context-tracking/FileContextTrackerTypes" -import { telemetryService } from "../../services/telemetry/TelemetryService" import { unescapeHtmlEntities } from "../../utils/text-normalization" export async function applyDiffTool( @@ -103,7 +104,7 @@ export async function applyDiffTool( const currentCount = (cline.consecutiveMistakeCountForApplyDiff.get(relPath) || 0) + 1 cline.consecutiveMistakeCountForApplyDiff.set(relPath, currentCount) let formattedError = "" - telemetryService.captureDiffApplicationError(cline.taskId, currentCount) + TelemetryService.instance.captureDiffApplicationError(cline.taskId, currentCount) if (diffResult.failParts && diffResult.failParts.length > 0) { for (const failPart of diffResult.failParts) { diff --git a/src/core/tools/attemptCompletionTool.ts b/src/core/tools/attemptCompletionTool.ts index a5e469c77f..08859c98c9 100644 --- a/src/core/tools/attemptCompletionTool.ts +++ b/src/core/tools/attemptCompletionTool.ts @@ -1,5 +1,7 @@ import Anthropic from "@anthropic-ai/sdk" +import { TelemetryService } from "@roo-code/telemetry" + import { Task } from "../task/Task" import { ToolResponse, @@ -12,7 +14,6 @@ import { AskFinishSubTaskApproval, } from "../../shared/tools" import { formatResponse } from "../prompts/responses" -import { telemetryService } from "../../services/telemetry/TelemetryService" import { type ExecuteCommandOptions, executeCommand } from "./executeCommandTool" export async function attemptCompletionTool( @@ -45,7 +46,7 @@ export async function attemptCompletionTool( // we have command string, which means we have the result as well, so finish it (doesnt have to exist yet) await cline.say("completion_result", removeClosingTag("result", result), undefined, false) - telemetryService.captureTaskCompleted(cline.taskId) + TelemetryService.instance.captureTaskCompleted(cline.taskId) cline.emit("taskCompleted", cline.taskId, cline.getTokenUsage(), cline.toolUsage) await cline.ask("command", removeClosingTag("command", command), block.partial).catch(() => {}) @@ -71,7 +72,7 @@ export async function attemptCompletionTool( if (lastMessage && lastMessage.ask !== "command") { // Haven't sent a command message yet so first send completion_result then command. await cline.say("completion_result", result, undefined, false) - telemetryService.captureTaskCompleted(cline.taskId) + TelemetryService.instance.captureTaskCompleted(cline.taskId) cline.emit("taskCompleted", cline.taskId, cline.getTokenUsage(), cline.toolUsage) } @@ -96,7 +97,7 @@ export async function attemptCompletionTool( commandResult = execCommandResult } else { await cline.say("completion_result", result, undefined, false) - telemetryService.captureTaskCompleted(cline.taskId) + TelemetryService.instance.captureTaskCompleted(cline.taskId) cline.emit("taskCompleted", cline.taskId, cline.getTokenUsage(), cline.toolUsage) } diff --git a/src/core/tools/executeCommandTool.ts b/src/core/tools/executeCommandTool.ts index f20b283082..e38d3c74f6 100644 --- a/src/core/tools/executeCommandTool.ts +++ b/src/core/tools/executeCommandTool.ts @@ -4,13 +4,13 @@ import * as path from "path" import delay from "delay" import { CommandExecutionStatus } from "@roo-code/types" +import { TelemetryService } from "@roo-code/telemetry" import { Task } from "../task/Task" import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag, ToolResponse } from "../../shared/tools" import { formatResponse } from "../prompts/responses" import { unescapeHtmlEntities } from "../../utils/text-normalization" -import { telemetryService } from "../../services/telemetry/TelemetryService" import { ExitCodeDetails, RooTerminalCallbacks, RooTerminalProcess } from "../../integrations/terminal/types" import { TerminalRegistry } from "../../integrations/terminal/TerminalRegistry" import { Terminal } from "../../integrations/terminal/Terminal" @@ -192,7 +192,7 @@ export async function executeCommand( if (terminalProvider === "vscode") { callbacks.onNoShellIntegration = async (error: string) => { - telemetryService.captureShellIntegrationError(cline.taskId) + TelemetryService.instance.captureShellIntegrationError(cline.taskId) shellIntegrationError = error } } diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index a8f0473dd5..5f9f650049 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -9,19 +9,23 @@ import axios from "axios" import pWaitFor from "p-wait-for" import * as vscode from "vscode" -import type { - GlobalState, - ProviderName, - ProviderSettings, - RooCodeSettings, - ProviderSettingsEntry, - TelemetryProperties, - CodeActionId, - CodeActionName, - TerminalActionId, - TerminalActionPromptType, - HistoryItem, +import { + type GlobalState, + type ProviderName, + type ProviderSettings, + type RooCodeSettings, + type ProviderSettingsEntry, + type TelemetryProperties, + type TelemetryPropertiesProvider, + type CodeActionId, + type CodeActionName, + type TerminalActionId, + type TerminalActionPromptType, + type HistoryItem, + ORGANIZATION_ALLOW_ALL, } from "@roo-code/types" +import { TelemetryService } from "@roo-code/telemetry" +import { CloudService } from "@roo-code/cloud" import { t } from "../../i18n" import { setPanel } from "../../activate/registerCommands" @@ -53,11 +57,11 @@ import { Task, TaskOptions } from "../task/Task" import { getNonce } from "./getNonce" import { getUri } from "./getUri" import { getSystemPromptFilePath } from "../prompts/sections/custom-system-prompt" -import { TelemetryPropertiesProvider, telemetryService } from "../../services/telemetry" import { getWorkspacePath } from "../../utils/path" import { webviewMessageHandler } from "./webviewMessageHandler" import { WebviewMessage } from "../../shared/WebviewMessage" import { EMBEDDING_MODEL_PROFILES } from "../../shared/embeddingModels" +import { ProfileValidator } from "../../shared/ProfileValidator" /** * https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts @@ -68,6 +72,12 @@ export type ClineProviderEvents = { clineCreated: [cline: Task] } +class OrganizationAllowListViolationError extends Error { + constructor(message: string) { + super(message) + } +} + export class ClineProvider extends EventEmitter implements vscode.WebviewViewProvider, TelemetryPropertiesProvider @@ -114,7 +124,7 @@ export class ClineProvider // Register this provider with the telemetry service to enable it to add // properties like mode and provider. - telemetryService.setProvider(this) + TelemetryService.instance.setProvider(this) this._workspaceTracker = new WorkspaceTracker(this) @@ -288,7 +298,7 @@ export class ClineProvider params: Record, ): Promise { // Capture telemetry for code action usage - telemetryService.captureCodeActionUsed(promptType) + TelemetryService.instance.captureCodeActionUsed(promptType) const visibleProvider = await ClineProvider.getInstance() @@ -314,7 +324,7 @@ export class ClineProvider promptType: TerminalActionPromptType, params: Record, ): Promise { - telemetryService.captureCodeActionUsed(promptType) + TelemetryService.instance.captureCodeActionUsed(promptType) const visibleProvider = await ClineProvider.getInstance() @@ -330,7 +340,15 @@ export class ClineProvider return } - await visibleProvider.initClineWithTask(prompt) + try { + await visibleProvider.initClineWithTask(prompt) + } catch (error) { + if (error instanceof OrganizationAllowListViolationError) { + // Errors from terminal commands seem to get swallowed / ignored. + vscode.window.showErrorMessage(error.message) + } + throw error + } } async resolveWebviewView(webviewView: vscode.WebviewView | vscode.WebviewPanel) { @@ -494,12 +512,17 @@ export class ClineProvider ) { const { apiConfiguration, + organizationAllowList, diffEnabled: enableDiff, enableCheckpoints, fuzzyMatchThreshold, experiments, } = await this.getState() + if (!ProfileValidator.isProfileAllowed(apiConfiguration, organizationAllowList)) { + throw new OrganizationAllowListViolationError(t("common:errors.violated_organization_allowlist")) + } + const cline = new Task({ provider: this, apiConfiguration, @@ -628,7 +651,7 @@ export class ClineProvider "default-src 'none'", `font-src ${webview.cspSource}`, `style-src ${webview.cspSource} 'unsafe-inline' https://* http://${localServerUrl} http://0.0.0.0:${localPort}`, - `img-src ${webview.cspSource} data:`, + `img-src ${webview.cspSource} https://storage.googleapis.com https://img.clerk.com data:`, `media-src ${webview.cspSource}`, `script-src 'unsafe-eval' ${webview.cspSource} https://* https://*.posthog.com http://${localServerUrl} http://0.0.0.0:${localPort} 'nonce-${nonce}'`, `connect-src https://* https://*.posthog.com ws://${localServerUrl} ws://0.0.0.0:${localPort} http://${localServerUrl} http://0.0.0.0:${localPort}`, @@ -713,7 +736,7 @@ export class ClineProvider - +