Date: Tue, 14 Jan 2025 14:51:12 -0600
Subject: [PATCH 052/294] update API pricing for Anthropic, as of 2025-01-02
(#1121)
---
src/shared/api.ts | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/shared/api.ts b/src/shared/api.ts
index 52d974f725..8229d02790 100644
--- a/src/shared/api.ts
+++ b/src/shared/api.ts
@@ -57,7 +57,7 @@ export interface ModelInfo {
}
// Anthropic
-// https://docs.anthropic.com/en/docs/about-claude/models
+// https://docs.anthropic.com/en/docs/about-claude/models // prices updated 2025-01-02
export type AnthropicModelId = keyof typeof anthropicModels
export const anthropicDefaultModelId: AnthropicModelId = "claude-3-5-sonnet-20241022"
export const anthropicModels = {
@@ -77,10 +77,10 @@ export const anthropicModels = {
contextWindow: 200_000,
supportsImages: false,
supportsPromptCache: true,
- inputPrice: 1.0,
- outputPrice: 5.0,
- cacheWritesPrice: 1.25,
- cacheReadsPrice: 0.1,
+ inputPrice: 0.8,
+ outputPrice: 4.0,
+ cacheWritesPrice: 1.0,
+ cacheReadsPrice: 0.08,
},
"claude-3-opus-20240229": {
maxTokens: 4096,
From 51e218c81a8bb97c88f4286d715653b127b316ce Mon Sep 17 00:00:00 2001
From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Date: Tue, 14 Jan 2025 17:25:25 -0800
Subject: [PATCH 053/294] Revert "Revert "Fix the chat context menu removing
UTF8 characters causing pure UTF8 character filenames not to display in the
menu (#1145)""
This reverts commit e0b90b2ea552a2b53ffd3bb4677aea17a01c6d64.
---
webview-ui/src/components/chat/ChatRow.tsx | 4 ++--
webview-ui/src/components/chat/ContextMenu.tsx | 4 ++--
webview-ui/src/components/common/CodeAccordian.tsx | 4 ++--
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx
index fc3c32e66b..edbb1e147a 100644
--- a/webview-ui/src/components/chat/ChatRow.tsx
+++ b/webview-ui/src/components/chat/ChatRow.tsx
@@ -16,7 +16,7 @@ import { useExtensionState } from "../../context/ExtensionStateContext"
import { findMatchingResourceOrTemplate } from "../../utils/mcp"
import { vscode } from "../../utils/vscode"
import { CheckpointControls, CheckpointOverlay } from "../common/CheckpointControls"
-import CodeAccordian, { removeLeadingNonAlphanumeric } from "../common/CodeAccordian"
+import CodeAccordian, { cleanPathPrefix } from "../common/CodeAccordian"
import CodeBlock, { CODE_BLOCK_BG_COLOR } from "../common/CodeBlock"
import MarkdownBlock from "../common/MarkdownBlock"
import SuccessButton from "../common/SuccessButton"
@@ -427,7 +427,7 @@ export const ChatRowContent = ({ message, isExpanded, onToggleExpand, lastModifi
direction: "rtl",
textAlign: "left",
}}>
- {removeLeadingNonAlphanumeric(tool.path ?? "") + "\u200E"}
+ {cleanPathPrefix(tool.path ?? "") + "\u200E"}
void
@@ -67,7 +67,7 @@ const ContextMenu: React.FC = ({
direction: "rtl",
textAlign: "left",
}}>
- {removeLeadingNonAlphanumeric(option.value || "") + "\u200E"}
+ {cleanPathPrefix(option.value || "") + "\u200E"}
>
)
diff --git a/webview-ui/src/components/common/CodeAccordian.tsx b/webview-ui/src/components/common/CodeAccordian.tsx
index 36f8fbc1f9..cb0c02bb42 100644
--- a/webview-ui/src/components/common/CodeAccordian.tsx
+++ b/webview-ui/src/components/common/CodeAccordian.tsx
@@ -20,7 +20,7 @@ We need to remove leading non-alphanumeric characters from the path in order for
[^a-zA-Z0-9]+: Matches one or more characters that are not alphanumeric.
The replace method removes these matched characters, effectively trimming the string up to the first alphanumeric character.
*/
-export const removeLeadingNonAlphanumeric = (path: string): string => path.replace(/^[^a-zA-Z0-9]+/, "")
+export const cleanPathPrefix = (path: string): string => path.replace(/^[^\u4e00-\u9fa5a-zA-Z0-9]+/, "")
const CodeAccordian = ({
code,
@@ -90,7 +90,7 @@ const CodeAccordian = ({
direction: "rtl",
textAlign: "left",
}}>
- {removeLeadingNonAlphanumeric(path ?? "") + "\u200E"}
+ {cleanPathPrefix(path ?? "") + "\u200E"}
>
)}
From ee9c865cd43a16341ec8c261e60581d8275fc15b Mon Sep 17 00:00:00 2001
From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Date: Tue, 14 Jan 2025 17:54:46 -0800
Subject: [PATCH 054/294] Prepare for release
---
CHANGELOG.md | 6 ++++++
README.md | 2 +-
package.json | 4 ++--
3 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3e2f8e1d2e..ecf9e31d6f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# Change Log
+## [3.1.6]
+
+- Fix bug where filepaths with Chinese characters would not show up in context mention menu (thanks @chi-chat!)
+- Add timestamp to prompts to help with certain MCP servers that need the current time (thanks @MrUbens!)
+- Update Anthropic model prices (thanks @timoteostewart!)
+
## [3.1.5]
- Fix bug where Cline couldn't read "@/" import path aliases from tool results
diff --git a/README.md b/README.md
index 3617732b80..8da668259e 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Cline (prev. Claude Dev) – \#1 on OpenRouter
+# Cline – \#1 on OpenRouter
diff --git a/package.json b/package.json
index 78d96b5367..be2fe79e39 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
{
"name": "claude-dev",
- "displayName": "Cline (prev. Claude Dev)",
+ "displayName": "Cline",
"description": "Autonomous coding agent right in your IDE, capable of creating/editing files, running commands, using the browser, and more with your permission every step of the way.",
- "version": "3.1.5",
+ "version": "3.1.6",
"icon": "assets/icons/icon.png",
"galleryBanner": {
"color": "#617A91",
From 7d1830f90b7127ff3a9a17d091cc6a5a8585d419 Mon Sep 17 00:00:00 2001
From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Date: Wed, 15 Jan 2025 16:54:11 -0800
Subject: [PATCH 055/294] Fix formatting
---
docs/PRIVACY.md | 93 ++++++++++---------
docs/getting-started-new-coders/README.md | 2 +-
docs/mcp/mcp-quickstart.md | 2 +-
.../cline-memory-bank.md | 2 +-
4 files changed, 54 insertions(+), 45 deletions(-)
diff --git a/docs/PRIVACY.md b/docs/PRIVACY.md
index 5052c8cb54..548948b359 100644
--- a/docs/PRIVACY.md
+++ b/docs/PRIVACY.md
@@ -4,69 +4,76 @@ Cline Bot Inc. ("Cline," "we," "our," and/or "us") values the privacy of individ
## Key Points
-- Cline operates entirely client-side as a VS Code extension
-- No code or data is collected, stored, or transmitted to Cline's servers
-- Your data is only sent to your chosen AI provider (e.g., Anthropic, OpenAI) when you explicitly request assistance
-- All processing happens locally on your machine
-- API keys are stored securely in VS Code's built-in settings storage
+- Cline operates entirely client-side as a VS Code extension
+- No code or data is collected, stored, or transmitted to Cline's servers
+- Your data is only sent to your chosen AI provider (e.g., Anthropic, OpenAI) when you explicitly request assistance
+- All processing happens locally on your machine
+- API keys are stored securely in VS Code's built-in settings storage
## Information We Process
### A. Information You Provide
-- **API Keys**: When you choose to use certain AI model providers (OpenRouter, Anthropic, OpenAI, etc.), you provide API keys. These are stored securely and locally in your VS Code settings.
-- **Communications**: If you contact us directly (e.g., via Discord or email), we may receive information like your name, email address, and message contents.
+
+- **API Keys**: When you choose to use certain AI model providers (OpenRouter, Anthropic, OpenAI, etc.), you provide API keys. These are stored securely and locally in your VS Code settings.
+- **Communications**: If you contact us directly (e.g., via Discord or email), we may receive information like your name, email address, and message contents.
### B. Information Processing
Cline functions solely as a client-side VS Code extension that facilitates communication between your editor and your chosen AI model provider:
-1. **File Contents**:
- - Only sent to your chosen AI provider when you explicitly request assistance
- - Never stored or transmitted to Cline's servers
- - Only the specific files/content you select are included
+1. **File Contents**:
-2. **Terminal Commands**:
- - Processed entirely locally on your machine
- - Require explicit user confirmation before execution
- - No command history is transmitted to Cline
+ - Only sent to your chosen AI provider when you explicitly request assistance
+ - Never stored or transmitted to Cline's servers
+ - Only the specific files/content you select are included
-3. **Browser Integration**:
- - Screenshots and console logs are processed locally
- - Temporary data is cleared after task completion
+2. **Terminal Commands**:
+
+ - Processed entirely locally on your machine
+ - Require explicit user confirmation before execution
+ - No command history is transmitted to Cline
+
+3. **Browser Integration**:
+ - Screenshots and console logs are processed locally
+ - Temporary data is cleared after task completion
## Data Security
1. **Local-Only Processing**:
- - All operations happen on your local machine
- - No central servers or data collection
- - No telemetry or usage statistics gathered
- - No account creation required
+
+ - All operations happen on your local machine
+ - No central servers or data collection
+ - No telemetry or usage statistics gathered
+ - No account creation required
2. **API Key Security**:
- - Stored using VS Code's secure settings storage system
- - Never transmitted to Cline's servers
- - You can remove/modify keys at any time
+
+ - Stored using VS Code's secure settings storage system
+ - Never transmitted to Cline's servers
+ - You can remove/modify keys at any time
3. **User Control**:
- - Explicit approval required for file changes
- - Terminal commands require confirmation
- - Browser actions need explicit permission
- - You control which AI provider to use
+ - Explicit approval required for file changes
+ - Terminal commands require confirmation
+ - Browser actions need explicit permission
+ - You control which AI provider to use
## Communication with AI Providers
When you request assistance:
+
1. Selected content is sent directly to your chosen AI provider
2. No data passes through Cline's servers
3. Provider's own privacy policy applies to this communication:
- - [Anthropic Privacy Policy](https://www.anthropic.com/privacy)
- - [OpenAI Privacy Policy](https://openai.com/privacy)
- - [OpenRouter Privacy Policy](https://openrouter.ai/privacy)
+ - [Anthropic Privacy Policy](https://www.anthropic.com/privacy)
+ - [OpenAI Privacy Policy](https://openai.com/privacy)
+ - [OpenRouter Privacy Policy](https://openrouter.ai/privacy)
## Error Handling & Debugging
-- Error logs are processed locally
-- No automatic error reporting to Cline
-- You control what information to include when reporting issues
+
+- Error logs are processed locally
+- No automatic error reporting to Cline
+- You control what information to include when reporting issues
## Children's Privacy
@@ -77,14 +84,16 @@ We do not knowingly collect, maintain, or use personal information from children
We will post any changes to this policy on our GitHub repository. Significant changes will be announced in our Discord community.
## Security Concerns & Auditing
-- Cline is open source and available for security audit
-- Our client-side architecture ensures no central point of data collection
-- You can inspect exactly what data is being sent to AI providers
-- Enterprise users can implement additional access controls through VS Code
+
+- Cline is open source and available for security audit
+- Our client-side architecture ensures no central point of data collection
+- You can inspect exactly what data is being sent to AI providers
+- Enterprise users can implement additional access controls through VS Code
## Contact Us
For privacy-related questions or concerns:
-- Open an issue on our [GitHub repository](https://github.com/cline/cline)
-- Join our [Discord community](https://discord.gg/cline)
-- Email: support@cline.bot
\ No newline at end of file
+
+- Open an issue on our [GitHub repository](https://github.com/cline/cline)
+- Join our [Discord community](https://discord.gg/cline)
+- Email: support@cline.bot
diff --git a/docs/getting-started-new-coders/README.md b/docs/getting-started-new-coders/README.md
index c0779f9fa3..40d575197c 100644
--- a/docs/getting-started-new-coders/README.md
+++ b/docs/getting-started-new-coders/README.md
@@ -21,7 +21,7 @@ Before you begin, make sure you have the following:
- Example: `Documents/Cline/portfolio-website` for your portfolio
- **Cline Extension in VS Code:** The Cline extension installed in VS Code.
-- Here's a [tutorial](https://www.youtube.com/watch?v=N4td-fKhsOQ) on everything you need to get started.
+- Here's a [tutorial](https://www.youtube.com/watch?v=N4td-fKhsOQ) on everything you need to get started.
## Step-by-Step Setup
diff --git a/docs/mcp/mcp-quickstart.md b/docs/mcp/mcp-quickstart.md
index 1c3bd0cbae..a62d5e7a47 100644
--- a/docs/mcp/mcp-quickstart.md
+++ b/docs/mcp/mcp-quickstart.md
@@ -35,7 +35,7 @@ STOP! Before proceeding, you MUST verify these requirements:
1. From the Cline extension, click the `MCP Server` tab
1. Click the `Edit MCP Settings` button
-
+
1. The MCP settings files should be display in a tab in VS Code.
1. Replce the file's contents with this code:
diff --git a/docs/prompting/custom instructions library/cline-memory-bank.md b/docs/prompting/custom instructions library/cline-memory-bank.md
index 0fb9a6a8e0..a368a74be0 100644
--- a/docs/prompting/custom instructions library/cline-memory-bank.md
+++ b/docs/prompting/custom instructions library/cline-memory-bank.md
@@ -122,4 +122,4 @@ When user says "update memory bank":
4. Complete current task
Remember: After every memory reset, you begin completely fresh. Your only link to previous work is the Memory Bank. Maintain it as if your functionality depends on it - because it does.
-```
\ No newline at end of file
+```
From 52582c44b144317f63d51676c02d92e6461969de Mon Sep 17 00:00:00 2001
From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Date: Wed, 15 Jan 2025 16:56:17 -0800
Subject: [PATCH 056/294] Remove timestamp context
---
CHANGELOG.md | 1 -
src/core/Cline.ts | 28 ++++++++++++++--------------
2 files changed, 14 insertions(+), 15 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ecf9e31d6f..b8bb61b9ac 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,6 @@
## [3.1.6]
- Fix bug where filepaths with Chinese characters would not show up in context mention menu (thanks @chi-chat!)
-- Add timestamp to prompts to help with certain MCP servers that need the current time (thanks @MrUbens!)
- Update Anthropic model prices (thanks @timoteostewart!)
## [3.1.5]
diff --git a/src/core/Cline.ts b/src/core/Cline.ts
index 2bf7218222..f53dbb7260 100644
--- a/src/core/Cline.ts
+++ b/src/core/Cline.ts
@@ -3149,20 +3149,20 @@ export class Cline {
}
// Add current time information with timezone
- const now = new Date()
- const formatter = new Intl.DateTimeFormat(undefined, {
- year: "numeric",
- month: "numeric",
- day: "numeric",
- hour: "numeric",
- minute: "numeric",
- second: "numeric",
- hour12: true,
- })
- const timeZone = formatter.resolvedOptions().timeZone
- const timeZoneOffset = -now.getTimezoneOffset() / 60 // Convert to hours and invert sign to match conventional notation
- const timeZoneOffsetStr = `${timeZoneOffset >= 0 ? "+" : ""}${timeZoneOffset}:00`
- details += `\n\n# Current Time\n${formatter.format(now)} (${timeZone}, UTC${timeZoneOffsetStr})`
+ // const now = new Date()
+ // const formatter = new Intl.DateTimeFormat(undefined, {
+ // year: "numeric",
+ // month: "numeric",
+ // day: "numeric",
+ // hour: "numeric",
+ // minute: "numeric",
+ // second: "numeric",
+ // hour12: true,
+ // })
+ // const timeZone = formatter.resolvedOptions().timeZone
+ // const timeZoneOffset = -now.getTimezoneOffset() / 60 // Convert to hours and invert sign to match conventional notation
+ // const timeZoneOffsetStr = `${timeZoneOffset >= 0 ? "+" : ""}${timeZoneOffset}:00`
+ // details += `\n\n# Current Time\n${formatter.format(now)} (${timeZone}, UTC${timeZoneOffsetStr})`
if (includeFileDetails) {
details += `\n\n# Current Working Directory (${cwd.toPosix()}) Files\n`
From e35d69d1246e72194f9c75032317d3e66e9f5de2 Mon Sep 17 00:00:00 2001
From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Date: Wed, 15 Jan 2025 19:04:36 -0800
Subject: [PATCH 057/294] Fix bug where continuing task with context mention
wouldnt pull file contents
---
src/core/Cline.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/core/Cline.ts b/src/core/Cline.ts
index f53dbb7260..f4d1bb5ae9 100644
--- a/src/core/Cline.ts
+++ b/src/core/Cline.ts
@@ -3019,7 +3019,8 @@ export class Cline {
if (
block.text.includes("") ||
block.text.includes("") ||
- block.text.includes("")
+ block.text.includes("") ||
+ block.text.includes("")
) {
return {
...block,
From 67786ada499d810c1428c8f304b3a5070ecd175f Mon Sep 17 00:00:00 2001
From: Evan
Date: Thu, 16 Jan 2025 14:08:03 +0800
Subject: [PATCH 058/294] reuse existing non-busy terminals
---
src/integrations/terminal/TerminalManager.ts | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/src/integrations/terminal/TerminalManager.ts b/src/integrations/terminal/TerminalManager.ts
index 81e91ab6b8..eb640b8c9a 100644
--- a/src/integrations/terminal/TerminalManager.ts
+++ b/src/integrations/terminal/TerminalManager.ts
@@ -157,8 +157,10 @@ export class TerminalManager {
}
async getOrCreateTerminal(cwd: string): Promise {
+ const terminals = TerminalRegistry.getAllTerminals()
+
// Find available terminal from our pool first (created for this task)
- const availableTerminal = TerminalRegistry.getAllTerminals().find((t) => {
+ const matchingTerminal = terminals.find((t) => {
if (t.busy) {
return false
}
@@ -168,11 +170,21 @@ export class TerminalManager {
}
return arePathsEqual(vscode.Uri.file(cwd).fsPath, terminalCwd.fsPath)
})
+ if (matchingTerminal) {
+ this.terminalIds.add(matchingTerminal.id)
+ return matchingTerminal
+ }
+
+ // If no matching terminal exists, try to find any non-busy terminal
+ const availableTerminal = terminals.find((t) => !t.busy)
if (availableTerminal) {
+ // Navigate back to the desired directory
+ await this.runCommand(availableTerminal, `cd "${cwd}"`)
this.terminalIds.add(availableTerminal.id)
return availableTerminal
}
+ // If all terminals are busy, create a new one
const newTerminalInfo = TerminalRegistry.createTerminal(cwd)
this.terminalIds.add(newTerminalInfo.id)
return newTerminalInfo
From 699ae18a7f60e2ccb91da83c7458d917fd5475ba Mon Sep 17 00:00:00 2001
From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Date: Wed, 15 Jan 2025 22:08:35 -0800
Subject: [PATCH 059/294] Add browser settings to change headless mode and size
---
package-lock.json | 4 +-
src/core/Cline.ts | 20 +-
src/core/prompts/system.ts | 6 +-
src/core/webview/ClineProvider.ts | 37 ++-
src/services/browser/BrowserSession.ts | 99 +++++++-
src/shared/BrowserSettings.ts | 27 ++
src/shared/ExtensionMessage.ts | 2 +
src/shared/WebviewMessage.ts | 4 +
.../browser/BrowserSettingsMenu.tsx | 235 ++++++++++++++++++
.../src/components/chat/BrowserSessionRow.tsx | 66 +++--
.../src/context/ExtensionStateContext.tsx | 2 +
11 files changed, 462 insertions(+), 40 deletions(-)
create mode 100644 src/shared/BrowserSettings.ts
create mode 100644 webview-ui/src/components/browser/BrowserSettingsMenu.tsx
diff --git a/package-lock.json b/package-lock.json
index 42e106c3bc..5621c32950 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "claude-dev",
- "version": "3.0.12",
+ "version": "3.1.6",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "claude-dev",
- "version": "3.0.12",
+ "version": "3.1.6",
"license": "Apache-2.0",
"dependencies": {
"@anthropic-ai/bedrock-sdk": "^0.10.2",
diff --git a/src/core/Cline.ts b/src/core/Cline.ts
index f4d1bb5ae9..5b4204105d 100644
--- a/src/core/Cline.ts
+++ b/src/core/Cline.ts
@@ -56,6 +56,7 @@ import { fixModelHtmlEscaping } from "../utils/string"
import { OpenAiHandler } from "../api/providers/openai"
import CheckpointTracker from "../integrations/checkpoints/CheckpointTracker"
import getFolderSize from "get-folder-size"
+import { BrowserSettings } from "../shared/BrowserSettings"
const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0) ?? path.join(os.homedir(), "Desktop") // may or may not exist but fs checking existence would immediately ask for permission which would be bad UX, need to come up with a better solution
@@ -69,10 +70,11 @@ export class Cline {
api: ApiHandler
private terminalManager: TerminalManager
private urlContentFetcher: UrlContentFetcher
- private browserSession: BrowserSession
+ browserSession: BrowserSession
private didEditFile: boolean = false
customInstructions?: string
autoApprovalSettings: AutoApprovalSettings
+ private browserSettings: BrowserSettings
apiConversationHistory: Anthropic.MessageParam[] = []
clineMessages: ClineMessage[] = []
private askResponse?: ClineAskResponse
@@ -107,6 +109,7 @@ export class Cline {
provider: ClineProvider,
apiConfiguration: ApiConfiguration,
autoApprovalSettings: AutoApprovalSettings,
+ browserSettings: BrowserSettings,
customInstructions?: string,
task?: string,
images?: string[],
@@ -116,10 +119,11 @@ export class Cline {
this.api = buildApiHandler(apiConfiguration)
this.terminalManager = new TerminalManager()
this.urlContentFetcher = new UrlContentFetcher(provider.context)
- this.browserSession = new BrowserSession(provider.context)
+ this.browserSession = new BrowserSession(provider.context, browserSettings)
this.diffViewProvider = new DiffViewProvider(cwd)
this.customInstructions = customInstructions
this.autoApprovalSettings = autoApprovalSettings
+ this.browserSettings = browserSettings
if (historyItem) {
this.taskId = historyItem.id
this.conversationHistoryDeletedRange = historyItem.conversationHistoryDeletedRange
@@ -132,6 +136,11 @@ export class Cline {
}
}
+ updateBrowserSettings(browserSettings: BrowserSettings) {
+ this.browserSettings = browserSettings
+ this.browserSession.browserSettings = browserSettings
+ }
+
// Storing task to disk for history
private async ensureTaskDirectoryExists(): Promise {
@@ -1177,7 +1186,12 @@ export class Cline {
throw new Error("MCP hub not available")
}
- let systemPrompt = await SYSTEM_PROMPT(cwd, this.api.getModel().info.supportsComputerUse ?? false, mcpHub)
+ let systemPrompt = await SYSTEM_PROMPT(
+ cwd,
+ this.api.getModel().info.supportsComputerUse ?? false,
+ mcpHub,
+ this.browserSettings,
+ )
let settingsCustomInstructions = this.customInstructions?.trim()
const clineRulesFilePath = path.resolve(cwd, GlobalFileNames.clineRules)
let clineRulesFileInstructions: string | undefined
diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts
index 1e39799303..d6b0d2ca22 100644
--- a/src/core/prompts/system.ts
+++ b/src/core/prompts/system.ts
@@ -2,11 +2,13 @@ import defaultShell from "default-shell"
import os from "os"
import osName from "os-name"
import { McpHub } from "../../services/mcp/McpHub"
+import { BrowserSettings } from "../../shared/BrowserSettings"
export const SYSTEM_PROMPT = async (
cwd: string,
supportsComputerUse: boolean,
mcpHub: McpHub,
+ browserSettings: BrowserSettings,
) => `You are Cline, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.
====
@@ -143,7 +145,7 @@ Usage:
Description: Request to interact with a Puppeteer-controlled browser. Every action, except \`close\`, will be responded to with a screenshot of the browser's current state, along with any new console logs. You may only perform one browser action per message, and wait for the user's response including a screenshot and logs to determine the next action.
- The sequence of actions **must always start with** launching the browser at a URL, and **must always end with** closing the browser. If you need to visit a new URL that is not possible to navigate to from the current webpage, you must first close the browser, then launch again at the new URL.
- While the browser is active, only the \`browser_action\` tool can be used. No other tools should be called during this time. You may proceed to use other tools only after closing the browser. For example if you run into an error and need to fix a file, you must close the browser, then use other tools to make the necessary changes, then re-launch the browser to verify the result.
-- The browser window has a resolution of **900x600** pixels. When performing any click actions, ensure the coordinates are within this resolution range.
+- The browser window has a resolution of **${browserSettings.viewport.width}x${browserSettings.viewport.height}** pixels. When performing any click actions, ensure the coordinates are within this resolution range.
- Before clicking on any elements such as icons, links, or buttons, you must consult the provided screenshot of the page to determine the coordinates of the element. The click should be targeted at the **center of the element**, not on its edges.
Parameters:
- action: (required) The action to perform. The available actions are:
@@ -161,7 +163,7 @@ Parameters:
- Example: \`close\`
- url: (optional) Use this for providing the URL for the \`launch\` action.
* Example: https://example.com
-- coordinate: (optional) The X and Y coordinates for the \`click\` action. Coordinates should be within the **900x600** resolution.
+- coordinate: (optional) The X and Y coordinates for the \`click\` action. Coordinates should be within the **${browserSettings.viewport.width}x${browserSettings.viewport.height}** resolution.
* Example: 450,300
- text: (optional) Use this for providing the text for the \`type\` action.
* Example: Hello, world!
diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts
index 5aec8adc9b..e2a1d7f453 100644
--- a/src/core/webview/ClineProvider.ts
+++ b/src/core/webview/ClineProvider.ts
@@ -23,6 +23,7 @@ import { openMention } from "../mentions"
import { getNonce } from "./getNonce"
import { getUri } from "./getUri"
import { AutoApprovalSettings, DEFAULT_AUTO_APPROVAL_SETTINGS } from "../../shared/AutoApprovalSettings"
+import { BrowserSettings, DEFAULT_BROWSER_SETTINGS } from "../../shared/BrowserSettings"
/*
https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts
@@ -61,6 +62,7 @@ type GlobalStateKey =
| "openRouterModelId"
| "openRouterModelInfo"
| "autoApprovalSettings"
+ | "browserSettings"
export const GlobalFileNames = {
apiConversationHistory: "api_conversation_history.json",
@@ -210,17 +212,18 @@ export class ClineProvider implements vscode.WebviewViewProvider {
async initClineWithTask(task?: string, images?: string[]) {
await this.clearTask() // ensures that an exising task doesn't exist before starting a new one, although this shouldn't be possible since user must clear task before starting a new one
- const { apiConfiguration, customInstructions, autoApprovalSettings } = await this.getState()
- this.cline = new Cline(this, apiConfiguration, autoApprovalSettings, customInstructions, task, images)
+ const { apiConfiguration, customInstructions, autoApprovalSettings, browserSettings } = await this.getState()
+ this.cline = new Cline(this, apiConfiguration, autoApprovalSettings, browserSettings, customInstructions, task, images)
}
async initClineWithHistoryItem(historyItem: HistoryItem) {
await this.clearTask()
- const { apiConfiguration, customInstructions, autoApprovalSettings } = await this.getState()
+ const { apiConfiguration, customInstructions, autoApprovalSettings, browserSettings } = await this.getState()
this.cline = new Cline(
this,
apiConfiguration,
autoApprovalSettings,
+ browserSettings,
customInstructions,
undefined,
undefined,
@@ -436,6 +439,20 @@ export class ClineProvider implements vscode.WebviewViewProvider {
await this.postStateToWebview()
}
break
+ case "browserSettings":
+ if (message.browserSettings) {
+ await this.updateGlobalState("browserSettings", message.browserSettings)
+ if (this.cline) {
+ this.cline.updateBrowserSettings(message.browserSettings)
+ }
+ await this.postStateToWebview()
+ }
+ break
+ // case "relaunchChromeDebugMode":
+ // if (this.cline) {
+ // this.cline.browserSession.relaunchChromeDebugMode()
+ // }
+ // break
case "askResponse":
this.cline?.handleWebviewAskResponse(message.askResponse!, message.text, message.images)
break
@@ -908,8 +925,14 @@ export class ClineProvider implements vscode.WebviewViewProvider {
}
async getStateToPostToWebview(): Promise {
- const { apiConfiguration, lastShownAnnouncementId, customInstructions, taskHistory, autoApprovalSettings } =
- await this.getState()
+ const {
+ apiConfiguration,
+ lastShownAnnouncementId,
+ customInstructions,
+ taskHistory,
+ autoApprovalSettings,
+ browserSettings,
+ } = await this.getState()
return {
version: this.context.extension?.packageJSON?.version ?? "",
apiConfiguration,
@@ -921,6 +944,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
taskHistory: (taskHistory || []).filter((item) => item.ts && item.task).sort((a, b) => b.ts - a.ts),
shouldShowAnnouncement: lastShownAnnouncementId !== this.latestAnnouncementId,
autoApprovalSettings,
+ browserSettings,
}
}
@@ -1006,6 +1030,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
customInstructions,
taskHistory,
autoApprovalSettings,
+ browserSettings,
] = await Promise.all([
this.getGlobalState("apiProvider") as Promise,
this.getGlobalState("apiModelId") as Promise,
@@ -1036,6 +1061,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
this.getGlobalState("customInstructions") as Promise,
this.getGlobalState("taskHistory") as Promise,
this.getGlobalState("autoApprovalSettings") as Promise,
+ this.getGlobalState("browserSettings") as Promise,
])
let apiProvider: ApiProvider
@@ -1084,6 +1110,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
customInstructions,
taskHistory,
autoApprovalSettings: autoApprovalSettings || DEFAULT_AUTO_APPROVAL_SETTINGS, // default value can be 0 or empty string
+ browserSettings: browserSettings || DEFAULT_BROWSER_SETTINGS,
}
}
diff --git a/src/services/browser/BrowserSession.ts b/src/services/browser/BrowserSession.ts
index 0b7b0961b5..cbc7734d36 100644
--- a/src/services/browser/BrowserSession.ts
+++ b/src/services/browser/BrowserSession.ts
@@ -8,20 +8,26 @@ import pWaitFor from "p-wait-for"
import delay from "delay"
import { fileExistsAtPath } from "../../utils/fs"
import { BrowserActionResult } from "../../shared/ExtensionMessage"
+import { BrowserSettings } from "../../shared/BrowserSettings"
+// import * as chromeLauncher from "chrome-launcher"
interface PCRStats {
puppeteer: { launch: typeof launch }
executablePath: string
}
+// const DEBUG_PORT = 9222 // Chrome's default debugging port
+
export class BrowserSession {
private context: vscode.ExtensionContext
private browser?: Browser
private page?: Page
private currentMousePosition?: string
+ browserSettings: BrowserSettings
- constructor(context: vscode.ExtensionContext) {
+ constructor(context: vscode.ExtensionContext, browserSettings: BrowserSettings) {
this.context = context
+ this.browserSettings = browserSettings
}
private async ensureChromiumExists(): Promise {
@@ -45,6 +51,70 @@ export class BrowserSession {
return stats
}
+ // private async checkExistingChromeDebugger(): Promise {
+ // try {
+ // // Try to connect to existing debugger
+ // const response = await fetch(`http://localhost:${DEBUG_PORT}/json/version`)
+ // return response.ok
+ // } catch {
+ // return false
+ // }
+ // }
+
+ // async relaunchChromeDebugMode() {
+ // const result = await vscode.window.showWarningMessage(
+ // "This will close your existing Chrome tabs and relaunch Chrome in debug mode. Are you sure?",
+ // { modal: true },
+ // "Yes",
+ // )
+
+ // if (result !== "Yes") {
+ // return
+ // }
+
+ // // // Kill any existing Chrome instances
+ // // await chromeLauncher.killAll()
+
+ // // // Launch Chrome with debug port
+ // // const launcher = new chromeLauncher.Launcher({
+ // // port: DEBUG_PORT,
+ // // chromeFlags: ["--remote-debugging-port=" + DEBUG_PORT, "--no-first-run", "--no-default-browser-check"],
+ // // })
+
+ // // await launcher.launch()
+ // const installation = chromeLauncher.Launcher.getFirstInstallation()
+ // if (!installation) {
+ // throw new Error("Could not find Chrome installation on this system")
+ // }
+ // console.log("chrome installation", installation)
+ // }
+
+ // private async getSystemChromeExecutablePath(): Promise {
+ // // Find installed Chrome
+ // const installation = chromeLauncher.Launcher.getFirstInstallation()
+ // if (!installation) {
+ // throw new Error("Could not find Chrome installation on this system")
+ // }
+ // console.log("chrome installation", installation)
+ // return installation
+ // }
+
+ // /**
+ // * Helper to detect user’s default Chrome data dir.
+ // * Adjust for OS if needed.
+ // */
+ // private getDefaultChromeUserDataDir(): string {
+ // const homedir = require("os").homedir()
+ // switch (process.platform) {
+ // case "win32":
+ // return path.join(homedir, "AppData", "Local", "Google", "Chrome", "User Data")
+ // case "darwin":
+ // return path.join(homedir, "Library", "Application Support", "Google", "Chrome")
+ // default:
+ // return path.join(homedir, ".config", "google-chrome")
+ // }
+ // }
+
async launchBrowser() {
console.log("launch browser called")
if (this.browser) {
@@ -58,12 +128,29 @@ export class BrowserSession {
"--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",
],
executablePath: stats.executablePath,
- defaultViewport: {
- width: 900,
- height: 600,
- },
- // headless: false,
+ defaultViewport: this.browserSettings.viewport,
+ headless: this.browserSettings.headless,
})
+
+ // if (this.browserSettings.chromeType === "system") {
+ // const userDataDir = this.getDefaultChromeUserDataDir()
+ // this.browser = await stats.puppeteer.launch({
+ // args: [`--user-data-dir=${userDataDir}`, "--profile-directory=Default"],
+ // executablePath: await this.getSystemChromeExecutablePath(),
+ // defaultViewport: this.browserSettings.viewport,
+ // headless: this.browserSettings.headless,
+ // })
+ // } else {
+ // this.browser = await stats.puppeteer.launch({
+ // args: [
+ // "--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",
+ // ],
+ // executablePath: stats.executablePath,
+ // defaultViewport: this.browserSettings.viewport,
+ // headless: this.browserSettings.headless,
+ // })
+ // }
+
// (latest version of puppeteer does not add headless to user agent)
this.page = await this.browser?.newPage()
}
diff --git a/src/shared/BrowserSettings.ts b/src/shared/BrowserSettings.ts
new file mode 100644
index 0000000000..e4a2f40d75
--- /dev/null
+++ b/src/shared/BrowserSettings.ts
@@ -0,0 +1,27 @@
+export interface BrowserSettings {
+ // Viewport size settings
+ viewport: {
+ width: number
+ height: number
+ }
+ // Browser mode settings
+ headless: boolean
+ // Chrome installation to use
+ // chromeType: "chromium" | "system"
+}
+
+export const DEFAULT_BROWSER_SETTINGS: BrowserSettings = {
+ viewport: {
+ width: 900,
+ height: 600,
+ },
+ headless: true,
+ // chromeType: "chromium",
+}
+
+export const BROWSER_VIEWPORT_PRESETS = {
+ "Large Desktop (1280x800)": { width: 1280, height: 800 },
+ "Small Desktop (900x600)": { width: 900, height: 600 },
+ "Tablet (768x1024)": { width: 768, height: 1024 },
+ "Mobile (360x640)": { width: 360, height: 640 },
+} as const
diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts
index e4760282a5..fe5584c54d 100644
--- a/src/shared/ExtensionMessage.ts
+++ b/src/shared/ExtensionMessage.ts
@@ -2,6 +2,7 @@
import { ApiConfiguration, ModelInfo } from "./api"
import { AutoApprovalSettings } from "./AutoApprovalSettings"
+import { BrowserSettings } from "./BrowserSettings"
import { HistoryItem } from "./HistoryItem"
import { McpServer } from "./mcp"
@@ -44,6 +45,7 @@ export interface ExtensionState {
taskHistory: HistoryItem[]
shouldShowAnnouncement: boolean
autoApprovalSettings: AutoApprovalSettings
+ browserSettings: BrowserSettings
}
export interface ClineMessage {
diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts
index 419306ef6c..4fa90b3e36 100644
--- a/src/shared/WebviewMessage.ts
+++ b/src/shared/WebviewMessage.ts
@@ -1,5 +1,6 @@
import { ApiConfiguration } from "./api"
import { AutoApprovalSettings } from "./AutoApprovalSettings"
+import { BrowserSettings } from "./BrowserSettings"
export interface WebviewMessage {
type:
@@ -26,9 +27,11 @@ export interface WebviewMessage {
| "openMcpSettings"
| "restartMcpServer"
| "autoApprovalSettings"
+ | "browserSettings"
| "checkpointDiff"
| "checkpointRestore"
| "taskCompletionViewChanges"
+ // | "relaunchChromeDebugMode"
text?: string
askResponse?: ClineAskResponse
apiConfiguration?: ApiConfiguration
@@ -36,6 +39,7 @@ export interface WebviewMessage {
bool?: boolean
number?: number
autoApprovalSettings?: AutoApprovalSettings
+ browserSettings?: BrowserSettings
}
export type ClineAskResponse = "yesButtonClicked" | "noButtonClicked" | "messageResponse"
diff --git a/webview-ui/src/components/browser/BrowserSettingsMenu.tsx b/webview-ui/src/components/browser/BrowserSettingsMenu.tsx
new file mode 100644
index 0000000000..092cc19af9
--- /dev/null
+++ b/webview-ui/src/components/browser/BrowserSettingsMenu.tsx
@@ -0,0 +1,235 @@
+import { VSCodeButton, VSCodeCheckbox, VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react"
+import React, { useRef, useState } from "react"
+import { useClickAway } from "react-use"
+import styled from "styled-components"
+import { BROWSER_VIEWPORT_PRESETS } from "../../../../src/shared/BrowserSettings"
+import { useExtensionState } from "../../context/ExtensionStateContext"
+import { vscode } from "../../utils/vscode"
+import { CODE_BLOCK_BG_COLOR } from "../common/CodeBlock"
+
+interface BrowserSettingsMenuProps {
+ disabled?: boolean
+ maxWidth?: number
+}
+
+export const BrowserSettingsMenu: React.FC = ({ disabled = false, maxWidth }) => {
+ const { browserSettings } = useExtensionState()
+ const [showMenu, setShowMenu] = useState(false)
+ const [hasMouseEntered, setHasMouseEntered] = useState(false)
+ const containerRef = useRef(null)
+ const menuRef = useRef(null)
+
+ useClickAway(containerRef, () => {
+ if (showMenu) {
+ setShowMenu(false)
+ setHasMouseEntered(false)
+ }
+ })
+
+ const handleMouseEnter = () => {
+ setHasMouseEntered(true)
+ }
+
+ const handleMouseLeave = () => {
+ if (hasMouseEntered) {
+ setShowMenu(false)
+ setHasMouseEntered(false)
+ }
+ }
+
+ const handleControlsMouseLeave = (e: React.MouseEvent) => {
+ const menuElement = menuRef.current
+
+ if (menuElement && showMenu) {
+ const menuRect = menuElement.getBoundingClientRect()
+
+ // If mouse is moving towards the menu, don't close it
+ if (
+ e.clientY >= menuRect.top &&
+ e.clientY <= menuRect.bottom &&
+ e.clientX >= menuRect.left &&
+ e.clientX <= menuRect.right
+ ) {
+ return
+ }
+ }
+
+ setShowMenu(false)
+ setHasMouseEntered(false)
+ }
+
+ const handleViewportChange = (event: Event) => {
+ const target = event.target as HTMLSelectElement
+ const selectedSize = BROWSER_VIEWPORT_PRESETS[target.value as keyof typeof BROWSER_VIEWPORT_PRESETS]
+ if (selectedSize) {
+ vscode.postMessage({
+ type: "browserSettings",
+ browserSettings: {
+ ...browserSettings,
+ viewport: selectedSize,
+ },
+ })
+ }
+ }
+
+ const updateHeadless = (headless: boolean) => {
+ vscode.postMessage({
+ type: "browserSettings",
+ browserSettings: {
+ ...browserSettings,
+ headless,
+ },
+ })
+ }
+
+ // const updateChromeType = (chromeType: BrowserSettings["chromeType"]) => {
+ // vscode.postMessage({
+ // type: "browserSettings",
+ // browserSettings: {
+ // ...browserSettings,
+ // chromeType,
+ // },
+ // })
+ // }
+
+ // const relaunchChromeDebugMode = () => {
+ // vscode.postMessage({
+ // type: "relaunchChromeDebugMode",
+ // })
+ // }
+
+ return (
+
+ setShowMenu(!showMenu)} disabled={disabled}>
+
+
+ {showMenu && (
+
+
+ {/* Headless Mode */}
+ updateHeadless((e.target as HTMLInputElement).checked)}>
+ Run in headless mode
+
+ When enabled, Chrome will run in the background.
+
+
+ {/*
+ Chrome Executable
+
+ updateChromeType((e.target as HTMLSelectElement).value as BrowserSettings["chromeType"])
+ }>
+ Chromium (Auto-downloaded)
+ System Chrome
+
+
+ {browserSettings.chromeType === "system" ? (
+ <>
+ Cline will use your personal browser. You must{" "}
+ {
+ e.preventDefault()
+ relaunchChromeDebugMode()
+ }}>
+ relaunch Chrome in debug mode
+ {" "}
+ to use this setting.
+ >
+ ) : (
+ "Cline will use a Chromium browser bundled with the extension."
+ )}
+
+ */}
+
+
+ Viewport Size
+
+ size.width === browserSettings.viewport.width &&
+ size.height === browserSettings.viewport.height,
+ )?.[0]
+ }
+ onChange={(event) => handleViewportChange(event as Event)}>
+ {Object.entries(BROWSER_VIEWPORT_PRESETS).map(([name]) => (
+
+ {name}
+
+ ))}
+
+
+
+ )}
+
+ )
+}
+
+const SettingsMenu = styled.div<{ maxWidth?: number }>`
+ position: absolute;
+ top: calc(100% + 8px);
+ right: -2px;
+ background: ${CODE_BLOCK_BG_COLOR};
+ border: 1px solid var(--vscode-editorGroup-border);
+ padding: 8px;
+ border-radius: 3px;
+ z-index: 1000;
+ width: calc(100vw - 57px);
+ min-width: 0px;
+ max-width: ${(props) => (props.maxWidth ? `${props.maxWidth - 23}px` : "100vw")};
+
+ // Add invisible padding to create a safe hover zone
+ &::before {
+ content: "";
+ position: absolute;
+ top: -14px; // Same as margin-top in the parent's top property
+ left: 0;
+ right: -6px;
+ height: 14px;
+ }
+
+ &::after {
+ content: "";
+ position: absolute;
+ top: -6px;
+ right: 6px;
+ width: 10px;
+ height: 10px;
+ background: ${CODE_BLOCK_BG_COLOR};
+ border-left: 1px solid var(--vscode-editorGroup-border);
+ border-top: 1px solid var(--vscode-editorGroup-border);
+ transform: rotate(45deg);
+ z-index: 1; // Ensure arrow stays above the padding
+ }
+`
+
+const SettingsGroup = styled.div`
+ &:not(:last-child) {
+ margin-bottom: 8px;
+ // padding-bottom: 8px;
+ border-bottom: 1px solid var(--vscode-editorGroup-border);
+ }
+`
+
+const SettingsHeader = styled.div`
+ font-size: 11px;
+ font-weight: 600;
+ margin-bottom: 6px;
+ color: var(--vscode-foreground);
+`
+
+const SettingsDescription = styled.div<{ isLast?: boolean }>`
+ font-size: 11px;
+ color: var(--vscode-descriptionForeground);
+ margin-bottom: ${(props) => (props.isLast ? "0" : "8px")};
+`
+
+export default BrowserSettingsMenu
diff --git a/webview-ui/src/components/chat/BrowserSessionRow.tsx b/webview-ui/src/components/chat/BrowserSessionRow.tsx
index 3c81eac31f..7153cb7f21 100644
--- a/webview-ui/src/components/chat/BrowserSessionRow.tsx
+++ b/webview-ui/src/components/chat/BrowserSessionRow.tsx
@@ -9,6 +9,9 @@ import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
import styled from "styled-components"
import { CheckpointControls, CheckpointOverlay } from "../common/CheckpointControls"
import { findLast } from "../../../../src/shared/array"
+import { BrowserSettingsMenu } from "../browser/BrowserSettingsMenu"
+import { useExtensionState } from "../../context/ExtensionStateContext"
+import { BROWSER_VIEWPORT_PRESETS } from "../../../../src/shared/BrowserSettings"
interface BrowserSessionRowProps {
messages: ClineMessage[]
@@ -21,6 +24,7 @@ interface BrowserSessionRowProps {
const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
const { messages, isLast, onHeightChange, lastModifiedMessage } = props
+ const { browserSettings } = useExtensionState()
const prevHeightRef = useRef(0)
const [maxActionHeight, setMaxActionHeight] = useState(0)
const [consoleLogsExpanded, setConsoleLogsExpanded] = useState(false)
@@ -169,17 +173,19 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
const currentPage = pages[currentPageIndex]
const isLastPage = currentPageIndex === pages.length - 1
+ const defaultMousePosition = `${browserSettings.viewport.width * 0.7},${browserSettings.viewport.height * 0.5}`
+
// Use latest state if we're on the last page and don't have a state yet
const displayState = isLastPage
? {
url: currentPage?.currentState.url || latestState.url || initialUrl,
- mousePosition: currentPage?.currentState.mousePosition || latestState.mousePosition || "700,400",
+ mousePosition: currentPage?.currentState.mousePosition || latestState.mousePosition || defaultMousePosition,
consoleLogs: currentPage?.currentState.consoleLogs,
screenshot: currentPage?.currentState.screenshot || latestState.screenshot,
}
: {
url: currentPage?.currentState.url || initialUrl,
- mousePosition: currentPage?.currentState.mousePosition || "700,400",
+ mousePosition: currentPage?.currentState.mousePosition || defaultMousePosition,
consoleLogs: currentPage?.currentState.consoleLogs,
screenshot: currentPage?.currentState.screenshot,
}
@@ -230,6 +236,14 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
shouldShowCheckpoints = lastModifiedMessage?.ask === "resume_completed_task" || lastModifiedMessage?.ask === "resume_task"
}
+ const shouldShowSettings = useMemo(() => {
+ const lastMessage = messages[messages.length - 1]
+ return lastMessage?.ask === "browser_action_launch" || lastMessage?.say === "browser_action_launch"
+ }, [messages])
+
+ // Calculate maxWidth
+ const maxWidth = browserSettings.viewport.width < BROWSER_VIEWPORT_PRESETS["Small Desktop (900x600)"].width ? 200 : undefined
+
const [browserSessionRow, { height }] = useSize(
{
style={{
borderRadius: 3,
border: "1px solid var(--vscode-editorGroup-border)",
- overflow: "hidden",
+ // overflow: "hidden",
backgroundColor: CODE_BLOCK_BG_COLOR,
- marginBottom: 10,
+ // marginBottom: 10,
+ maxWidth,
+ margin: "0 auto 10px auto", // Center the container
}}>
{/* URL Bar */}
- {displayState.url || "http"}
+
+ {displayState.url || "http"}
+
+
{/* Screenshot Area */}
@@ -338,8 +360,8 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
@@ -355,7 +377,7 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
display: "flex",
alignItems: "center",
gap: "4px",
- width: "100%",
+ // width: "100%",
justifyContent: "flex-start",
cursor: "pointer",
padding: `9px 8px ${consoleLogsExpanded ? 0 : 8}px 8px`,
diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx
index c6bbd04a86..d61eb754d4 100644
--- a/webview-ui/src/context/ExtensionStateContext.tsx
+++ b/webview-ui/src/context/ExtensionStateContext.tsx
@@ -7,6 +7,7 @@ import { findLastIndex } from "../../../src/shared/array"
import { McpServer } from "../../../src/shared/mcp"
import { convertTextMateToHljs } from "../utils/textMateToHljs"
import { vscode } from "../utils/vscode"
+import { DEFAULT_BROWSER_SETTINGS } from "../../../src/shared/BrowserSettings"
interface ExtensionStateContextType extends ExtensionState {
didHydrateState: boolean
@@ -31,6 +32,7 @@ export const ExtensionStateContextProvider: React.FC<{
taskHistory: [],
shouldShowAnnouncement: false,
autoApprovalSettings: DEFAULT_AUTO_APPROVAL_SETTINGS,
+ browserSettings: DEFAULT_BROWSER_SETTINGS,
})
const [didHydrateState, setDidHydrateState] = useState(false)
const [showWelcome, setShowWelcome] = useState(false)
From 0bcfe0275e16a743191b4469de6c9d01229ce7d3 Mon Sep 17 00:00:00 2001
From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Date: Wed, 15 Jan 2025 22:14:00 -0800
Subject: [PATCH 060/294] Prepare for release
---
CHANGELOG.md | 4 ++++
package.json | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b8bb61b9ac..7c9c7eb80d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# Change Log
+## [3.1.7]
+
+- Add ability to change viewport size and headless mode when Cline asks to launch the browser
+
## [3.1.6]
- Fix bug where filepaths with Chinese characters would not show up in context mention menu (thanks @chi-chat!)
diff --git a/package.json b/package.json
index be2fe79e39..a540c7b1c3 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "claude-dev",
"displayName": "Cline",
"description": "Autonomous coding agent right in your IDE, capable of creating/editing files, running commands, using the browser, and more with your permission every step of the way.",
- "version": "3.1.6",
+ "version": "3.1.7",
"icon": "assets/icons/icon.png",
"galleryBanner": {
"color": "#617A91",
From 6302ae0eb2d3608fef85d7109c7549ad080c827f Mon Sep 17 00:00:00 2001
From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Date: Wed, 15 Jan 2025 22:26:03 -0800
Subject: [PATCH 061/294] Add links to reddit
---
README.md | 3 +++
webview-ui/src/components/chat/Announcement.tsx | 8 ++++++--
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 8da668259e..e4181d1253 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,9 @@
Join the Discord
+r/cline
+ |
+
Feature Requests
|
diff --git a/webview-ui/src/components/chat/Announcement.tsx b/webview-ui/src/components/chat/Announcement.tsx
index 5567089226..da4c002e98 100644
--- a/webview-ui/src/components/chat/Announcement.tsx
+++ b/webview-ui/src/components/chat/Announcement.tsx
@@ -120,9 +120,13 @@ const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => {
}}
/>
- Join
+ Join our{" "}
- discord.gg/cline
+ discord
+ {" "}
+ or{" "}
+
+ r/cline
for more updates!
From 33e04c8baa0c1915954055527259e2c3e0ef0297 Mon Sep 17 00:00:00 2001
From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Date: Wed, 15 Jan 2025 22:28:27 -0800
Subject: [PATCH 062/294] Copy
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index e4181d1253..22a9606a42 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@
Download on VS Marketplace
|
-Join the Discord
+Discord
|
r/cline
From ed17085df93c2c77f79d0c79a1d7a642e3919372 Mon Sep 17 00:00:00 2001
From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Date: Wed, 15 Jan 2025 22:28:50 -0800
Subject: [PATCH 063/294] Prepare for release
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index a540c7b1c3..b6c38ac0a3 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "claude-dev",
"displayName": "Cline",
"description": "Autonomous coding agent right in your IDE, capable of creating/editing files, running commands, using the browser, and more with your permission every step of the way.",
- "version": "3.1.7",
+ "version": "3.1.8",
"icon": "assets/icons/icon.png",
"galleryBanner": {
"color": "#617A91",
From 06146d5bd0ac0e874026bda519654f661169be4e Mon Sep 17 00:00:00 2001
From: Takuma TSUJI <61522301+itTkm@users.noreply.github.com>
Date: Fri, 17 Jan 2025 02:54:58 +0900
Subject: [PATCH 064/294] Update installation instructions in CONTRIBUTING.md
(#1287)
---
CONTRIBUTING.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index bdaba1f306..75edd9ed43 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -27,7 +27,7 @@ If you're planning to work on a bigger feature, please create a [feature request
- If you dismissed the prompts, you can install them manually from the Extensions panel
2. **Local Development**
- - Run `npm install` to install dependencies
+ - Run `npm run install:all` to install dependencies
- Run `npm run test` to run tests locally
- Before submitting PR, run `npm run format:fix` to format your code
From 2b1e3f553b996e0a0230b3308c530ccea34246f4 Mon Sep 17 00:00:00 2001
From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Date: Thu, 16 Jan 2025 19:40:27 -0800
Subject: [PATCH 065/294] Add Mistral API provider
---
package-lock.json | 13 ++-
package.json | 1 +
src/api/index.ts | 3 +
src/api/providers/mistral.ts | 74 +++++++++++++++
src/api/transform/mistral-format.ts | 92 +++++++++++++++++++
src/core/webview/ClineProvider.ts | 7 ++
src/shared/api.ts | 17 ++++
.../src/components/settings/ApiOptions.tsx | 37 ++++++++
.../src/context/ExtensionStateContext.tsx | 1 +
webview-ui/src/utils/validate.ts | 5 +
10 files changed, 248 insertions(+), 2 deletions(-)
create mode 100644 src/api/providers/mistral.ts
create mode 100644 src/api/transform/mistral-format.ts
diff --git a/package-lock.json b/package-lock.json
index 5621c32950..b1de717b85 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,18 +1,19 @@
{
"name": "claude-dev",
- "version": "3.1.6",
+ "version": "3.1.8",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "claude-dev",
- "version": "3.1.6",
+ "version": "3.1.8",
"license": "Apache-2.0",
"dependencies": {
"@anthropic-ai/bedrock-sdk": "^0.10.2",
"@anthropic-ai/sdk": "^0.26.0",
"@anthropic-ai/vertex-sdk": "^0.4.1",
"@google/generative-ai": "^0.18.0",
+ "@mistralai/mistralai": "^1.3.6",
"@modelcontextprotocol/sdk": "^1.0.1",
"@types/clone-deep": "^4.0.4",
"@types/get-folder-size": "^3.0.4",
@@ -2795,6 +2796,14 @@
"integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==",
"license": "MIT"
},
+ "node_modules/@mistralai/mistralai": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-1.3.6.tgz",
+ "integrity": "sha512-2y7U5riZq+cIjKpxGO9y417XuZv9CpBXEAvbjRMzWPGhXY7U1ZXj4VO4H9riS2kFZqTR2yLEKSE6/pGWVVIqgQ==",
+ "peerDependencies": {
+ "zod": ">= 3"
+ }
+ },
"node_modules/@mixmark-io/domino": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@mixmark-io/domino/-/domino-2.2.0.tgz",
diff --git a/package.json b/package.json
index b6c38ac0a3..488dd6e54b 100644
--- a/package.json
+++ b/package.json
@@ -169,6 +169,7 @@
"@anthropic-ai/sdk": "^0.26.0",
"@anthropic-ai/vertex-sdk": "^0.4.1",
"@google/generative-ai": "^0.18.0",
+ "@mistralai/mistralai": "^1.3.6",
"@modelcontextprotocol/sdk": "^1.0.1",
"@types/clone-deep": "^4.0.4",
"@types/get-folder-size": "^3.0.4",
diff --git a/src/api/index.ts b/src/api/index.ts
index 287f843642..d3308df5c6 100644
--- a/src/api/index.ts
+++ b/src/api/index.ts
@@ -11,6 +11,7 @@ import { GeminiHandler } from "./providers/gemini"
import { OpenAiNativeHandler } from "./providers/openai-native"
import { ApiStream } from "./transform/stream"
import { DeepSeekHandler } from "./providers/deepseek"
+import { MistralHandler } from "./providers/mistral"
export interface ApiHandler {
createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream
@@ -40,6 +41,8 @@ export function buildApiHandler(configuration: ApiConfiguration): ApiHandler {
return new OpenAiNativeHandler(options)
case "deepseek":
return new DeepSeekHandler(options)
+ case "mistral":
+ return new MistralHandler(options)
default:
return new AnthropicHandler(options)
}
diff --git a/src/api/providers/mistral.ts b/src/api/providers/mistral.ts
new file mode 100644
index 0000000000..c4377f0003
--- /dev/null
+++ b/src/api/providers/mistral.ts
@@ -0,0 +1,74 @@
+import { Anthropic } from "@anthropic-ai/sdk"
+import { Mistral } from "@mistralai/mistralai"
+import { ApiHandler } from "../"
+import {
+ ApiHandlerOptions,
+ mistralDefaultModelId,
+ MistralModelId,
+ mistralModels,
+ ModelInfo,
+ openAiNativeDefaultModelId,
+ OpenAiNativeModelId,
+ openAiNativeModels,
+} from "../../shared/api"
+import { convertToMistralMessages } from "../transform/mistral-format"
+import { ApiStream } from "../transform/stream"
+
+export class MistralHandler implements ApiHandler {
+ private options: ApiHandlerOptions
+ private client: Mistral
+
+ constructor(options: ApiHandlerOptions) {
+ this.options = options
+ this.client = new Mistral({
+ serverURL: "https://codestral.mistral.ai",
+ apiKey: this.options.mistralApiKey,
+ })
+ }
+
+ async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
+ const stream = await this.client.chat.stream({
+ model: this.getModel().id,
+ // max_completion_tokens: this.getModel().info.maxTokens,
+ temperature: 0,
+ messages: [{ role: "system", content: systemPrompt }, ...convertToMistralMessages(messages)],
+ stream: true,
+ })
+
+ for await (const chunk of stream) {
+ 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,
+ }
+ }
+
+ if (chunk.data.usage) {
+ yield {
+ type: "usage",
+ inputTokens: chunk.data.usage.promptTokens || 0,
+ outputTokens: chunk.data.usage.completionTokens || 0,
+ }
+ }
+ }
+ }
+
+ 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],
+ }
+ }
+}
diff --git a/src/api/transform/mistral-format.ts b/src/api/transform/mistral-format.ts
new file mode 100644
index 0000000000..16c6aaf238
--- /dev/null
+++ b/src/api/transform/mistral-format.ts
@@ -0,0 +1,92 @@
+import { Anthropic } from "@anthropic-ai/sdk"
+import { Mistral } from "@mistralai/mistralai"
+import { AssistantMessage } from "@mistralai/mistralai/models/components/assistantmessage"
+import { SystemMessage } from "@mistralai/mistralai/models/components/systemmessage"
+import { ToolMessage } from "@mistralai/mistralai/models/components/toolmessage"
+import { UserMessage } from "@mistralai/mistralai/models/components/usermessage"
+
+export type MistralMessage =
+ | (SystemMessage & { role: "system" })
+ | (UserMessage & { role: "user" })
+ | (AssistantMessage & { role: "assistant" })
+ | (ToolMessage & { role: "tool" })
+
+export function convertToMistralMessages(anthropicMessages: Anthropic.Messages.MessageParam[]): MistralMessage[] {
+ const mistralMessages: MistralMessage[] = []
+ for (const anthropicMessage of anthropicMessages) {
+ if (typeof anthropicMessage.content === "string") {
+ mistralMessages.push({
+ role: anthropicMessage.role,
+ content: anthropicMessage.content,
+ })
+ } else {
+ if (anthropicMessage.role === "user") {
+ const { nonToolMessages, toolMessages } = anthropicMessage.content.reduce<{
+ nonToolMessages: (Anthropic.TextBlockParam | Anthropic.ImageBlockParam)[]
+ toolMessages: Anthropic.ToolResultBlockParam[]
+ }>(
+ (acc, part) => {
+ if (part.type === "tool_result") {
+ acc.toolMessages.push(part)
+ } else if (part.type === "text" || part.type === "image") {
+ acc.nonToolMessages.push(part)
+ } // user cannot send tool_use messages
+ return acc
+ },
+ { nonToolMessages: [], toolMessages: [] },
+ )
+
+ if (nonToolMessages.length > 0) {
+ mistralMessages.push({
+ role: "user",
+ content: nonToolMessages.map((part) => {
+ if (part.type === "image") {
+ return {
+ type: "image_url",
+ imageUrl: {
+ url: `data:${part.source.media_type};base64,${part.source.data}`,
+ },
+ }
+ }
+ return { type: "text", text: part.text }
+ }),
+ })
+ }
+ } else if (anthropicMessage.role === "assistant") {
+ const { nonToolMessages, toolMessages } = anthropicMessage.content.reduce<{
+ nonToolMessages: (Anthropic.TextBlockParam | Anthropic.ImageBlockParam)[]
+ toolMessages: Anthropic.ToolUseBlockParam[]
+ }>(
+ (acc, part) => {
+ if (part.type === "tool_use") {
+ acc.toolMessages.push(part)
+ } else if (part.type === "text" || part.type === "image") {
+ acc.nonToolMessages.push(part)
+ } // assistant cannot send tool_result messages
+ return acc
+ },
+ { nonToolMessages: [], toolMessages: [] },
+ )
+
+ let content: string | undefined
+ if (nonToolMessages.length > 0) {
+ content = nonToolMessages
+ .map((part) => {
+ if (part.type === "image") {
+ return "" // impossible as the assistant cannot send images
+ }
+ return part.text
+ })
+ .join("\n")
+ }
+
+ mistralMessages.push({
+ role: "assistant",
+ content,
+ })
+ }
+ }
+ }
+
+ return mistralMessages
+}
diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts
index e2a1d7f453..54e47055f2 100644
--- a/src/core/webview/ClineProvider.ts
+++ b/src/core/webview/ClineProvider.ts
@@ -41,6 +41,7 @@ type SecretKey =
| "geminiApiKey"
| "openAiNativeApiKey"
| "deepSeekApiKey"
+ | "mistralApiKey"
type GlobalStateKey =
| "apiProvider"
| "apiModelId"
@@ -392,6 +393,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
geminiApiKey,
openAiNativeApiKey,
deepSeekApiKey,
+ mistralApiKey,
azureApiVersion,
openRouterModelId,
openRouterModelInfo,
@@ -418,6 +420,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
await this.storeSecret("geminiApiKey", geminiApiKey)
await this.storeSecret("openAiNativeApiKey", openAiNativeApiKey)
await this.storeSecret("deepSeekApiKey", deepSeekApiKey)
+ await this.storeSecret("mistralApiKey", mistralApiKey)
await this.updateGlobalState("azureApiVersion", azureApiVersion)
await this.updateGlobalState("openRouterModelId", openRouterModelId)
await this.updateGlobalState("openRouterModelInfo", openRouterModelInfo)
@@ -1023,6 +1026,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
geminiApiKey,
openAiNativeApiKey,
deepSeekApiKey,
+ mistralApiKey,
azureApiVersion,
openRouterModelId,
openRouterModelInfo,
@@ -1054,6 +1058,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
this.getSecret("geminiApiKey") as Promise,
this.getSecret("openAiNativeApiKey") as Promise,
this.getSecret("deepSeekApiKey") as Promise,
+ this.getSecret("mistralApiKey") as Promise,
this.getGlobalState("azureApiVersion") as Promise,
this.getGlobalState("openRouterModelId") as Promise,
this.getGlobalState("openRouterModelInfo") as Promise,
@@ -1102,6 +1107,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
geminiApiKey,
openAiNativeApiKey,
deepSeekApiKey,
+ mistralApiKey,
azureApiVersion,
openRouterModelId,
openRouterModelInfo,
@@ -1187,6 +1193,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
"geminiApiKey",
"openAiNativeApiKey",
"deepSeekApiKey",
+ "mistralApiKey",
]
for (const key of secretKeys) {
await this.storeSecret(key, undefined)
diff --git a/src/shared/api.ts b/src/shared/api.ts
index 8229d02790..f5ff3017fe 100644
--- a/src/shared/api.ts
+++ b/src/shared/api.ts
@@ -9,6 +9,7 @@ export type ApiProvider =
| "gemini"
| "openai-native"
| "deepseek"
+ | "mistral"
export interface ApiHandlerOptions {
apiModelId?: string
@@ -34,6 +35,7 @@ export interface ApiHandlerOptions {
geminiApiKey?: string
openAiNativeApiKey?: string
deepSeekApiKey?: string
+ mistralApiKey?: string
azureApiVersion?: string
}
@@ -374,3 +376,18 @@ export const deepSeekModels = {
cacheReadsPrice: 0.014,
},
} as const satisfies Record
+
+// Mistral
+// https://docs.mistral.ai/getting-started/models/models_overview/
+export type MistralModelId = keyof typeof mistralModels
+export const mistralDefaultModelId: MistralModelId = "codestral-latest"
+export const mistralModels = {
+ "codestral-latest": {
+ maxTokens: 32_768,
+ contextWindow: 256_000,
+ supportsImages: false,
+ supportsPromptCache: false,
+ inputPrice: 0.3,
+ outputPrice: 0.9,
+ },
+} as const satisfies Record
diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx
index fa00598a8e..28cbb6fd7c 100644
--- a/webview-ui/src/components/settings/ApiOptions.tsx
+++ b/webview-ui/src/components/settings/ApiOptions.tsx
@@ -21,6 +21,8 @@ import {
deepSeekModels,
geminiDefaultModelId,
geminiModels,
+ mistralDefaultModelId,
+ mistralModels,
openAiModelInfoSaneDefaults,
openAiNativeDefaultModelId,
openAiNativeModels,
@@ -142,6 +144,7 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage }:
Anthropic
Google Gemini
DeepSeek
+ Mistral
GCP Vertex AI
AWS Bedrock
OpenAI
@@ -270,6 +273,37 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage }:
)}
+ {selectedProvider === "mistral" && (
+
+
+ Mistral API Key
+
+
+ This key is stored locally and only used to make API requests from this extension.
+ {!apiConfiguration?.mistralApiKey && (
+
+ You can get a Mistral API key by signing up here.
+
+ )}
+
+
+ )}
+
{selectedProvider === "openrouter" && (
key !== undefined)
: false
setShowWelcome(!hasKey)
diff --git a/webview-ui/src/utils/validate.ts b/webview-ui/src/utils/validate.ts
index 91cf4f9136..7dce99bebd 100644
--- a/webview-ui/src/utils/validate.ts
+++ b/webview-ui/src/utils/validate.ts
@@ -38,6 +38,11 @@ export function validateApiConfiguration(apiConfiguration?: ApiConfiguration): s
return "You must provide a valid API key or choose a different provider."
}
break
+ case "mistral":
+ if (!apiConfiguration.mistralApiKey) {
+ return "You must provide a valid API key or choose a different provider."
+ }
+ break
case "openai":
if (!apiConfiguration.openAiBaseUrl || !apiConfiguration.openAiApiKey || !apiConfiguration.openAiModelId) {
return "You must provide a valid base URL, API key, and model ID."
From 52bb98fd90b74d42318264e6ec7f2babde042290 Mon Sep 17 00:00:00 2001
From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Date: Thu, 16 Jan 2025 19:43:03 -0800
Subject: [PATCH 066/294] Prepare for release
---
CHANGELOG.md | 4 ++++
package.json | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7c9c7eb80d..6c3c07d673 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# Change Log
+## [3.1.9]
+
+- Add Mistral API provider with codestral-latest model
+
## [3.1.7]
- Add ability to change viewport size and headless mode when Cline asks to launch the browser
diff --git a/package.json b/package.json
index 488dd6e54b..def6b5b323 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "claude-dev",
"displayName": "Cline",
"description": "Autonomous coding agent right in your IDE, capable of creating/editing files, running commands, using the browser, and more with your permission every step of the way.",
- "version": "3.1.8",
+ "version": "3.1.9",
"icon": "assets/icons/icon.png",
"galleryBanner": {
"color": "#617A91",
From bbee587cfe57c7fbd82672412259143a9e7ab7af Mon Sep 17 00:00:00 2001
From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Date: Fri, 17 Jan 2025 09:53:29 -0800
Subject: [PATCH 067/294] New icon
---
CHANGELOG.md | 4 ++++
assets/icons/icon.png | Bin 9385 -> 5047 bytes
assets/icons/icon.svg | 16 ++++++++++++++++
assets/icons/robot_panel_dark.png | Bin 718 -> 902 bytes
assets/icons/robot_panel_light.png | Bin 689 -> 666 bytes
package.json | 4 ++--
6 files changed, 22 insertions(+), 2 deletions(-)
create mode 100644 assets/icons/icon.svg
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6c3c07d673..fad3f4aae0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# Change Log
+## [3.1.10]
+
+- New icon!
+
## [3.1.9]
- Add Mistral API provider with codestral-latest model
diff --git a/assets/icons/icon.png b/assets/icons/icon.png
index e8736aaa02433a094b7696f9b0f17ade498c7b62..db6f1d8fd14162365d2002436980460c921111f4 100644
GIT binary patch
delta 4964
zcmV-q6PxU*Nw+7EfPWJqNkl^oDvih440z{Am9uPpo5s<@3fCZ8nKnRKW
zyp~h$Ll>U7UhlEukHrhu=Zd;N_IV(zOE_Fb#NGE6S4oISl1U_x#6>|NT#G`GBOwwX
zgv@+f^)VA>CNq=o>^bt)d*94=bXQkb)z{r$cU3imFoo3Y?0*hz>KeN12=&aO;k
z2_mr0OmrpygH9q7;zg$z<pG6*@40I1vv#Z;J>0$6~q|5b=PfL%?%PDjn`~yu?%h
zPS?Ntyb-S(F=Zn@rSk?n>hZXS2LZm%YT?Y?0}dNG8i%To*qsnrdS+~6p^tMBqjwJn}P)O!Gi=FO>hY-a{}vF
ziFK@GqFCv$SSkgPRhAX*s0vj@xha6vYQ3diY@ZGmuz@Jru)@E=Bie+lR*rR;aW61d
z1P&;QV^>P-b~`DI;b5c!u(Wa6V+E%;4Fx_Ho6=OQjeo`P8bwC|4(v|$qe2uq1-7lC
zAh!^!MO~Bbpt}N$vSki%0Gp3OpNADl)}7+kh)@(yATKvtK-^SXlz&)H7U-q`!>yC1
z;(%-g5;s~;0!>`aIIsr?o~z64J3njoM8yrw3V?zigWCBPj(jszly7EH$pQQ#D#liJ
zIV!?8Dt}Q(*;FgQsBzhS98Tx!h-HQr0?fRcIk43t2+x%kc6QxEA}Jg-QxA({knjV;kzLZ+~5S;J{Xzxoi<=np75;!k5LS&?mwd
z6lFzpERsWvtS*3>eVm7&PdpPMg9F2`JE)V>9kkd5xCVTmW4&qd#DvJn5N;(WiKhXGpvCJ1jve9g@YNE`?zOTR6EYhh6J^zqz!brWQH~g_B
zOJUlSX^N`wb-`|U{jE2k;o3Dt`NBCd+7k@)wI^sc29S(7!nqa-X?x=F-zyeA6>;~t
z@$ljwpT{nu**l_;g78W1Pqw}afXn)5$rB}zos|s_-T$EK+Kd@B8s^VkpgMopWq-UO
zTQ80FQUJ2yixxAkxVSi2^2no_WiV^TOz7IRt7bf5fq=1ONyZ+;cqxEn!0GWYl20dR
z-vbGq6Es&LHZ~S!OjS01&|Ij22z+Jir2yFck2SDtuBdG|>emab8T#SUizd~0d}Q3D
z0A#oCGTl~xD!O;
z|HX?nIGoYfk)|4_i=zfSWPMWs07gbj1sfY1;Z)UW-Kuk~{u-P;cUCuiVSj+AN!F4A
zq*=3Qf;_4mHA`?|M=#i0whtPvU)Kv=IN*tQ$z1ZA$6*2M4`@IX&Jf$SEhHu+;E^c(
z)AxBWsI9F9TnE=>D!1y)X~@c)6s$OCY;@qN;sv0t;=+XsaJBww(DWgHNHPaFEm;M-
z{PROjlP>}N`}K#6^bD|NSbw2k-@d+&xn0kgrri1I=hLU4xVRYX#dbLO<3ULoxe+X*
zAd)#IE07d`f-}g-!!1dAdO9pw^a$L3dos5&^)9GaQcsvUeTHp31;Et-ip&vm7&u@6EL-{*+&T2lkkg_k0koE|V)?T$W7>3Bvw!Y=*jHAjCwf)5
zNamDL@(bv3Ti=y<-c1!Ioia_nXEMC}{7cZIM-QDah98>Fw|0g}6El%8jQ;TbccCV*
z5&Z_c6QHT({HiJ@=fJU9{2Jde{31FZ{MDKxg>jm
zB5?99IeSueM1SM!&96=!JHf-#XX=f%MSLWCYynv37E|h&fPbcN7cVelO!%^N#?|!9NG+
z1xdY=-#Q4%UVp%uNp1?z)$IHIOWxyd*F=8fBa%H?1aJ6Wiv|}z3ZorA9+B+nGmu*g&{E~i=bSl!vZqaz
z^!K9RP!9^+P*S=F$YS@GGiTsBnu1gFITRO}C>jVEkbmk0SFc`?!pD?Vl*8GxXTj-k
zNUmbT?ivO*t2J`JY*6-O5E9(HRxd8d|3CvI?4Lh7wxXyU&H%ob~WXpDX<8q
zoV9xN{CoR$$)2zF*IM7A;H$59!F%toftlz+K5zCMDc(@CMB#lSHU_%@Y6T(&-dXc5
zeD<$@27jfup`ih`Y~2FiANUSlUG*y560iJAmrTl6|K$zH+nE=%EHeMzuxSI9^*y}u
z!plHYx)B2?djW;-HVX@Tx}ei=n*!W%#~rG=c)WXM~Y(ejvMpfB2fApauXHli;J&D@{7n@v*+?^1#tC&Lm2q<(PLnchUUMz`aJj>g)8CmvE#>NC8w@NYivML0xdwT
z07W?C0FnaWjG%EqcJTB$rUj~Nq#ZD{pn&R&@^IAD)MzS)GV6h3-mE!5p=}KYMlFD$
z3Sh$kOXTFK1spi>q7~%gkee!1%F%u?j%ezl#?v2*xY(Sr87)#j$|F
zt$?HeK}gW{XyV-yP3vC`ETrgU_ZU1Dpf13Wc|iJ>oj-5BhU%MHi34+u9Sex00LrJy
zWXOiTWywe01Z^DXhOGcaJv9iNhP43nEo*{49GLy^Y<&L%EORO!b |