Compare commits

...
Author SHA1 Message Date
Saoud Rizwan d58dc8f36a Create fair-glasses-lick.md 2025-07-14 20:53:45 -07:00
Saoud Rizwan 2a054ef6a5 Add groq to kimi providers 2025-07-14 20:50:02 -07:00
Saoud Rizwan 57c8b8120d Revert "pass linter errors with read_file (#4159)" (#4905)
This reverts commit 5e2b199377.
2025-07-14 20:18:19 -07:00
Saoud Rizwan 5243f0b9b1 Set default kimi provider to together (#4904) 2025-07-14 19:49:45 -07:00
celestial-vault c014060275 remove unused ExtensionStateContext setters (#4865) 2025-07-14 18:35:54 -07:00
celestial-vault d790ce86a0 Add markdown parsing to MCP responses (#4862)
* refactor out useEffect logic

* add markdown parsing to mcp response

* add display mode to global state; simplify state flow

* fix imports after merge conflicts
2025-07-14 18:17:14 -07:00
celestial-vault 5e2b199377 pass linter errors with read_file (#4159)
* pass linter errors with read_file

* changeset

* add code back after merge with main
2025-07-14 16:49:37 -07:00
celestial-vault 9234d0cdc4 [McpResponseDisplay] Refactor out useEffect logic to helper function (#4852)
* refactor out useEffect logic

* refactor: memoize renderSegment with useCallback
2025-07-14 15:29:50 -07:00
Sarah Fortune db1db8c95d Change the name of the cline core script from standalone.js to cline-core.js (#4894)
Update the name of the script so that you can tell from the process name what it is,
standalone.js is too vague to be able to associate it with cline.
2025-07-14 15:22:11 -07:00
Sarah Fortune f53af72643 Update the vscode usages script to separate out vscode.commands.executeCommand calls. (#4891)
vscode.commands.executeCommand runs other vscode commands, so we need the know which commands are being run.
2025-07-14 15:21:27 -07:00
Massimiliano Angelino 260e0d5f8e feat: adding Bedrock Api Keys support (#4728) 2025-07-15 00:19:58 +05:30
celestial-vault 5b68ee5523 Add kimi-k2 as trending model (#4889)
* add kimi k2 as trending model

* changeset

* adjust wording
2025-07-14 11:21:26 -07:00
Ara 3e5abd5e72 Removing reasoning UX for Grok 4 models and correct pricing (#4849)
* Removing reasoning UX for Grok 4 models and correct pricing

* More resiliency
2025-07-13 17:36:41 -07:00
Sarah Fortuneandellipsis-dev[bot] 1ba5873454 DiffViewProvider refactoring for the host bridge (#4877)
* DiffViewProvider refactoring.

Move the platform specific logic for updating the diff out of DiffViewProvider and into VscodeDiffViewProvider.replaceText().
Add a stub handler for replaceText in the ExternalDiffviewProvider.

* Add comment to replaceText()

* Update src/integrations/editor/DiffViewProvider.ts

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-07-13 16:28:09 -07:00
Sarah Fortune 1bdaf8ef6f Implement openDiffEditor in the ExternalDiffViewProvider (#4875)
Use the host bridge to open the diff view.
2025-07-13 14:43:33 -07:00
44 changed files with 1373 additions and 1265 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Add groq to kimi providers
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": minor
---
Add Kimi-K2 as the trending model in the Cline Provider
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": minor
---
Added API Key support for Bedrock integration
+1
View File
@@ -147,6 +147,7 @@
"pages": [
"provider-config/anthropic",
"provider-config/claude-code",
"provider-config/aws-bedrock-with-apikey-authentication",
"provider-config/aws-bedrock-with-credentials-authentication",
"provider-config/aws-bedrock-with-profile-authentication",
"provider-config/gcp-vertex-ai",
@@ -14,6 +14,8 @@ Certain scenarios may warrant using local models, including handling highly sens
#### [IAM Security Best Practices](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html) (For administrators)
#### [AWS Bedrock setup for API Keys](/provider-config/aws-bedrock-with-apikey-authentication)
#### [AWS Bedrock setup for Legacy IAM (AWS Credentials)](/provider-config/aws-bedrock-with-credentials-authentication)
#### [AWS Bedrock setup for SSO token (AWS Profile)](/provider-config/aws-bedrock-with-profile-authentication)
@@ -0,0 +1,135 @@
---
title: "AWS Bedrock"
description: "Learn how to set up AWS Bedrock with Cline using credentials authentication. This guide covers AWS environment setup, regional access verification, and secure integration with the Cline VS Code extension."
---
### Overview
- **AWS Bedrock:** A fully managed service that offers access to leading generative AI models (e.g., Anthropic Claude, Amazon Nova) through AWS.\
[Learn more about AWS Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html).
- **Cline:** A VS Code extension that acts as a coding assistant by integrating with AI models—empowering developers to generate code, debug, and analyze data.
- **Developer Focus:** This guide is tailored for individual developers that want to enable access to frontier models via AWS Bedrock with a simplified setup using API Keys.
---
### Step 1: Prepare Your AWS Environment
#### 1.1 Individual user setup - Create a Bedrock API Key
For more detailed instructions check the [documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/api-keys.html).
1. **Sign in to the AWS Management Console:**\
[AWS Console](https://aws.amazon.com/console/)
2. **Access Bedrock Console:**
- [Bedrock Console](https://console.aws.amazon.com/bedrock)
- Create a new Long Lived API Key. This API Key will have by default the `AmazonBedrockLimitedAccess` IAM policy
[View AmazonBedrockLimitedAccess Policy Details](https://docs.aws.amazon.com/bedrock/latest/userguide/security-iam.html)
#### 1.2 Create or Modify the Policy
To ensure Cline can interact with AWS Bedrock, your IAM user or role needs specific permissions. While the `AmazonBedrockLimitedAccess` managed policy provides comprehensive access, for a more restricted and secure setup adhering to the principle of least privilege, the following minimal permissions are sufficient for Cline's core model invocation functionality:
- `bedrock:InvokeModel`
- `bedrock:InvokeModelWithResponseStream`
- `bedrock:CallWithBearerToken`
You can create a custom IAM policy with these permissions and attach it to your IAM user or role.
1. In the AWS IAM console, create a new policy.
2. Use the JSON editor to add the following policy document:
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream", "bedrock:CallWithBearerToken"],
"Resource": "*" // For enhanced security, scope this to specific model ARNs if possible.
}
]
}
```
3. Name the policy (e.g., `ClineBedrockInvokeAccess`) and attach it to the IAM user associated with the key you created. The IAM user and the API key have the same prefix.
**Important Considerations:**
- **Model Listing in Cline:** The minimal permissions (`bedrock:InvokeModel`, `bedrock:InvokeModelWithResponseStream`) are sufficient for Cline to _use_ a model if you specify the model ID directly in Cline's settings. If you rely on Cline to dynamically list available Bedrock models, you might need additional permissions like `bedrock:ListFoundationModels`.
- **AWS Marketplace Subscriptions:** For third-party models (e.g., Anthropic Claude), the **`AmazonBedrockLimitedAccess`** policy grants you the necessary permissions to subscribe via the AWS Marketplace. There is no explicit access to be enabled. For Anthropic models you are still required to submit a First Time Use (FTU) form via the Console. If you get the following message in the Cline chat `[ERROR] Failed to process response: Model use case details have not been submitted for this account. Fill out the Anthropic use case details form before using the model.` then open the [Playground in the AWS Bedrock Console](https://console.aws.amazon.com/bedrock/home?#/text-generation-playground), select any Anthropic model and fill in the form (you might need to send a prompt first)
---
### Step 2: Verify Regional and Model Access
#### 2.1 Choose and Confirm a Region
1. **Select a Region:**\
AWS Bedrock is available in multiple regions (e.g., US East, Europe, Asia Pacific). Choose the region that meets your latency and compliance needs.\
[AWS Global Infrastructure](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/)
2. **Verify Model Access:**
- **Note:** Some models are only accessible via an [Inference Profile](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html). In such case check the box "Cross Region Inference".
---
### Step 3: Configure the Cline VS Code Extension
#### 3.1 Install and Open Cline
1. **Install VS Code:**\
Download from the [VS Code website](https://code.visualstudio.com/).
2. **Install the Cline Extension:**
- Open VS Code.
- Go to the Extensions Marketplace (`Ctrl+Shift+X` or `Cmd+Shift+X`).
- Search for **Cline** and install it.
#### 3.2 Configure Cline Settings
1. **Open Cline Settings:**
- Click on the settings ⚙️ to select your API Provider.
2. **Select AWS Bedrock as the API Provider:**
- From the API Provider dropdown, choose **AWS Bedrock**.
3. **Enter Your AWS API Key:**
- Input your **API Key**
- Specify the correct **AWS Region** (e.g., `us-east-1` or your enterprise-approved region).
4. **Select a Model:**
- Choose an on-demand model (e.g., **anthropic.claude-3-5-sonnet-20241022-v2:0**).
5. **Save and Test:**
- Click **Done/Save** to apply your settings.
- Test the integration by sending a simple prompt (e.g., "Generate a Python function to check if a number is prime.").
---
### Step 4: Security, Monitoring, and Best Practices
1. **Secure Access:**
- Prefer AWS SSO/federated roles over long-lived API Key when possible.
- [AWS IAM Best Practices](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html)
2. **Enhance Network Security:**
- Consider setting up [AWS PrivateLink](https://docs.aws.amazon.com/vpc/latest/userguide/endpoint-services-overview.html) to securely connect to Bedrock.
3. **Monitor and Log Activity:**
- Enable AWS CloudTrail to log Bedrock API calls.
- Use CloudWatch to monitor metrics like invocation count, latency, and token usage.
- Set up alerts for abnormal activity.
4. **Handle Errors and Manage Costs:**
- Implement exponential backoff for throttling errors.
- Use AWS Cost Explorer and set billing alerts to track usage.\
[AWS Cost Management](https://docs.aws.amazon.com/cost-management/latest/userguide/what-is-aws-cost-management.html)
5. **Regular Audits and Compliance:**
- Periodically review IAM roles and CloudTrail logs.
- Follow internal data privacy and governance policies.
---
### Conclusion
By following these steps, your enterprise team can securely integrate AWS Bedrock with the Cline VS Code extension to accelerate development:
1. **Prepare Your AWS Environment:** Create or use a secure IAM role/user, attach the `AmazonBedrockLimitedAccess` policy, and ensure necessary permissions.
2. **Verify Region and Model Access:** Confirm that your selected region supports your required models.
3. **Configure Cline in VS Code:** Install and set up Cline with your AWS credentials and choose an appropriate model.
4. **Implement Security and Monitoring:** Use best practices for IAM, network security, monitoring, and cost management.
For further details, consult the [AWS Bedrock Documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html) and coordinate with your internal cloud team. Happy coding!
---
_This guide will be updated as AWS Bedrock and Cline evolve. Always refer to the latest documentation and internal policies for up-to-date practices._
@@ -5,7 +5,7 @@ description: "Learn how to set up AWS Bedrock with Cline using credentials authe
### Overview
- **AWS Bedrock:** A fully managed service that offers access to leading generative AI models (e.g., Anthropic Claude, Amazon Titan) through AWS.\
- **AWS Bedrock:** A fully managed service that offers access to leading generative AI models (e.g., Anthropic Claude, Amazon Nova) through AWS.\
[Learn more about AWS Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html).
- **Cline:** A VS Code extension that acts as a coding assistant by integrating with AI models—empowering developers to generate code, debug, and analyze data.
- **Enterprise Focus:** This guide is tailored for organizations with established AWS environments (using IAM roles, AWS SSO, AWS Organizations, etc.) to ensure secure and compliant usage.
@@ -25,7 +25,7 @@ description: "Learn how to set up AWS Bedrock with Cline using credentials authe
#### 1.2 Attach the Required Policies
To ensure Cline can interact with AWS Bedrock, your IAM user or role needs specific permissions. While the `AmazonBedrockFullAccess` managed policy provides comprehensive access, for a more restricted and secure setup adhering to the principle of least privilege, the following minimal permissions are sufficient for Cline's core model invocation functionality:
To ensure Cline can interact with AWS Bedrock, your IAM user or role needs specific permissions. While the `AmazonBedrockLimitedAccess` managed policy provides comprehensive access, for a more restricted and secure setup adhering to the principle of least privilege, the following minimal permissions are sufficient for Cline's core model invocation functionality:
- `bedrock:InvokeModel`
- `bedrock:InvokeModelWithResponseStream`
@@ -52,8 +52,8 @@ You can create a custom IAM policy with these permissions and attach it to your
**Option 2: Using a Managed Policy (Simpler Initial Setup)**
- Alternatively, you can attach the AWS managed policy **`AmazonBedrockFullAccess`**. This grants broader permissions, including the ability to list models, manage provisioning, and other Bedrock features. This might be simpler for initial setup or if you require these wider capabilities.
[View AmazonBedrockFullAccess Policy Details](https://docs.aws.amazon.com/bedrock/latest/userguide/security-iam.html)
- Alternatively, you can attach the AWS managed policy **`AmazonBedrockLimitedAccess`**. This grants broader permissions, including the ability to list models, manage provisioning, and other Bedrock features. This might be simpler for initial setup or if you require these wider capabilities.
[View AmazonBedrockLimitedAccess Policy Details](https://docs.aws.amazon.com/bedrock/latest/userguide/security-iam.html)
**Important Considerations:**
@@ -71,8 +71,8 @@ You can create a custom IAM policy with these permissions and attach it to your
AWS Bedrock is available in multiple regions (e.g., US East, Europe, Asia Pacific). Choose the region that meets your latency and compliance needs.\
[AWS Global Infrastructure](https://aws.amazon.com/about-aws/global-infrastructure/regions_az/)
2. **Verify Model Access:**
- In the AWS Bedrock console, confirm that the models your team requires (e.g., Anthropic Claude, Amazon Titan) are marked as "Access granted."
- **Note:** Some advanced models might require an [Inference Profile](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-prereq.html) if not available on-demand.
- In the AWS Bedrock console, confirm that the models your team requires (e.g., Anthropic Claude, Amazon Nova) are marked as "Access granted."
- **Note:** Some advanced models might require an [Inference Profile](https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html) if not available on-demand.
#### 2.2 Set Up AWS Marketplace Subscriptions (if needed)
@@ -138,7 +138,7 @@ You can create a custom IAM policy with these permissions and attach it to your
By following these steps, your enterprise team can securely integrate AWS Bedrock with the Cline VS Code extension to accelerate development:
1. **Prepare Your AWS Environment:** Create or use a secure IAM role/user, attach the `AmazonBedrockFullAccess` policy, and ensure necessary permissions.
1. **Prepare Your AWS Environment:** Create or use a secure IAM role/user, attach the `AmazonBedrockLimitedAccess` policy, and ensure necessary permissions.
2. **Verify Region and Model Access:** Confirm that your selected region supports your required models and subscribe via AWS Marketplace if needed.
3. **Configure Cline in VS Code:** Install and set up Cline with your AWS credentials and choose an appropriate model.
4. **Implement Security and Monitoring:** Use best practices for IAM, network security, monitoring, and cost management.
+2 -2
View File
@@ -153,8 +153,8 @@ const extensionConfig = {
// Standalone-specific configuration
const standaloneConfig = {
...baseConfig,
entryPoints: ["src/standalone/standalone.ts"],
outfile: `${destDir}/standalone.js`,
entryPoints: ["src/standalone/cline-core.ts"],
outfile: `${destDir}/cline-core.js`,
// These gRPC protos need to load files from the module directory at runtime,
// so they cannot be bundled.
external: ["vscode", "@grpc/reflection", "grpc-health-check"],
+567 -834
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -409,8 +409,8 @@
"dependencies": {
"@anthropic-ai/sdk": "^0.37.0",
"@anthropic-ai/vertex-sdk": "^0.6.4",
"@aws-sdk/client-bedrock-runtime": "^3.826.0",
"@aws-sdk/credential-providers": "^3.826.0",
"@aws-sdk/client-bedrock-runtime": "^3.840.0",
"@aws-sdk/credential-providers": "^3.840.0",
"@bufbuild/protobuf": "^2.2.5",
"@cerebras/cerebras_cloud_sdk": "^1.35.0",
"@google-cloud/vertexai": "^1.9.3",
+3 -1
View File
@@ -235,4 +235,6 @@ message ModelsApiConfiguration {
optional string sap_ai_core_token_url = 71;
optional string sap_ai_core_base_url = 72;
optional string claude_code_path = 73;
}
optional string aws_authentication = 74;
optional string aws_bedrock_api_key = 75;
}
+5 -1
View File
@@ -112,7 +112,7 @@ message UpdateSettingsRequest {
optional int64 shell_integration_timeout = 8;
optional bool terminal_reuse_enabled = 9;
optional bool mcp_responses_collapsed = 10;
optional bool mcp_rich_display_enabled = 11;
optional string mcp_display_mode = 11;
optional int64 terminal_output_line_limit = 12;
}
@@ -232,4 +232,8 @@ message ApiConfiguration {
// Claude Code specific
optional string claude_code_path = 77;
// Extension fields for Bedrock Api Keys
optional string aws_authentication = 78;
optional string aws_bedrock_api_key = 79;
}
+16 -2
View File
@@ -5,17 +5,31 @@ DIR=${1:-src/}
DEST_DIR=dist-standalone
SDK_DEST=$DEST_DIR/vscode-sdk-uses.txt
CSS_DEST=$DEST_DIR/vscode-css-uses.txt
TMP=/tmp/vscode-sdk-uses.txt.tmp
mkdir -p $DEST_DIR
{
git grep -h 'vscode\.' $DIR |
grep -Ev '//.*vscode' | # remove commented out code
grep -v vscode.commands.executeCommand | # executeCommand is handled separately
grep -Ev '"vscode' | # remove command strings that get included because they start with vscode
sed 's|.*vscode\.|vscode.|'| # remove everything before vscode.
sed 's/[^a-zA-Z0-9_.].*$//' | # remove everything after last identifier
grep -E '\.[a-z][^.]+$' | # remove types (last part of identifier should be lowercase)
sort | uniq -c | sort -n | # Count occurrences
cat > $SDK_DEST
cat > $TMP
}
{
grep -rh vscode.commands.executeCommand $DIR |
perl -ne 'print if /["\x27"]/' | # Remove occurrences where the command is not on the same line (line doesnt contain quote chars) :(
sed -n 's|.*\(vscode.commands.executeCommand[^,]*\).*|\1|p'| # Remove all params after the first one
sed 's|\(".*"\).*|\1)|'| # Close the parantheses
cat >> $TMP
}
# Count occurrences
cat $TMP | sort | uniq -c | sort -n > $SDK_DEST
rm $TMP
echo Wrote uses of the vscode SDK to $(realpath $SDK_DEST)
{
+2
View File
@@ -63,6 +63,8 @@ function createHandlerForProvider(apiProvider: string | undefined, options: Omit
awsSecretKey: options.awsSecretKey,
awsSessionToken: options.awsSessionToken,
awsRegion: options.awsRegion,
awsAuthentication: options.awsAuthentication,
awsBedrockApiKey: options.awsBedrockApiKey,
awsUseCrossRegionInference: options.awsUseCrossRegionInference,
awsBedrockUsePromptCache: options.awsBedrockUsePromptCache,
awsUseProfile: options.awsUseProfile,
@@ -182,6 +182,24 @@ describe("AwsBedrockHandler", () => {
process.env["AWS_PROFILE"]!.should.equal(preAWSProfile)
})
it("should work with AWS_BEARER_TOKEN_BEDROCK", async () => {
process.env["AWS_BEARER_TOKEN_BEDROCK"] = "test-key"
const preAWSProfile = process.env["AWS_BEARER_TOKEN_BEDROCK"]
await AwsBedrockHandler["withTempEnv"](
() => {
delete process.env["AWS_BEARER_TOKEN_BEDROCK"]
},
async () => {
should.not.exist(process.env["AWS_BEARER_TOKEN_BEDROCK"])
return "test"
},
)
process.env["AWS_BEARER_TOKEN_BEDROCK"]!.should.equal(preAWSProfile)
})
})
const mockOptions: ApiHandlerOptions = {
@@ -192,6 +210,7 @@ describe("AwsBedrockHandler", () => {
awsSessionToken: "",
awsUseProfile: false,
awsProfile: "",
awsBedrockApiKey: "",
awsBedrockUsePromptCache: false,
awsUseCrossRegionInference: false,
awsBedrockEndpoint: "",
+24 -8
View File
@@ -22,6 +22,8 @@ interface AwsBedrockHandlerOptions {
awsSecretKey?: string
awsSessionToken?: string
awsRegion?: string
awsAuthentication?: string
awsBedrockApiKey?: string
awsUseCrossRegionInference?: boolean
awsBedrockUsePromptCache?: boolean
awsUseProfile?: boolean
@@ -186,7 +188,10 @@ export class AwsBedrockHandler implements ApiHandler {
}> {
// Configure provider options
const providerOptions: ProviderChainOptions = {}
if (this.options.awsUseProfile) {
const useProfile =
(this.options.awsAuthentication === undefined && this.options.awsUseProfile) ||
this.options.awsAuthentication === "profile"
if (useProfile) {
// For profile-based auth, always use ignoreCache to detect credential file changes
// This solves the AWS Identity Manager issue where credential files change externally
providerOptions.ignoreCache = true
@@ -200,7 +205,7 @@ export class AwsBedrockHandler implements ApiHandler {
return await AwsBedrockHandler.withTempEnv(
() => {
AwsBedrockHandler.setEnv("AWS_REGION", this.options.awsRegion)
if (this.options.awsUseProfile) {
if (useProfile) {
AwsBedrockHandler.setEnv("AWS_PROFILE", this.options.awsProfile)
} else {
delete process.env["AWS_PROFILE"]
@@ -224,15 +229,26 @@ export class AwsBedrockHandler implements ApiHandler {
* Creates a BedrockRuntimeClient with the appropriate credentials
*/
private async getBedrockClient(): Promise<BedrockRuntimeClient> {
const credentials = await this.getAwsCredentials()
let auth: any
if (this.options.awsAuthentication === "apikey") {
auth = {
token: { token: this.options.awsBedrockApiKey },
authSchemePreference: ["httpBearerAuth"],
}
} else {
const credentials = await this.getAwsCredentials()
auth = {
credentials: {
accessKeyId: credentials.accessKeyId,
secretAccessKey: credentials.secretAccessKey,
sessionToken: credentials.sessionToken,
},
}
}
return new BedrockRuntimeClient({
region: this.getRegion(),
credentials: {
accessKeyId: credentials.accessKeyId,
secretAccessKey: credentials.secretAccessKey,
sessionToken: credentials.sessionToken,
},
...auth,
...(this.options.awsBedrockEndpoint && { endpoint: this.options.awsBedrockEndpoint }),
})
}
+3 -1
View File
@@ -10,6 +10,7 @@ import { withRetry } from "../retry"
import { AuthService } from "@/services/auth/AuthService"
import OpenAI from "openai"
import { version as extensionVersion } from "../../../package.json"
import { shouldSkipReasoningForModel } from "@utils/model-utils"
interface ClineHandlerOptions {
taskId?: string
@@ -127,7 +128,8 @@ export class ClineHandler implements ApiHandler {
}
// Reasoning tokens are returned separately from the content
if ("reasoning" in delta && delta.reasoning) {
// Skip reasoning content for Grok 4 models since it only displays "thinking" without providing useful information
if ("reasoning" in delta && delta.reasoning && !shouldSkipReasoningForModel(this.options.openRouterModelId)) {
yield {
type: "reasoning",
// @ts-ignore-next-line
+3 -1
View File
@@ -8,6 +8,7 @@ import { withRetry } from "../retry"
import { createOpenRouterStream } from "../transform/openrouter-stream"
import { ApiStream, ApiStreamUsageChunk } from "../transform/stream"
import { OpenRouterErrorResponse } from "./types"
import { shouldSkipReasoningForModel } from "@utils/model-utils"
interface OpenRouterHandlerOptions {
openRouterApiKey?: string
@@ -112,7 +113,8 @@ export class OpenRouterHandler implements ApiHandler {
}
// Reasoning tokens are returned separately from the content
if ("reasoning" in delta && delta.reasoning) {
// Skip reasoning content for Grok 4 models since it only displays "thinking" without providing useful information
if ("reasoning" in delta && delta.reasoning && !shouldSkipReasoningForModel(this.options.openRouterModelId)) {
yield {
type: "reasoning",
// @ts-ignore-next-line
+8 -4
View File
@@ -6,6 +6,7 @@ import { convertToOpenAiMessages } from "@api/transform/openai-format"
import { ApiStream } from "@api/transform/stream"
import { ChatCompletionReasoningEffort } from "openai/resources/chat/completions"
import { withRetry } from "../retry"
import { shouldSkipReasoningForModel } from "@utils/model-utils"
interface XAIHandlerOptions {
xaiApiKey?: string
@@ -70,10 +71,13 @@ export class XAIHandler implements ApiHandler {
}
if (delta && "reasoning_content" in delta && delta.reasoning_content) {
yield {
type: "reasoning",
// @ts-ignore-next-line
reasoning: delta.reasoning_content,
// Skip reasoning content for Grok 4 models since it only displays "thinking" without providing useful information
if (!shouldSkipReasoningForModel(modelId)) {
yield {
type: "reasoning",
// @ts-ignore-next-line
reasoning: delta.reasoning_content,
}
}
}
+6
View File
@@ -139,6 +139,10 @@ export async function createOpenRouterStream(
shouldApplyMiddleOutTransform = true
}
// hardcoded provider sorting for kimi-k2
const isKimiK2 = model.id.startsWith("moonshotai/kimi-k2")
openRouterProviderSorting = isKimiK2 ? undefined : openRouterProviderSorting
// @ts-ignore-next-line
const stream = await client.chat.completions.create({
model: model.id,
@@ -153,6 +157,8 @@ export async function createOpenRouterStream(
...(model.id.startsWith("openai/o") ? { reasoning_effort: reasoningEffort || "medium" } : {}),
...(reasoning ? { reasoning } : {}),
...(openRouterProviderSorting ? { provider: { sort: openRouterProviderSorting } } : {}),
// limit providers to only those that support the 131k context window
...(isKimiK2 ? { provider: { order: ["groq", "together"], allow_fallbacks: false } } : {}),
})
return stream
+2 -2
View File
@@ -834,7 +834,7 @@ export class Controller {
chatSettings: storedChatSettings,
userInfo,
mcpMarketplaceEnabled,
mcpRichDisplayEnabled,
mcpDisplayMode,
telemetrySetting,
planActSeparateModelsSetting,
enableCheckpointsSetting,
@@ -887,7 +887,7 @@ export class Controller {
chatSettings,
userInfo,
mcpMarketplaceEnabled,
mcpRichDisplayEnabled,
mcpDisplayMode,
telemetrySetting,
planActSeparateModelsSetting,
enableCheckpointsSetting: enableCheckpointsSetting ?? true,
@@ -104,6 +104,12 @@ export async function refreshOpenRouterModels(
modelInfo.cacheWritesPrice = 0.75
modelInfo.cacheReadsPrice = 0
break
case "moonshotai/kimi-k2":
// forcing kimi-k2 to use the together provider for full context and best throughput
modelInfo.inputPrice = 1
modelInfo.outputPrice = 3
modelInfo.contextWindow = 131_000
break
default:
if (rawModel.id.startsWith("openai/")) {
modelInfo.cacheReadsPrice = parsePrice(rawModel.pricing?.input_cache_read)
+3 -3
View File
@@ -50,9 +50,9 @@ export async function updateSettings(controller: Controller, request: UpdateSett
await controller.context.globalState.update("mcpResponsesCollapsed", request.mcpResponsesCollapsed)
}
// Update MCP responses collapsed setting
if (request.mcpRichDisplayEnabled !== undefined) {
await controller.context.globalState.update("mcpRichDisplayEnabled", request.mcpRichDisplayEnabled)
// Update MCP display mode setting
if (request.mcpDisplayMode !== undefined) {
await controller.context.globalState.update("mcpDisplayMode", request.mcpDisplayMode)
}
// Update chat settings
+4 -1
View File
@@ -5,6 +5,7 @@ export type SecretKey =
| "awsAccessKey"
| "awsSecretKey"
| "awsSessionToken"
| "awsBedrockApiKey"
| "openAiApiKey"
| "geminiApiKey"
| "openAiNativeApiKey"
@@ -31,6 +32,8 @@ export type GlobalStateKey =
| "awsBedrockUsePromptCache"
| "awsBedrockEndpoint"
| "awsProfile"
| "awsBedrockApiKey"
| "awsAuthentication"
| "awsUseProfile"
| "vertexProjectId"
| "vertexRegion"
@@ -73,7 +76,7 @@ export type GlobalStateKey =
| "isNewUser"
| "welcomeViewCompleted"
| "terminalOutputLineLimit"
| "mcpRichDisplayEnabled"
| "mcpDisplayMode"
| "sapAiCoreTokenUrl"
| "sapAiCoreBaseUrl"
| "sapAiResourceGroup"
+15 -3
View File
@@ -11,6 +11,7 @@ import { StoredChatSettings } from "@shared/ChatSettings"
import { TelemetrySetting } from "@shared/TelemetrySetting"
import { UserInfo } from "@shared/UserInfo"
import { ClineRulesToggles } from "@shared/cline-rules"
import { DEFAULT_MCP_DISPLAY_MODE, McpDisplayMode } from "@shared/McpDisplayMode"
import { migrateEnableCheckpointsSetting, migrateMcpMarketplaceEnableSetting } from "./state-migrations"
/*
Storage
@@ -124,7 +125,9 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
awsBedrockUsePromptCache,
awsBedrockEndpoint,
awsProfile,
awsBedrockApiKey,
awsUseProfile,
awsAuthentication,
vertexProjectId,
vertexRegion,
openAiBaseUrl,
@@ -171,7 +174,7 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
shellIntegrationTimeout,
enableCheckpointsSettingRaw,
mcpMarketplaceEnabledRaw,
mcpRichDisplayEnabled,
mcpDisplayMode,
mcpResponsesCollapsedRaw,
globalWorkflowToggles,
terminalReuseEnabled,
@@ -197,7 +200,9 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
getGlobalState(context, "awsBedrockUsePromptCache") as Promise<boolean | undefined>,
getGlobalState(context, "awsBedrockEndpoint") as Promise<string | undefined>,
getGlobalState(context, "awsProfile") as Promise<string | undefined>,
getSecret(context, "awsBedrockApiKey") as Promise<string | undefined>,
getGlobalState(context, "awsUseProfile") as Promise<boolean | undefined>,
getGlobalState(context, "awsAuthentication") as Promise<string | undefined>,
getGlobalState(context, "vertexProjectId") as Promise<string | undefined>,
getGlobalState(context, "vertexRegion") as Promise<string | undefined>,
getGlobalState(context, "openAiBaseUrl") as Promise<string | undefined>,
@@ -244,7 +249,7 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
getGlobalState(context, "shellIntegrationTimeout") as Promise<number | undefined>,
getGlobalState(context, "enableCheckpointsSetting") as Promise<boolean | undefined>,
getGlobalState(context, "mcpMarketplaceEnabled") as Promise<boolean | undefined>,
getGlobalState(context, "mcpRichDisplayEnabled") as Promise<boolean | undefined>,
getGlobalState(context, "mcpDisplayMode") as Promise<McpDisplayMode | undefined>,
getGlobalState(context, "mcpResponsesCollapsed") as Promise<boolean | undefined>,
getGlobalState(context, "globalWorkflowToggles") as Promise<ClineRulesToggles | undefined>,
getGlobalState(context, "terminalReuseEnabled") as Promise<boolean | undefined>,
@@ -380,7 +385,9 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
awsBedrockUsePromptCache,
awsBedrockEndpoint,
awsProfile,
awsBedrockApiKey,
awsUseProfile,
awsAuthentication,
awsBedrockCustomSelected,
awsBedrockCustomModelBaseId,
vertexProjectId,
@@ -464,7 +471,7 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
previousModeAwsBedrockCustomModelBaseId,
previousModeSapAiCoreModelId,
mcpMarketplaceEnabled: mcpMarketplaceEnabled,
mcpRichDisplayEnabled: mcpRichDisplayEnabled ?? true,
mcpDisplayMode: mcpDisplayMode ?? DEFAULT_MCP_DISPLAY_MODE,
mcpResponsesCollapsed: mcpResponsesCollapsed,
telemetrySetting: telemetrySetting || "unset",
planActSeparateModelsSetting,
@@ -490,8 +497,10 @@ export async function updateApiConfiguration(context: vscode.ExtensionContext, a
awsUseCrossRegionInference,
awsBedrockUsePromptCache,
awsBedrockEndpoint,
awsBedrockApiKey,
awsProfile,
awsUseProfile,
awsAuthentication,
awsBedrockCustomSelected,
awsBedrockCustomModelBaseId,
vertexProjectId,
@@ -584,6 +593,7 @@ export async function updateApiConfiguration(context: vscode.ExtensionContext, a
awsBedrockEndpoint,
awsProfile,
awsUseProfile,
awsAuthentication,
vertexProjectId,
vertexRegion,
openAiBaseUrl,
@@ -617,6 +627,7 @@ export async function updateApiConfiguration(context: vscode.ExtensionContext, a
awsAccessKey,
awsSecretKey,
awsSessionToken,
awsBedrockApiKey,
openAiApiKey,
geminiApiKey,
openAiNativeApiKey,
@@ -658,6 +669,7 @@ export async function resetGlobalState(context: vscode.ExtensionContext) {
"awsAccessKey",
"awsSecretKey",
"awsSessionToken",
"awsBedrockApiKey",
"openAiApiKey",
"geminiApiKey",
"openAiNativeApiKey",
+20 -8
View File
@@ -121,15 +121,9 @@ export abstract class DiffViewProvider {
// Replace all content up to the current line with accumulated lines
// This is necessary (as compared to inserting one line at a time) to handle cases where html tags on previous lines are auto closed for example
const edit = new vscode.WorkspaceEdit()
const rangeToReplace = new vscode.Range(0, 0, currentLine + 1, 0)
const contentToReplace = accumulatedLines.slice(0, currentLine + 1).join("\n") + "\n"
edit.replace(document.uri, rangeToReplace, contentToReplace)
await vscode.workspace.applyEdit(edit)
// Update decorations for the entire changed section
this.activeLineController.setActiveLine(currentLine)
this.fadedOverlayController.updateOverlayAfterLine(currentLine, document.lineCount)
const rangeToReplace = { startLine: 0, endLine: currentLine + 1 }
await this.replaceText(contentToReplace, rangeToReplace, currentLine)
// Scroll to the actual change location if provided.
if (changeLocation) {
@@ -186,6 +180,24 @@ export abstract class DiffViewProvider {
}
}
/**
* Replaces text in the diff editor with the specified content.
*
* This abstract method must be implemented by subclasses to handle the actual
* text replacement in their specific diff editor implementation. It's called
* during the streaming update process to progressively show changes.
*
* @param content The new content to insert into the document
* @param rangeToReplace An object specifying the line range to replace
* @param currentLine The current line number being edited, used for scroll positioning
* @returns A promise that resolves when the text replacement is complete
*/
abstract replaceText(
content: string,
rangeToReplace: { startLine: number; endLine: number },
currentLine: number,
): Promise<void>
async saveChanges(): Promise<{
newProblemsMessage: string | undefined
userEdits: string | undefined
@@ -77,4 +77,24 @@ export class VscodeDiffViewProvider extends DiffViewProvider {
// Apply faded overlay to all lines initially
this.fadedOverlayController.addLines(0, this.activeDiffEditor.document.lineCount)
}
override async replaceText(
content: string,
rangeToReplace: { startLine: number; endLine: number },
currentLine: number,
): Promise<void> {
const document = this.activeDiffEditor?.document
if (!document) {
throw new Error("User closed text editor, unable to edit file...")
}
const edit = new vscode.WorkspaceEdit()
const range = new vscode.Range(rangeToReplace.startLine, 0, rangeToReplace.endLine, 0)
edit.replace(document.uri, range, content)
await vscode.workspace.applyEdit(edit)
// Update decorations for the entire changed section
this.activeLineController?.setActiveLine(currentLine)
this.fadedOverlayController?.updateOverlayAfterLine(currentLine, document.lineCount)
}
}
+2 -1
View File
@@ -7,6 +7,7 @@ import { HistoryItem } from "./HistoryItem"
import { TelemetrySetting } from "./TelemetrySetting"
import { ClineRulesToggles } from "./cline-rules"
import { UserInfo } from "./UserInfo"
import { McpDisplayMode, DEFAULT_MCP_DISPLAY_MODE } from "./McpDisplayMode"
// webview will hold state
export interface ExtensionMessage {
@@ -37,7 +38,7 @@ export interface ExtensionState {
clineMessages: ClineMessage[]
currentTaskItem?: HistoryItem
mcpMarketplaceEnabled?: boolean
mcpRichDisplayEnabled: boolean
mcpDisplayMode: McpDisplayMode
planActSeparateModelsSetting: boolean
enableCheckpointsSetting?: boolean
platform: Platform
+9
View File
@@ -0,0 +1,9 @@
/**
* Represents the different display modes available for MCP responses
*/
export type McpDisplayMode = "rich" | "plain" | "markdown"
/**
* Default display mode for MCP responses
*/
export const DEFAULT_MCP_DISPLAY_MODE: McpDisplayMode = "plain"
+4 -1
View File
@@ -50,8 +50,10 @@ export interface ApiHandlerOptions {
awsRegion?: string
awsUseCrossRegionInference?: boolean
awsBedrockUsePromptCache?: boolean
awsAuthentication?: string
awsUseProfile?: boolean
awsProfile?: string
awsBedrockApiKey?: string
awsBedrockEndpoint?: string
awsBedrockCustomSelected?: boolean
awsBedrockCustomModelBaseId?: BedrockModelId
@@ -2094,7 +2096,8 @@ export const xaiModels = {
supportsImages: true,
supportsPromptCache: true,
inputPrice: 3.0, // will have different pricing for long context vs short context
outputPrice: 6.0,
cacheReadsPrice: 0.75,
outputPrice: 15.0,
},
"grok-3-beta": {
maxTokens: 8192,
@@ -328,7 +328,9 @@ export function convertApiConfigurationToProto(config: ApiConfiguration): ProtoA
awsUseCrossRegionInference: config.awsUseCrossRegionInference,
awsBedrockUsePromptCache: config.awsBedrockUsePromptCache,
awsUseProfile: config.awsUseProfile,
awsAuthentication: config.awsAuthentication,
awsProfile: config.awsProfile,
awsBedrockApiKey: config.awsBedrockApiKey,
awsBedrockEndpoint: config.awsBedrockEndpoint,
awsBedrockCustomSelected: config.awsBedrockCustomSelected,
awsBedrockCustomModelBaseId: config.awsBedrockCustomModelBaseId as string | undefined,
@@ -407,7 +409,9 @@ export function convertProtoToApiConfiguration(protoConfig: ProtoApiConfiguratio
awsUseCrossRegionInference: protoConfig.awsUseCrossRegionInference,
awsBedrockUsePromptCache: protoConfig.awsBedrockUsePromptCache,
awsUseProfile: protoConfig.awsUseProfile,
awsAuthentication: protoConfig.awsAuthentication,
awsProfile: protoConfig.awsProfile,
awsBedrockApiKey: protoConfig.awsBedrockApiKey,
awsBedrockEndpoint: protoConfig.awsBedrockEndpoint,
awsBedrockCustomSelected: protoConfig.awsBedrockCustomSelected,
awsBedrockCustomModelBaseId: protoConfig.awsBedrockCustomModelBaseId as BedrockModelId | undefined,
@@ -60,7 +60,9 @@ export function convertApiConfigurationToProtoApiConfiguration(config: ApiConfig
awsUseCrossRegionInference: config.awsUseCrossRegionInference,
awsBedrockUsePromptCache: config.awsBedrockUsePromptCache,
awsUseProfile: config.awsUseProfile,
awsAuthentication: config.awsAuthentication,
awsProfile: config.awsProfile,
awsBedrockApiKey: config.awsBedrockApiKey,
awsBedrockEndpoint: config.awsBedrockEndpoint,
// Vertex AI fields
@@ -177,6 +179,8 @@ export function convertProtoApiConfigurationToApiConfiguration(protoConfig: Prot
awsBedrockUsePromptCache: protoConfig.awsBedrockUsePromptCache,
awsUseProfile: protoConfig.awsUseProfile,
awsProfile: protoConfig.awsProfile,
awsAuthentication: protoConfig.awsAuthentication,
awsBedrockApiKey: protoConfig.awsBedrockApiKey,
awsBedrockEndpoint: protoConfig.awsBedrockEndpoint,
// Vertex AI fields
+12 -2
View File
@@ -1,8 +1,18 @@
import { getHostBridgeProvider } from "@/hosts/host-providers"
import { DiffViewProvider } from "@/integrations/editor/DiffViewProvider"
export class ExternalDiffViewProvider extends DiffViewProvider {
override async openDiffEditor(): Promise<void> {
// The host bridge proto changes are not submitted yet.
//getHostBridgeProvider().diffClient.openDiff(this.absolutePath)
if (!this.absolutePath) {
return
}
getHostBridgeProvider().diffClient.openDiff({ path: this.absolutePath, content: this.originalContent ?? "" })
}
override replaceText(
content: string,
rangeToReplace: { startLine: number; endLine: number },
currentLine: number,
): Promise<void> {
throw new Error("Method not implemented.")
}
}
+30
View File
@@ -0,0 +1,30 @@
import { describe, it } from "mocha"
import "should"
import { shouldSkipReasoningForModel } from "../model-utils"
describe("shouldSkipReasoningForModel", () => {
it("should return true for grok-4 models", () => {
shouldSkipReasoningForModel("grok-4").should.equal(true)
shouldSkipReasoningForModel("x-ai/grok-4").should.equal(true)
shouldSkipReasoningForModel("openrouter/grok-4-turbo").should.equal(true)
shouldSkipReasoningForModel("some-provider/grok-4-mini").should.equal(true)
})
it("should return false for non-grok-4 models", () => {
shouldSkipReasoningForModel("grok-3").should.equal(false)
shouldSkipReasoningForModel("grok-2").should.equal(false)
shouldSkipReasoningForModel("claude-3-sonnet").should.equal(false)
shouldSkipReasoningForModel("gpt-4").should.equal(false)
shouldSkipReasoningForModel("gemini-pro").should.equal(false)
})
it("should return false for undefined or empty model IDs", () => {
shouldSkipReasoningForModel(undefined).should.equal(false)
shouldSkipReasoningForModel("").should.equal(false)
})
it("should be case sensitive", () => {
shouldSkipReasoningForModel("GROK-4").should.equal(false)
shouldSkipReasoningForModel("Grok-4").should.equal(false)
})
})
+11
View File
@@ -13,3 +13,14 @@ export function isGemini2dot5ModelFamily(api: ApiHandler): boolean {
const modelId = model.id
return modelId.includes("gemini-2.5")
}
/**
* Determines if reasoning content should be skipped for a given model
* Currently skips reasoning for Grok-4 models since they only display "thinking" without useful information
*/
export function shouldSkipReasoningForModel(modelId?: string): boolean {
if (!modelId) {
return false
}
return modelId.includes("grok-4")
}
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "cline-standalone",
"name": "cline-core",
"version": "0.0.1",
"main": "standalone.js",
"main": "cline-core.js",
"dependencies": {
"@grpc/grpc-js": "^1.13.3",
"@grpc/reflection": "^1.0.4",
@@ -0,0 +1,29 @@
import React from "react"
import { VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"
import { McpDisplayMode } from "@shared/McpDisplayMode"
interface McpDisplayModeDropdownProps {
value: McpDisplayMode
onChange: (mode: McpDisplayMode) => void
id?: string
className?: string
style?: React.CSSProperties
onClick?: (e: React.MouseEvent) => void
}
const McpDisplayModeDropdown: React.FC<McpDisplayModeDropdownProps> = ({ value, onChange, id, className, style, onClick }) => {
const handleChange = (e: any) => {
const newMode = e.target.value as McpDisplayMode
onChange(newMode)
}
return (
<VSCodeDropdown id={id} value={value} onChange={handleChange} onClick={onClick} className={className} style={style}>
<VSCodeOption value="plain">Plain Text</VSCodeOption>
<VSCodeOption value="rich">Rich Display</VSCodeOption>
<VSCodeOption value="markdown">Markdown</VSCodeOption>
</VSCodeDropdown>
)
}
export default McpDisplayModeDropdown
@@ -1,12 +1,17 @@
import React, { useEffect, useState, useCallback } from "react"
import { VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react" // Import ProgressRing
import { VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react"
import { useExtensionState } from "../../../context/ExtensionStateContext"
import LinkPreview from "./LinkPreview"
import ImagePreview from "./ImagePreview"
import styled from "styled-components"
import { CODE_BLOCK_BG_COLOR } from "@/components/common/CodeBlock"
import ChatErrorBoundary from "@/components/chat/ChatErrorBoundary"
import { isUrl, isLocalhostUrl, formatUrlForOpening, checkIfImageUrl } from "./utils/mcpRichUtil"
import MarkdownBlock from "@/components/common/MarkdownBlock"
import McpDisplayModeDropdown from "./McpDisplayModeDropdown"
import { DropdownContainer } from "@/components/settings/ApiOptions"
import { updateSetting } from "@/components/settings/utils/settingsHandlers"
import { McpDisplayMode } from "@shared/McpDisplayMode"
import { UrlMatch, processResponseUrls, DisplaySegment, buildDisplaySegments } from "./utils/mcpRichUtil"
// Maximum number of URLs to process in total, per response
export const MAX_URLS = 50
@@ -36,46 +41,6 @@ const ResponseHeader = styled.div`
}
`
const ToggleSwitch = styled.div`
display: flex;
align-items: center;
font-size: 12px;
color: var(--vscode-descriptionForeground);
.toggle-label {
margin-right: 8px;
}
.toggle-container {
position: relative;
width: 40px;
height: 20px;
background-color: var(--vscode-button-secondaryBackground);
border-radius: 10px;
cursor: pointer;
transition: background-color 0.3s;
}
.toggle-container.active {
background-color: var(--vscode-button-background);
}
.toggle-handle {
position: absolute;
top: 2px;
left: 2px;
width: 16px;
height: 16px;
background-color: var(--vscode-button-foreground);
border-radius: 50%;
transition: transform 0.3s;
}
.toggle-container.active .toggle-handle {
transform: translateX(20px);
}
`
const ResponseContainer = styled.div`
position: relative;
font-family: var(--vscode-editor-font-family, monospace);
@@ -107,28 +72,16 @@ interface McpResponseDisplayProps {
responseText: string
}
// Represents a URL found in the text with its position and metadata
interface UrlMatch {
url: string // The actual URL
fullMatch: string // The full matched text
index: number // Position in the text
isImage: boolean // Whether this URL is an image
isProcessed: boolean // Whether we've already processed this URL (to avoid duplicates)
}
const McpResponseDisplay: React.FC<McpResponseDisplayProps> = ({ responseText }) => {
const { mcpResponsesCollapsed, mcpRichDisplayEnabled } = useExtensionState() // Get setting from context
const { mcpResponsesCollapsed, mcpDisplayMode } = useExtensionState() // Get setting from context
const [isExpanded, setIsExpanded] = useState(!mcpResponsesCollapsed) // Initialize with context setting
const [isLoading, setIsLoading] = useState(false) // Initial loading state for rich content
const [displayMode, setDisplayMode] = useState<"rich" | "plain">(() => {
// Initialize directly from the global setting.
return mcpRichDisplayEnabled ? "rich" : "plain"
})
const [urlMatches, setUrlMatches] = useState<UrlMatch[]>([])
const [error, setError] = useState<string | null>(null)
const toggleDisplayMode = useCallback(() => {
setDisplayMode((prevMode) => (prevMode === "rich" ? "plain" : "rich"))
const handleDisplayModeChange = useCallback((newMode: McpDisplayMode) => {
updateSetting("mcpDisplayMode", newMode)
}, [])
const toggleExpand = useCallback(() => {
@@ -142,138 +95,89 @@ const McpResponseDisplay: React.FC<McpResponseDisplayProps> = ({ responseText })
// Find all URLs in the text and determine if they're images
useEffect(() => {
// Skip all processing if in plain mode
if (!isExpanded || displayMode === "plain") {
// Skip all processing if in plain mode or markdown mode
if (!isExpanded || mcpDisplayMode === "plain" || mcpDisplayMode === "markdown") {
setIsLoading(false)
if (urlMatches.length > 0) {
setUrlMatches([]) // Clear any existing matches when in plain mode
setUrlMatches([]) // Clear any existing matches when not in rich mode
}
return
}
// Use a direct boolean for cancellation that's scoped to this effect run
let processingCanceled = false
const processResponse = async () => {
console.log("Processing MCP response for URL extraction")
setIsLoading(true)
setError(null)
try {
const text = responseText || ""
const matches: UrlMatch[] = []
const urlRegex = /(?:https?:\/\/|data:image)[^\s<>"']+/g
let urlMatch: RegExpExecArray | null
let urlCount = 0
console.log("Processing MCP response for URL extraction")
setIsLoading(true)
setError(null)
// First pass: Extract all URLs and immediately make them available for rendering
while ((urlMatch = urlRegex.exec(text)) !== null && urlCount < MAX_URLS) {
// Get the original URL from the match - never modify the original URL text
const url = urlMatch[0]
// Skip invalid URLs
if (!isUrl(url)) {
console.log("Skipping invalid URL:", url)
continue
}
// Skip localhost URLs to prevent security issues
if (isLocalhostUrl(url)) {
console.log("Skipping localhost URL:", url)
continue
}
matches.push({
url,
fullMatch: url,
index: urlMatch.index,
isImage: false, // Will check later
isProcessed: false,
})
urlCount++
}
console.log(`Found ${matches.length} URLs in text, will check if they are images`)
// Set matches immediately so UI can start rendering with loading states
setUrlMatches(matches.sort((a, b) => a.index - b.index))
// Mark loading as complete to show content immediately
// Use the orchestrator function from mcpRichUtil
const cleanup = processResponseUrls(
responseText || "",
MAX_URLS,
(matches) => {
setUrlMatches(matches)
setIsLoading(false)
// Process image checks in the background - one at a time to avoid network flooding
const processImageChecks = async () => {
console.log(`Starting sequential URL processing for ${matches.length} URLs`)
for (let i = 0; i < matches.length; i++) {
// Skip already processed URLs (from extension check)
if (matches[i].isProcessed) continue
// Check if processing has been canceled (switched to plain mode)
if (processingCanceled) {
console.log("URL processing canceled - display mode changed to plain")
return
}
const match = matches[i]
console.log(`Processing URL ${i + 1} of ${matches.length}: ${match.url}`)
try {
// Process each URL individually
const isImage = await checkIfImageUrl(match.url)
// Skip if processing has been canceled
if (processingCanceled) return
// Update the match in place
match.isImage = isImage
match.isProcessed = true
// Update state after each URL to show progress
// Create a new array to ensure React detects the state change
setUrlMatches([...matches])
} catch (err) {
console.log(`URL check error: ${match.url}`, err)
match.isProcessed = true
// Update state even on error
if (!processingCanceled) {
setUrlMatches([...matches])
}
}
// Delay between URL processing to avoid overwhelming the network
if (!processingCanceled && i < matches.length - 1) {
await new Promise((resolve) => setTimeout(resolve, 100))
}
}
console.log(`URL processing complete. Found ${matches.filter((m) => m.isImage).length} image URLs`)
}
// Start the background processing
processImageChecks()
} catch (error) {
setError("Failed to process response content. Switch to plain text mode to view safely.")
},
(updatedMatches) => {
setUrlMatches(updatedMatches)
},
(errorMessage) => {
setError(errorMessage)
setIsLoading(false)
}
}
},
)
processResponse()
return cleanup
}, [responseText, mcpDisplayMode, isExpanded])
// Cleanup function to cancel processing if component unmounts or dependencies change
return () => {
processingCanceled = true
console.log("Cleaning up URL processing")
// Helper function to render a display segment
const renderSegment = (segment: DisplaySegment): JSX.Element => {
switch (segment.type) {
case "text":
case "url":
return <UrlText key={segment.key}>{segment.content}</UrlText>
case "image":
return (
<div key={segment.key}>
<ImagePreview url={segment.url!} />
</div>
)
case "link":
return (
<div key={segment.key} style={{ margin: "10px 0" }}>
<LinkPreview url={segment.url!} />
</div>
)
case "error":
return (
<div
key={segment.key}
style={{
margin: "10px 0",
padding: "8px",
color: "var(--vscode-errorForeground)",
border: "1px solid var(--vscode-editorError-foreground)",
borderRadius: "4px",
height: "128px",
overflow: "auto",
}}>
{segment.content}
</div>
)
default:
return <React.Fragment key={segment.key} />
}
}, [responseText, displayMode, isExpanded])
}
// Function to render content based on display mode
const renderContent = () => {
if (!isExpanded) {
return null // Don't render content if not expanded
return null
}
if (isLoading && displayMode === "rich") {
if (isLoading && mcpDisplayMode === "rich") {
return (
<div style={{ display: "flex", justifyContent: "center", alignItems: "center", height: "50px" }}>
<VSCodeProgressRing />
@@ -281,12 +185,14 @@ const McpResponseDisplay: React.FC<McpResponseDisplayProps> = ({ responseText })
)
}
// For plain text mode, just show the text
if (displayMode === "plain") {
if (mcpDisplayMode === "plain") {
return <UrlText>{responseText}</UrlText>
}
// Show error message if there was an error
if (mcpDisplayMode === "markdown") {
return <MarkdownBlock markdown={responseText} />
}
if (error) {
return (
<>
@@ -296,97 +202,9 @@ const McpResponseDisplay: React.FC<McpResponseDisplayProps> = ({ responseText })
)
}
// For rich display mode, show the text with embedded content
if (displayMode === "rich") {
// We already know displayMode is "rich" if we get here
// Create an array of text segments and embedded content
const segments: JSX.Element[] = []
let lastIndex = 0
let segmentIndex = 0
// Track embed count for logging
let embedCount = 0
// Add the text before the first URL
if (urlMatches.length === 0) {
segments.push(<UrlText key={`segment-${segmentIndex}`}>{responseText}</UrlText>)
} else {
for (let i = 0; i < urlMatches.length; i++) {
const match = urlMatches[i]
const { url, fullMatch, index } = match
// Add text segment before this URL
if (index > lastIndex) {
segments.push(
<UrlText key={`segment-${segmentIndex++}`}>{responseText.substring(lastIndex, index)}</UrlText>,
)
}
// Add the URL text itself
segments.push(<UrlText key={`url-${segmentIndex++}`}>{fullMatch}</UrlText>)
// Calculate the end position of this URL in the text
const urlEndIndex = index + fullMatch.length
// Add embedded content after the URL
// For images, use the ImagePreview component
if (match.isImage) {
segments.push(
<div key={`embed-image-${url}-${segmentIndex++}`}>
{/* Use formatUrlForOpening for network calls but preserve original URL in display */}
<ImagePreview url={formatUrlForOpening(url)} />
</div>,
)
embedCount++
// console.log(`Added image embed for ${url}, embed count: ${embedCount}`);
} else if (match.isProcessed) {
// For non-image URLs or URLs we haven't processed yet, show link preview
try {
// Skip localhost URLs
if (!isLocalhostUrl(url)) {
// Use a unique key that includes the URL to ensure each preview is isolated
segments.push(
<div key={`embed-${url}-${segmentIndex++}`} style={{ margin: "10px 0" }}>
{/* Already using formatUrlForOpening for link previews */}
<LinkPreview url={formatUrlForOpening(url)} />
</div>,
)
embedCount++
// console.log(`Added link preview for ${url}, embed count: ${embedCount}`);
}
} catch (e) {
console.log("Link preview could not be created")
// Show error message for failed link preview
segments.push(
<div
key={`embed-error-${segmentIndex++}`}
style={{
margin: "10px 0",
padding: "8px",
color: "var(--vscode-errorForeground)",
border: "1px solid var(--vscode-editorError-foreground)",
borderRadius: "4px",
height: "128px", // Fixed height
overflow: "auto", // Allow scrolling if content overflows
}}>
Failed to create preview for: {url}
</div>,
)
}
}
// Update lastIndex for next segment
lastIndex = urlEndIndex
}
// Add any remaining text after the last URL
if (lastIndex < responseText.length) {
segments.push(<UrlText key={`segment-${segmentIndex++}`}>{responseText.substring(lastIndex)}</UrlText>)
}
}
return <>{segments}</>
if (mcpDisplayMode === "rich") {
const segments = buildDisplaySegments(responseText, urlMatches)
return <>{segments.map(renderSegment)}</>
}
return null
@@ -405,16 +223,15 @@ const McpResponseDisplay: React.FC<McpResponseDisplayProps> = ({ responseText })
<span className={`codicon codicon-chevron-${isExpanded ? "down" : "right"} header-icon`}></span>
Response
</div>
<div style={{ minWidth: isExpanded ? "auto" : "0", visibility: isExpanded ? "visible" : "hidden" }}>
<ToggleSwitch onClick={(e) => e.stopPropagation()}>
<span className="toggle-label">{displayMode === "rich" ? "Rich Display" : "Plain Text"}</span>
<div
className={`toggle-container ${displayMode === "rich" ? "active" : ""}`}
onClick={toggleDisplayMode}>
<div className="toggle-handle"></div>
</div>
</ToggleSwitch>
</div>
<DropdownContainer
style={{ minWidth: isExpanded ? "auto" : "0", visibility: isExpanded ? "visible" : "hidden" }}>
<McpDisplayModeDropdown
value={mcpDisplayMode}
onChange={handleDisplayModeChange}
onClick={(e) => e.stopPropagation()}
style={{ minWidth: "120px" }}
/>
</DropdownContainer>
</ResponseHeader>
{isExpanded && <div className="response-content">{renderContent()}</div>}
@@ -1,6 +1,23 @@
import { WebServiceClient } from "@/services/grpc-client"
import { StringRequest } from "@shared/proto/common"
// Represents a URL found in the text with its position and metadata
export interface UrlMatch {
url: string // The actual URL
fullMatch: string // The full matched text
index: number // Position in the text
isImage: boolean // Whether this URL is an image
isProcessed: boolean // Whether we've already processed this URL (to avoid duplicates)
}
// Display segment interface
export interface DisplaySegment {
type: "text" | "url" | "image" | "link" | "error"
content: string
url?: string
key: string // Pre-computed key for React
}
// Safely create a URL object with error handling and ensure HTTPS
export const safeCreateUrl = (url: string): URL | null => {
try {
@@ -168,3 +185,224 @@ export const checkIfImageUrl = async (url: string): Promise<boolean> => {
console.log(`URL protocol not supported for image check: ${url}`)
return false
}
/**
* Extracts all valid URLs from the given text
* @param text - The text to search for URLs
* @param maxUrls - Maximum number of URLs to extract (default: 50)
* @returns Array of URL matches sorted by position in text
*/
export const extractUrlsFromText = (text: string, maxUrls: number = 50): UrlMatch[] => {
const matches: UrlMatch[] = []
const urlRegex = /(?:https?:\/\/|data:image)[^\s<>"']+/g
let urlMatch: RegExpExecArray | null
let urlCount = 0
while ((urlMatch = urlRegex.exec(text)) !== null && urlCount < maxUrls) {
const url = urlMatch[0]
// Skip invalid URLs
if (!isUrl(url)) {
console.log("Skipping invalid URL:", url)
continue
}
// Skip localhost URLs to prevent security issues
if (isLocalhostUrl(url)) {
console.log("Skipping localhost URL:", url)
continue
}
matches.push({
url,
fullMatch: url,
index: urlMatch.index,
isImage: false, // Will be determined later
isProcessed: false,
})
urlCount++
}
console.log(`Found ${matches.length} URLs in text`)
return matches.sort((a, b) => a.index - b.index)
}
/**
* Processes URLs to determine their types (e.g., image vs link)
* Processes URLs sequentially to avoid network flooding
* @param matches - Array of URL matches to process
* @param onProgress - Callback for progress updates with updated matches
* @param cancellationToken - Object to check if processing should be cancelled
* @returns Promise that resolves when processing is complete
*/
export const processUrlTypes = async (
matches: UrlMatch[],
onProgress: (updatedMatches: UrlMatch[]) => void,
cancellationToken: { cancelled: boolean },
): Promise<void> => {
console.log(`Starting sequential URL processing for ${matches.length} URLs`)
for (let i = 0; i < matches.length; i++) {
// Skip already processed URLs
if (matches[i].isProcessed) continue
// Check if processing has been canceled
if (cancellationToken.cancelled) {
console.log("URL processing canceled")
return
}
const match = matches[i]
console.log(`Processing URL ${i + 1} of ${matches.length}: ${match.url}`)
try {
// Check if URL is an image
const isImage = await checkIfImageUrl(match.url)
// Skip if processing has been canceled
if (cancellationToken.cancelled) return
// Update the match
match.isImage = isImage
match.isProcessed = true
// Notify progress with a new array to ensure React detects changes
onProgress([...matches])
} catch (err) {
console.log(`URL check error: ${match.url}`, err)
match.isProcessed = true
// Update state even on error
if (!cancellationToken.cancelled) {
onProgress([...matches])
}
}
// Delay between URL processing to avoid overwhelming the network
if (!cancellationToken.cancelled && i < matches.length - 1) {
await new Promise((resolve) => setTimeout(resolve, 100))
}
}
console.log(`URL processing complete. Found ${matches.filter((m) => m.isImage).length} image URLs`)
}
/**
* Orchestrates the URL extraction and processing pipeline
* @param text - The response text to process
* @param maxUrls - Maximum number of URLs to process
* @param onMatchesFound - Callback when initial URLs are extracted
* @param onMatchesUpdated - Callback when URL types are determined
* @param onError - Error handler callback
* @returns Cleanup function to cancel processing
*/
export const processResponseUrls = (
text: string,
maxUrls: number,
onMatchesFound: (matches: UrlMatch[]) => void,
onMatchesUpdated: (matches: UrlMatch[]) => void,
onError: (error: string) => void,
): (() => void) => {
const cancellationToken = { cancelled: false }
const process = async () => {
try {
// Extract URLs from text
const matches = extractUrlsFromText(text, maxUrls)
// Immediately notify about found matches
onMatchesFound(matches)
// Process URLs in the background
await processUrlTypes(matches, onMatchesUpdated, cancellationToken)
} catch (error) {
onError("Failed to process response content. Switch to plain text mode to view safely.")
}
}
// Start processing
process()
// Return cleanup function
return () => {
cancellationToken.cancelled = true
console.log("Cleaning up URL processing")
}
}
/**
* Builds an array of display segments from response text and URL matches
* @param responseText - The full response text
* @param urlMatches - Array of URL matches with their positions and types
* @returns Array of display segments describing how to render the content
*/
export const buildDisplaySegments = (responseText: string, urlMatches: UrlMatch[]): DisplaySegment[] => {
const segments: DisplaySegment[] = []
let lastIndex = 0
let segmentIndex = 0
// Handle case with no URLs
if (urlMatches.length === 0) {
return [
{
type: "text",
content: responseText,
key: "segment-0",
},
]
}
// Process each URL match
for (let i = 0; i < urlMatches.length; i++) {
const match = urlMatches[i]
const { url, fullMatch, index } = match
// Add text segment before this URL
if (index > lastIndex) {
segments.push({
type: "text",
content: responseText.substring(lastIndex, index),
key: `segment-${segmentIndex++}`,
})
}
// Add the URL text itself
segments.push({
type: "url",
content: fullMatch,
key: `url-${segmentIndex++}`,
})
// Add embedded content after the URL
if (match.isImage) {
segments.push({
type: "image",
content: url,
url: formatUrlForOpening(url),
key: `embed-image-${url}-${segmentIndex++}`,
})
} else if (match.isProcessed && !isLocalhostUrl(url)) {
segments.push({
type: "link",
content: url,
url: formatUrlForOpening(url),
key: `embed-${url}-${segmentIndex++}`,
})
}
// Update lastIndex for next segment
lastIndex = index + fullMatch.length
}
// Add any remaining text after the last URL
if (lastIndex < responseText.length) {
segments.push({
type: "text",
content: responseText.substring(lastIndex),
key: `segment-${segmentIndex++}`,
})
}
return segments
}
@@ -54,9 +54,9 @@ const featuredModels = [
label: "Trending",
},
{
id: "x-ai/grok-4",
description: "Latest flagship model from xAI",
label: "Fast & Cheap",
id: "moonshotai/kimi-k2",
description: "Open source model topping coding benchmarks",
label: "New",
},
]
@@ -32,28 +32,40 @@ export const BedrockProvider = ({ showModelOptions, isPopup }: BedrockProviderPr
gap: 5,
}}>
<VSCodeRadioGroup
value={apiConfiguration?.awsUseProfile ? "profile" : "credentials"}
value={apiConfiguration?.awsAuthentication ?? (apiConfiguration?.awsProfile ? "profile" : "credentials")}
onChange={(e) => {
const value = (e.target as HTMLInputElement)?.value
const useProfile = value === "profile"
handleFieldChange("awsUseProfile", useProfile)
handleFieldChange("awsAuthentication", value)
}}>
<VSCodeRadio value="credentials">AWS Credentials</VSCodeRadio>
<VSCodeRadio value="apikey">API Key</VSCodeRadio>
<VSCodeRadio value="profile">AWS Profile</VSCodeRadio>
<VSCodeRadio value="credentials">AWS Credentials</VSCodeRadio>
</VSCodeRadioGroup>
{apiConfiguration?.awsUseProfile ? (
{(apiConfiguration?.awsAuthentication === undefined && apiConfiguration?.awsUseProfile) ||
apiConfiguration?.awsAuthentication == "profile" ? (
<DebouncedTextField
initialValue={apiConfiguration?.awsProfile || ""}
onChange={(value) => handleFieldChange("awsProfile", value)}
key="profile"
initialValue={apiConfiguration?.awsProfile ?? ""}
style={{ width: "100%" }}
onChange={(value) => handleFieldChange("awsProfile", value)}
placeholder="Enter profile name (default if empty)">
<span style={{ fontWeight: 500 }}>AWS Profile Name</span>
</DebouncedTextField>
) : apiConfiguration?.awsAuthentication == "apikey" ? (
<DebouncedTextField
key="apikey"
type="password"
initialValue={apiConfiguration?.awsBedrockApiKey ?? ""}
style={{ width: "100%" }}
onChange={(value) => handleFieldChange("awsBedrockApiKey", value)}
placeholder="Enter Bedrock Api Key">
<span style={{ fontWeight: 500 }}>AWS Bedrock Api Key</span>
</DebouncedTextField>
) : (
<>
<DebouncedTextField
key="accessKey"
initialValue={apiConfiguration?.awsAccessKey || ""}
onChange={(value) => handleFieldChange("awsAccessKey", value)}
style={{ width: "100%" }}
@@ -4,6 +4,8 @@ import { memo } from "react"
import { OpenAIReasoningEffort } from "@shared/ChatSettings"
import { updateSetting } from "../utils/settingsHandlers"
import { convertChatSettingsToProtoChatSettings } from "@shared/proto-conversions/state/chat-settings-conversion"
import { McpDisplayMode } from "@shared/McpDisplayMode"
import McpDisplayModeDropdown from "@/components/mcp/chat-display/McpDisplayModeDropdown"
import Section from "../Section"
interface FeatureSettingsSectionProps {
@@ -11,7 +13,7 @@ interface FeatureSettingsSectionProps {
}
const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionProps) => {
const { enableCheckpointsSetting, mcpMarketplaceEnabled, mcpRichDisplayEnabled, mcpResponsesCollapsed, chatSettings } =
const { enableCheckpointsSetting, mcpMarketplaceEnabled, mcpDisplayMode, mcpResponsesCollapsed, chatSettings } =
useExtensionState()
const handleReasoningEffortChange = (newValue: OpenAIReasoningEffort) => {
@@ -59,16 +61,20 @@ const FeatureSettingsSection = ({ renderSectionHeader }: FeatureSettingsSectionP
</p>
</div>
<div style={{ marginTop: 10 }}>
<VSCodeCheckbox
checked={mcpRichDisplayEnabled}
onChange={(e: any) => {
const checked = e.target.checked === true
updateSetting("mcpRichDisplayEnabled", checked)
}}>
Enable Rich MCP Display
</VSCodeCheckbox>
<p className="text-xs text-[var(--vscode-descriptionForeground)]">
Enables rich formatting for MCP responses. When disabled, responses will be shown in plain text.
<label
htmlFor="mcp-display-mode-dropdown"
className="block text-sm font-medium text-[var(--vscode-foreground)] mb-1">
MCP Display Mode
</label>
<McpDisplayModeDropdown
id="mcp-display-mode-dropdown"
value={mcpDisplayMode}
onChange={(newMode: McpDisplayMode) => updateSetting("mcpDisplayMode", newMode)}
className="w-full"
/>
<p className="text-xs mt-[5px] text-[var(--vscode-descriptionForeground)]">
Controls how MCP responses are displayed: plain text, rich formatting with links/images, or markdown
rendering.
</p>
</div>
<div style={{ marginTop: 10 }}>
@@ -14,13 +14,11 @@ import { TerminalProfile } from "@shared/proto/state"
import { convertProtoToClineMessage } from "@shared/proto-conversions/cline-message"
import { convertProtoMcpServersToMcpServers } from "@shared/proto-conversions/mcp/mcp-server-conversion"
import { DEFAULT_AUTO_APPROVAL_SETTINGS } from "@shared/AutoApprovalSettings"
import { DEFAULT_BROWSER_SETTINGS, BrowserSettings } from "@shared/BrowserSettings"
import { DEFAULT_BROWSER_SETTINGS } from "@shared/BrowserSettings"
import { ChatSettings, DEFAULT_CHAT_SETTINGS } from "@shared/ChatSettings"
import { DEFAULT_PLATFORM, ExtensionMessage, ExtensionState } from "@shared/ExtensionMessage"
import { TelemetrySetting } from "@shared/TelemetrySetting"
import { DEFAULT_PLATFORM, ExtensionState } from "@shared/ExtensionMessage"
import { findLastIndex } from "@shared/array"
import {
ApiConfiguration,
ModelInfo,
openRouterDefaultModelId,
openRouterDefaultModelInfo,
@@ -31,6 +29,7 @@ import { McpMarketplaceCatalog, McpServer, McpViewTab } from "../../../src/share
import { convertTextMateToHljs } from "../utils/textMateToHljs"
import { OpenRouterCompatibleModelInfo } from "@shared/proto/models"
import { UserInfo } from "@shared/proto/account"
import { DEFAULT_MCP_DISPLAY_MODE } from "@shared/McpDisplayMode"
interface ExtensionStateContextType extends ExtensionState {
didHydrateState: boolean
@@ -54,19 +53,8 @@ interface ExtensionStateContextType extends ExtensionState {
showAnnouncement: boolean
// Setters
setApiConfiguration: (config: ApiConfiguration) => void
setTelemetrySetting: (value: TelemetrySetting) => void
setShowAnnouncement: (value: boolean) => void
setShouldShowAnnouncement: (value: boolean) => void
setPlanActSeparateModelsSetting: (value: boolean) => void
setEnableCheckpointsSetting: (value: boolean) => void
setMcpMarketplaceEnabled: (value: boolean) => void
setMcpRichDisplayEnabled: (value: boolean) => void
setMcpResponsesCollapsed: (value: boolean) => void
setShellIntegrationTimeout: (value: number) => void
setTerminalReuseEnabled: (value: boolean) => void
setTerminalOutputLineLimit: (value: number) => void
setDefaultTerminalProfile: (value: string) => void
setChatSettings: (value: ChatSettings) => void
setMcpServers: (value: McpServer[]) => void
setRequestyModels: (value: Record<string, ModelInfo>) => void
@@ -78,8 +66,6 @@ interface ExtensionStateContextType extends ExtensionState {
setGlobalWorkflowToggles: (toggles: Record<string, boolean>) => void
setMcpMarketplaceCatalog: (value: McpMarketplaceCatalog) => void
setTotalTasksSize: (value: number | null) => void
setAvailableTerminalProfiles: (profiles: TerminalProfile[]) => void // Setter for profiles
setBrowserSettings: (value: BrowserSettings) => void
// Refresh functions
refreshOpenRouterModels: () => void
@@ -190,7 +176,7 @@ export const ExtensionStateContextProvider: React.FC<{
distinctId: "",
planActSeparateModelsSetting: true,
enableCheckpointsSetting: true,
mcpRichDisplayEnabled: true,
mcpDisplayMode: DEFAULT_MCP_DISPLAY_MODE,
globalClineRulesToggles: {},
localClineRulesToggles: {},
localCursorRulesToggles: {},
@@ -676,72 +662,15 @@ export const ExtensionStateContextProvider: React.FC<{
hideHistory,
hideAccount,
hideAnnouncement,
setApiConfiguration: (value) =>
setState((prevState) => ({
...prevState,
apiConfiguration: value,
})),
setTelemetrySetting: (value) =>
setState((prevState) => ({
...prevState,
telemetrySetting: value,
})),
setPlanActSeparateModelsSetting: (value) =>
setState((prevState) => ({
...prevState,
planActSeparateModelsSetting: value,
})),
setEnableCheckpointsSetting: (value) =>
setState((prevState) => ({
...prevState,
enableCheckpointsSetting: value,
})),
setMcpMarketplaceEnabled: (value) =>
setState((prevState) => ({
...prevState,
mcpMarketplaceEnabled: value,
})),
setMcpRichDisplayEnabled: (value) =>
setState((prevState) => ({
...prevState,
mcpRichDisplayEnabled: value,
})),
setMcpResponsesCollapsed: (value) => {
setState((prevState) => ({
...prevState,
mcpResponsesCollapsed: value,
}))
},
setShowAnnouncement,
setShouldShowAnnouncement: (value) =>
setState((prevState) => ({
...prevState,
shouldShowAnnouncement: value,
})),
setShellIntegrationTimeout: (value) =>
setState((prevState) => ({
...prevState,
shellIntegrationTimeout: value,
})),
setTerminalReuseEnabled: (value) =>
setState((prevState) => ({
...prevState,
terminalReuseEnabled: value,
})),
setTerminalOutputLineLimit: (value) =>
setState((prevState) => ({
...prevState,
terminalOutputLineLimit: value,
})),
setDefaultTerminalProfile: (value) =>
setState((prevState) => ({
...prevState,
defaultTerminalProfile: value,
})),
setMcpServers: (mcpServers: McpServer[]) => setMcpServers(mcpServers),
setRequestyModels: (models: Record<string, ModelInfo>) => setRequestyModels(models),
setMcpMarketplaceCatalog: (catalog: McpMarketplaceCatalog) => setMcpMarketplaceCatalog(catalog),
setAvailableTerminalProfiles,
setShowMcp,
closeMcpView,
setChatSettings: async (value) => {
@@ -768,7 +697,7 @@ export const ExtensionStateContextProvider: React.FC<{
planActSeparateModelsSetting: state.planActSeparateModelsSetting,
enableCheckpointsSetting: state.enableCheckpointsSetting,
mcpMarketplaceEnabled: state.mcpMarketplaceEnabled,
mcpRichDisplayEnabled: state.mcpRichDisplayEnabled,
mcpDisplayMode: state.mcpDisplayMode,
mcpResponsesCollapsed: state.mcpResponsesCollapsed,
}),
)
@@ -811,11 +740,6 @@ export const ExtensionStateContextProvider: React.FC<{
refreshOpenRouterModels,
onRelinquishControl,
setUserInfo: (userInfo?: UserInfo) => setState((prevState) => ({ ...prevState, userInfo })),
setBrowserSettings: (value: BrowserSettings) =>
setState((prevState) => ({
...prevState,
browserSettings: value,
})),
}
return <ExtensionStateContext.Provider value={contextValue}>{children}</ExtensionStateContext.Provider>