Skip to content

Commit 93611ea

Browse files
committed
Merge remote-tracking branch 'origin/main' into support-custom-baseUrl-for-google-ai-studio-gemini
2 parents 9d0b824 + 4ce2664 commit 93611ea

File tree

106 files changed

+5210
-2756
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+5210
-2756
lines changed

.changeset/seven-apricots-watch.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"roo-cline": patch
3+
---
4+
5+
ContextProxy fix - constructor should not be async

.changeset/thin-fans-deliver.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"roo-cline": patch
3+
---
4+
5+
Add prompt caching to OpenAI-compatible custom model info

.github/workflows/code-qa.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,9 @@ jobs:
110110
cache: 'npm'
111111
- name: Install dependencies
112112
run: npm run install:all
113-
- name: Create env.integration file
113+
- name: Create .env.local file
114114
working-directory: e2e
115-
run: echo "OPENROUTER_API_KEY=${{ secrets.OPENROUTER_API_KEY }}" > .env.integration
115+
run: echo "OPENROUTER_API_KEY=${{ secrets.OPENROUTER_API_KEY }}" > .env.local
116116
- name: Run integration tests
117117
working-directory: e2e
118118
run: xvfb-run -a npm run ci

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ docs/_site/
2222

2323
# Dotenv
2424
.env
25-
.env.integration
25+
.env.*
26+
!.env.*.sample
27+
2628

2729
#Local lint config
2830
.eslintrc.local.json

.roomodes

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"customModes": [
3+
{
4+
"slug": "translate",
5+
"name": "Translate",
6+
"roleDefinition": "You are Roo, a linguistic specialist focused on translating and managing localization files. Your responsibility is to help maintain and update translation files for the application, ensuring consistency and accuracy across all language resources.",
7+
"groups": [
8+
"read",
9+
["edit", { "fileRegex": "src/i18n/locales/", "description": "Translation files only" }]
10+
],
11+
"customInstructions": "When translating content:\n- Maintain consistent terminology across all translations\n- Respect the JSON structure of translation files\n- Consider context when translating UI strings\n- Watch for placeholders (like {{variable}}) and preserve them in translations\n- Be mindful of text length in UI elements when translating to languages that might require more characters\n- If you need context for a translation, use read_file to examine the components using these strings"
12+
},
13+
{
14+
"slug": "test",
15+
"name": "Test",
16+
"roleDefinition": "You are Roo, a Jest testing specialist with deep expertise in:\n- Writing and maintaining Jest test suites\n- Test-driven development (TDD) practices\n- Mocking and stubbing with Jest\n- Integration testing strategies\n- TypeScript testing patterns\n- Code coverage analysis\n- Test performance optimization\n\nYour focus is on maintaining high test quality and coverage across the codebase, working primarily with:\n- Test files in __tests__ directories\n- Mock implementations in __mocks__\n- Test utilities and helpers\n- Jest configuration and setup\n\nYou ensure tests are:\n- Well-structured and maintainable\n- Following Jest best practices\n- Properly typed with TypeScript\n- Providing meaningful coverage\n- Using appropriate mocking strategies",
17+
"groups": [
18+
"read",
19+
"browser",
20+
"command",
21+
["edit", {
22+
"fileRegex": "(__tests__/.*|__mocks__/.*|\\.test\\.(ts|tsx|js|jsx)$|/test/.*|jest\\.config\\.(js|ts)$)",
23+
"description": "Test files, mocks, and Jest configuration"
24+
}]
25+
],
26+
"customInstructions": "When writing tests:\n- Always use describe/it blocks for clear test organization\n- Include meaningful test descriptions\n- Use beforeEach/afterEach for proper test isolation\n- Implement proper error cases\n- Add JSDoc comments for complex test scenarios\n- Ensure mocks are properly typed\n- Verify both positive and negative test cases"
27+
}
28+
]
29+
}
File renamed without changes.

e2e/VSCODE_INTEGRATION_TESTS.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ The test runner (`runTest.ts`) is responsible for:
3030

3131
### Environment Setup
3232

33-
1. Create a `.env.integration` file in the root directory with required environment variables:
33+
1. Create a `.env.local` file in the root directory with required environment variables:
3434

3535
```
3636
OPENROUTER_API_KEY=sk-or-v1-...
@@ -67,7 +67,7 @@ declare global {
6767

6868
## Running Tests
6969

70-
1. Ensure you have the required environment variables set in `.env.integration`
70+
1. Ensure you have the required environment variables set in `.env.local`
7171

7272
2. Run the integration tests:
7373

@@ -117,8 +117,10 @@ const interval = 1000
117117
2. **State Management**: Reset extension state before/after tests:
118118

119119
```typescript
120-
await globalThis.provider.updateGlobalState("mode", "Ask")
121-
await globalThis.provider.updateGlobalState("alwaysAllowModeSwitch", true)
120+
await globalThis.api.setConfiguration({
121+
mode: "Ask",
122+
alwaysAllowModeSwitch: true,
123+
})
122124
```
123125

124126
3. **Assertions**: Use clear assertions with meaningful messages:
@@ -141,8 +143,12 @@ try {
141143

142144
```typescript
143145
let startTime = Date.now()
146+
144147
while (Date.now() - startTime < timeout) {
145-
if (condition) break
148+
if (condition) {
149+
break
150+
}
151+
146152
await new Promise((resolve) => setTimeout(resolve, interval))
147153
}
148154
```

e2e/package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
"version": "0.1.0",
44
"private": true,
55
"scripts": {
6-
"build": "cd .. && npm run build",
7-
"compile": "tsc -p tsconfig.json",
6+
"build": "cd .. && npm run compile && npm run build:webview",
7+
"compile": "rm -rf out && tsc -p tsconfig.json",
88
"lint": "eslint src --ext ts",
99
"check-types": "tsc --noEmit",
10-
"test": "npm run compile && npx dotenvx run -f .env.integration -- node ./out/runTest.js",
11-
"ci": "npm run build && npm run test"
10+
"test": "npm run compile && npx dotenvx run -f .env.local -- node ./out/runTest.js",
11+
"ci": "npm run build && npm run test",
12+
"clean": "rimraf out"
1213
},
1314
"dependencies": {},
1415
"devDependencies": {

e2e/src/suite/index.ts

Lines changed: 19 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,46 @@
11
import * as path from "path"
22
import Mocha from "mocha"
33
import { glob } from "glob"
4-
import { RooCodeAPI, ClineProvider } from "../../../src/exports/roo-code"
54
import * as vscode from "vscode"
65

6+
import { RooCodeAPI } from "../../../src/exports/roo-code"
7+
8+
import { waitUntilReady } from "./utils"
9+
710
declare global {
8-
var api: RooCodeAPI
9-
var provider: ClineProvider
1011
var extension: vscode.Extension<RooCodeAPI> | undefined
11-
var panel: vscode.WebviewPanel | undefined
12+
var api: RooCodeAPI
1213
}
1314

14-
export async function run(): Promise<void> {
15-
const mocha = new Mocha({
16-
ui: "tdd",
17-
timeout: 600000, // 10 minutes to compensate for time communicating with LLM while running in GHA.
18-
})
19-
15+
export async function run() {
16+
const mocha = new Mocha({ ui: "tdd", timeout: 300_000 })
2017
const testsRoot = path.resolve(__dirname, "..")
2118

2219
try {
2320
// Find all test files.
2421
const files = await glob("**/**.test.js", { cwd: testsRoot })
25-
26-
// Add files to the test suite.
2722
files.forEach((f: string) => mocha.addFile(path.resolve(testsRoot, f)))
2823

29-
// Set up global extension, api, provider, and panel.
30-
globalThis.extension = vscode.extensions.getExtension("RooVeterinaryInc.roo-cline")
24+
const extension = vscode.extensions.getExtension<RooCodeAPI>("RooVeterinaryInc.roo-cline")
3125

32-
if (!globalThis.extension) {
26+
if (!extension) {
3327
throw new Error("Extension not found")
3428
}
3529

36-
globalThis.api = globalThis.extension.isActive
37-
? globalThis.extension.exports
38-
: await globalThis.extension.activate()
39-
40-
globalThis.provider = globalThis.api.sidebarProvider
30+
const api = extension.isActive ? extension.exports : await extension.activate()
4131

42-
await globalThis.provider.updateGlobalState("apiProvider", "openrouter")
43-
await globalThis.provider.updateGlobalState("openRouterModelId", "anthropic/claude-3.5-sonnet")
44-
45-
await globalThis.provider.storeSecret(
46-
"openRouterApiKey",
47-
process.env.OPENROUTER_API_KEY || "sk-or-v1-fake-api-key",
48-
)
49-
50-
globalThis.panel = vscode.window.createWebviewPanel(
51-
"roo-cline.SidebarProvider",
52-
"Roo Code",
53-
vscode.ViewColumn.One,
54-
{
55-
enableScripts: true,
56-
enableCommandUris: true,
57-
retainContextWhenHidden: true,
58-
localResourceRoots: [globalThis.extension?.extensionUri],
59-
},
60-
)
61-
62-
await globalThis.provider.resolveWebviewView(globalThis.panel)
63-
64-
let startTime = Date.now()
65-
const timeout = 60000
66-
const interval = 1000
32+
await api.setConfiguration({
33+
apiProvider: "openrouter",
34+
openRouterApiKey: process.env.OPENROUTER_API_KEY!,
35+
openRouterModelId: "anthropic/claude-3.5-sonnet",
36+
})
6737

68-
while (Date.now() - startTime < timeout) {
69-
if (globalThis.provider.viewLaunched) {
70-
break
71-
}
38+
await waitUntilReady(api)
7239

73-
await new Promise((resolve) => setTimeout(resolve, interval))
74-
}
40+
globalThis.api = api
41+
globalThis.extension = extension
7542

76-
// Run the mocha test.
77-
return new Promise((resolve, reject) => {
43+
return new Promise<void>((resolve, reject) => {
7844
try {
7945
mocha.run((failures: number) => {
8046
if (failures > 0) {

e2e/src/suite/modes.test.ts

Lines changed: 26 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,60 @@
11
import * as assert from "assert"
22

3+
import { waitForMessage } from "./utils"
4+
35
suite("Roo Code Modes", () => {
46
test("Should handle switching modes correctly", async function () {
5-
const timeout = 30000
6-
const interval = 1000
7+
const timeout = 300_000
8+
const api = globalThis.api
79

810
const testPrompt =
9-
"For each mode (Code, Architect, Ask) respond with the mode name and what it specializes in after switching to that mode, do not start with the current mode, be sure to say 'I AM DONE' after the task is complete"
10-
11-
if (!globalThis.extension) {
12-
assert.fail("Extension not found")
13-
}
14-
15-
let startTime = Date.now()
16-
17-
// Ensure the webview is launched.
18-
while (Date.now() - startTime < timeout) {
19-
if (globalThis.provider.viewLaunched) {
20-
break
21-
}
22-
23-
await new Promise((resolve) => setTimeout(resolve, interval))
24-
}
25-
26-
await globalThis.provider.updateGlobalState("mode", "Ask")
27-
await globalThis.provider.updateGlobalState("alwaysAllowModeSwitch", true)
28-
await globalThis.provider.updateGlobalState("autoApprovalEnabled", true)
29-
30-
// Start a new task.
31-
await globalThis.api.startNewTask(testPrompt)
11+
"For each mode (Code, Architect, Ask) respond with the mode name and what it specializes in after switching to that mode, do not start with the current mode, be sure to say 'I AM DONE' after the task is complete."
3212

33-
// Wait for task to appear in history with tokens.
34-
startTime = Date.now()
13+
await api.setConfiguration({ mode: "Code", alwaysAllowModeSwitch: true, autoApprovalEnabled: true })
14+
await api.startNewTask(testPrompt)
3515

36-
while (Date.now() - startTime < timeout) {
37-
const messages = globalThis.provider.messages
16+
await waitForMessage(api, { include: "I AM DONE", exclude: "be sure to say", timeout })
3817

39-
if (
40-
messages.some(
41-
({ type, text }) =>
42-
type === "say" && text?.includes("I AM DONE") && !text?.includes("be sure to say"),
43-
)
44-
) {
45-
break
46-
}
47-
48-
await new Promise((resolve) => setTimeout(resolve, interval))
49-
}
50-
51-
if (globalThis.provider.messages.length === 0) {
18+
if (api.getMessages().length === 0) {
5219
assert.fail("No messages received")
5320
}
5421

5522
// Log the messages to the console.
56-
globalThis.provider.messages.forEach(({ type, text }) => {
23+
api.getMessages().forEach(({ type, text }) => {
5724
if (type === "say") {
5825
console.log(text)
5926
}
6027
})
6128

6229
// Start Grading Portion of test to grade the response from 1 to 10.
63-
await globalThis.provider.updateGlobalState("mode", "Ask")
64-
let output = globalThis.provider.messages.map(({ type, text }) => (type === "say" ? text : "")).join("\n")
65-
66-
await globalThis.api.startNewTask(
67-
`Given this prompt: ${testPrompt} grade the response from 1 to 10 in the format of "Grade: (1-10)": ${output} \n Be sure to say 'I AM DONE GRADING' after the task is complete`,
68-
)
69-
70-
startTime = Date.now()
30+
await api.setConfiguration({ mode: "Ask" })
7131

72-
while (Date.now() - startTime < timeout) {
73-
const messages = globalThis.provider.messages
32+
let output = api
33+
.getMessages()
34+
.map(({ type, text }) => (type === "say" ? text : ""))
35+
.join("\n")
7436

75-
if (
76-
messages.some(
77-
({ type, text }) =>
78-
type === "say" && text?.includes("I AM DONE GRADING") && !text?.includes("be sure to say"),
79-
)
80-
) {
81-
break
82-
}
37+
await api.startNewTask(
38+
`Given this prompt: ${testPrompt} grade the response from 1 to 10 in the format of "Grade: (1-10)": ${output}\nBe sure to say 'I AM DONE GRADING' after the task is complete.`,
39+
)
8340

84-
await new Promise((resolve) => setTimeout(resolve, interval))
85-
}
41+
await waitForMessage(api, { include: "I AM DONE GRADING", exclude: "be sure to say", timeout })
8642

87-
if (globalThis.provider.messages.length === 0) {
43+
if (api.getMessages().length === 0) {
8844
assert.fail("No messages received")
8945
}
9046

91-
globalThis.provider.messages.forEach(({ type, text }) => {
47+
api.getMessages().forEach(({ type, text }) => {
9248
if (type === "say" && text?.includes("Grade:")) {
9349
console.log(text)
9450
}
9551
})
9652

97-
const gradeMessage = globalThis.provider.messages.find(
98-
({ type, text }) => type === "say" && !text?.includes("Grade: (1-10)") && text?.includes("Grade:"),
99-
)?.text
53+
const gradeMessage = api
54+
.getMessages()
55+
.find(
56+
({ type, text }) => type === "say" && !text?.includes("Grade: (1-10)") && text?.includes("Grade:"),
57+
)?.text
10058

10159
const gradeMatch = gradeMessage?.match(/Grade: (\d+)/)
10260
const gradeNum = gradeMatch ? parseInt(gradeMatch[1]) : undefined

0 commit comments

Comments
 (0)