Skip to content

Commit fb1550c

Browse files
authored
Merge branch 'RooVetGit:main' into human-relay
2 parents 2ed74f9 + fbdf758 commit fb1550c

File tree

35 files changed

+3958
-2754
lines changed

35 files changed

+3958
-2754
lines changed

.github/workflows/marketplace-publish.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ jobs:
2727
node-version: 18
2828

2929
- run: |
30-
git config user.name github-actions
31-
git config user.email github-actions@github.com
30+
git config user.name "github-actions[bot]"
31+
git config user.email "github-actions[bot]@users.noreply.github.com"
3232
3333
- name: Install Dependencies
3434
run: |

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Roo Code Changelog
22

3+
## [3.8.6] - 2025-03-13
4+
5+
- Revert SSE MCP support while we debug some config validation issues
6+
37
## [3.8.5] - 2025-03-12
48

59
- Refactor terminal architecture to address critical issues with the current design (thanks @KJ7LNW!)

e2e/src/suite/extension.test.ts

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@ suite("Roo Code Extension", () => {
99
})
1010

1111
test("Commands should be registered", async () => {
12-
const timeout = 10 * 1_000
13-
const interval = 1_000
14-
const startTime = Date.now()
15-
1612
const expectedCommands = [
1713
"roo-cline.plusButtonClicked",
1814
"roo-cline.mcpButtonClicked",
@@ -25,23 +21,6 @@ suite("Roo Code Extension", () => {
2521
"roo-cline.improveCode",
2622
]
2723

28-
while (Date.now() - startTime < timeout) {
29-
const commands = await vscode.commands.getCommands(true)
30-
const missingCommands = []
31-
32-
for (const cmd of expectedCommands) {
33-
if (!commands.includes(cmd)) {
34-
missingCommands.push(cmd)
35-
}
36-
}
37-
38-
if (missingCommands.length === 0) {
39-
break
40-
}
41-
42-
await new Promise((resolve) => setTimeout(resolve, interval))
43-
}
44-
4524
const commands = await vscode.commands.getCommands(true)
4625

4726
for (const cmd of expectedCommands) {

e2e/src/suite/index.ts

Lines changed: 30 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,55 +8,39 @@ import { RooCodeAPI } from "../../../src/exports/roo-code"
88
import { waitUntilReady } from "./utils"
99

1010
declare global {
11-
var extension: vscode.Extension<RooCodeAPI> | undefined
1211
var api: RooCodeAPI
1312
}
1413

1514
export async function run() {
16-
const mocha = new Mocha({ ui: "tdd", timeout: 300_000 })
17-
const testsRoot = path.resolve(__dirname, "..")
18-
19-
try {
20-
// Find all test files.
21-
const files = await glob("**/**.test.js", { cwd: testsRoot })
22-
files.forEach((f: string) => mocha.addFile(path.resolve(testsRoot, f)))
23-
24-
const extension = vscode.extensions.getExtension<RooCodeAPI>("RooVeterinaryInc.roo-cline")
25-
26-
if (!extension) {
27-
throw new Error("Extension not found")
28-
}
29-
30-
const api = extension.isActive ? extension.exports : await extension.activate()
31-
32-
await api.setConfiguration({
33-
apiProvider: "openrouter",
34-
openRouterApiKey: process.env.OPENROUTER_API_KEY!,
35-
openRouterModelId: "anthropic/claude-3.5-sonnet",
36-
})
37-
38-
await waitUntilReady(api)
39-
40-
globalThis.api = api
41-
globalThis.extension = extension
42-
43-
return new Promise<void>((resolve, reject) => {
44-
try {
45-
mocha.run((failures: number) => {
46-
if (failures > 0) {
47-
reject(new Error(`${failures} tests failed.`))
48-
} else {
49-
resolve()
50-
}
51-
})
52-
} catch (err) {
53-
console.error(err)
54-
reject(err)
55-
}
56-
})
57-
} catch (err) {
58-
console.error("Error while running tests:")
59-
console.error(err)
60-
throw err
15+
const extension = vscode.extensions.getExtension<RooCodeAPI>("RooVeterinaryInc.roo-cline")
16+
17+
if (!extension) {
18+
throw new Error("Extension not found")
6119
}
20+
21+
// Activate the extension if it's not already active.
22+
const api = extension.isActive ? extension.exports : await extension.activate()
23+
24+
// TODO: We might want to support a "free" model out of the box so
25+
// contributors can run the tests locally without having to pay.
26+
await api.setConfiguration({
27+
apiProvider: "openrouter",
28+
openRouterApiKey: process.env.OPENROUTER_API_KEY!,
29+
openRouterModelId: "anthropic/claude-3.5-sonnet",
30+
})
31+
32+
await waitUntilReady({ api })
33+
34+
// Expose the API to the tests.
35+
globalThis.api = api
36+
37+
// Add all the tests to the runner.
38+
const mocha = new Mocha({ ui: "tdd", timeout: 300_000 })
39+
const cwd = path.resolve(__dirname, "..")
40+
;(await glob("**/**.test.js", { cwd })).forEach((testFile) => mocha.addFile(path.resolve(cwd, testFile)))
41+
42+
// Let's go!
43+
return new Promise<void>((resolve, reject) =>
44+
mocha.run((failures) => (failures === 0 ? resolve() : reject(new Error(`${failures} tests failed.`)))),
45+
)
6246
}

e2e/src/suite/modes.test.ts

Lines changed: 27 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,44 @@
11
import * as assert from "assert"
22

3-
import { waitForMessage } from "./utils"
3+
import { getCompletion, getMessage, sleep, waitForCompletion, waitUntilAborted } from "./utils"
44

55
suite("Roo Code Modes", () => {
66
test("Should handle switching modes correctly", async function () {
7-
const timeout = 300_000
87
const api = globalThis.api
98

10-
const 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."
9+
/**
10+
* Switch modes.
11+
*/
1212

13-
await api.setConfiguration({ mode: "Code", alwaysAllowModeSwitch: true, autoApprovalEnabled: true })
14-
await api.startNewTask(testPrompt)
13+
const switchModesPrompt =
14+
"For each mode (Code, Architect, Ask) respond with the mode name and what it specializes in after switching to that mode. " +
15+
"Do not start with the current mode."
1516

16-
await waitForMessage(api, { include: "I AM DONE", exclude: "be sure to say", timeout })
17+
await api.setConfiguration({ mode: "Code", alwaysAllowModeSwitch: true, autoApprovalEnabled: true })
18+
const switchModesTaskId = await api.startNewTask(switchModesPrompt)
19+
await waitForCompletion({ api, taskId: switchModesTaskId, timeout: 60_000 })
1720

18-
if (api.getMessages().length === 0) {
19-
assert.fail("No messages received")
20-
}
21+
/**
22+
* Grade the response.
23+
*/
2124

22-
// Log the messages to the console.
23-
api.getMessages().forEach(({ type, text }) => {
24-
if (type === "say") {
25-
console.log(text)
26-
}
27-
})
25+
const gradePrompt =
26+
`Given this prompt: ${switchModesPrompt} grade the response from 1 to 10 in the format of "Grade: (1-10)": ` +
27+
api
28+
.getMessages(switchModesTaskId)
29+
.filter(({ type }) => type === "say")
30+
.map(({ text }) => text ?? "")
31+
.join("\n")
2832

29-
// Start Grading Portion of test to grade the response from 1 to 10.
3033
await api.setConfiguration({ mode: "Ask" })
34+
const gradeTaskId = await api.startNewTask(gradePrompt)
35+
await waitForCompletion({ api, taskId: gradeTaskId, timeout: 60_000 })
3136

32-
let output = api
33-
.getMessages()
34-
.map(({ type, text }) => (type === "say" ? text : ""))
35-
.join("\n")
36-
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-
)
40-
41-
await waitForMessage(api, { include: "I AM DONE GRADING", exclude: "be sure to say", timeout })
42-
43-
if (api.getMessages().length === 0) {
44-
assert.fail("No messages received")
45-
}
46-
47-
api.getMessages().forEach(({ type, text }) => {
48-
if (type === "say" && text?.includes("Grade:")) {
49-
console.log(text)
50-
}
51-
})
52-
53-
const gradeMessage = api
54-
.getMessages()
55-
.find(
56-
({ type, text }) => type === "say" && !text?.includes("Grade: (1-10)") && text?.includes("Grade:"),
57-
)?.text
37+
const completion = getCompletion({ api, taskId: gradeTaskId })
38+
const match = completion?.text?.match(/Grade: (\d+)/)
39+
const score = parseInt(match?.[1] ?? "0")
40+
assert.ok(score >= 7 && score <= 10, `Grade must be between 7 and 10 - ${completion?.text}`)
5841

59-
const gradeMatch = gradeMessage?.match(/Grade: (\d+)/)
60-
const gradeNum = gradeMatch ? parseInt(gradeMatch[1]) : undefined
61-
assert.ok(gradeNum !== undefined && gradeNum >= 7 && gradeNum <= 10, "Grade must be between 7 and 10")
42+
await api.cancelCurrentTask()
6243
})
6344
})

e2e/src/suite/subtasks.test.ts

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,71 @@
11
import * as assert from "assert"
22

3-
import { sleep, waitForToolUse, waitForMessage } from "./utils"
3+
import { sleep, waitFor, getMessage, waitForCompletion } from "./utils"
44

55
suite("Roo Code Subtasks", () => {
6-
test.skip("Should handle subtask cancellation and resumption correctly", async function () {
6+
test("Should handle subtask cancellation and resumption correctly", async function () {
77
const api = globalThis.api
88

99
await api.setConfiguration({
1010
mode: "Code",
1111
alwaysAllowModeSwitch: true,
1212
alwaysAllowSubtasks: true,
1313
autoApprovalEnabled: true,
14+
enableCheckpoints: false,
1415
})
1516

17+
const childPrompt = "You are a calculator. Respond only with numbers. What is the square root of 9?"
18+
1619
// Start a parent task that will create a subtask.
17-
await api.startNewTask(
20+
const parentTaskId = await api.startNewTask(
1821
"You are the parent task. " +
19-
"Create a subtask by using the new_task tool with the message 'You are the subtask'. " +
20-
"After creating the subtask, wait for it to complete and then respond with 'Parent task resumed'.",
22+
`Create a subtask by using the new_task tool with the message '${childPrompt}'.` +
23+
"After creating the subtask, wait for it to complete and then respond 'Parent task resumed'.",
2124
)
2225

23-
await waitForToolUse(api, "new_task")
26+
let spawnedTaskId: string | undefined = undefined
2427

25-
// Cancel the current task (which should be the subtask).
26-
await api.cancelTask()
28+
// Wait for the subtask to be spawned and then cancel it.
29+
api.on("taskSpawned", (_, childTaskId) => (spawnedTaskId = childTaskId))
30+
await waitFor(() => !!spawnedTaskId)
31+
await sleep(2_000) // Give the task a chance to start and populate the history.
32+
await api.cancelCurrentTask()
2733

28-
// Check if the parent task is still waiting (not resumed). We need to
29-
// wait a bit to ensure any task resumption would have happened.
30-
await sleep(5_000)
34+
// Wait a bit to ensure any task resumption would have happened.
35+
await sleep(2_000)
3136

3237
// The parent task should not have resumed yet, so we shouldn't see
3338
// "Parent task resumed".
3439
assert.ok(
35-
!api.getMessages().some(({ type, text }) => type === "say" && text?.includes("Parent task resumed")),
36-
"Parent task should not have resumed after subtask cancellation.",
40+
getMessage({
41+
api,
42+
taskId: parentTaskId,
43+
include: "Parent task resumed",
44+
exclude: "You are the parent task",
45+
}) === undefined,
46+
"Parent task should not have resumed after subtask cancellation",
3747
)
3848

3949
// Start a new task with the same message as the subtask.
40-
await api.startNewTask("You are the subtask")
41-
42-
// Wait for the subtask to complete.
43-
await waitForMessage(api, { include: "Task complete" })
50+
const anotherTaskId = await api.startNewTask(childPrompt)
51+
await waitForCompletion({ api, taskId: anotherTaskId })
4452

45-
// Verify that the parent task is still not resumed. We need to wait a
46-
// bit to ensure any task resumption would have happened.
47-
await sleep(5_000)
53+
// Wait a bit to ensure any task resumption would have happened.
54+
await sleep(2_000)
4855

4956
// The parent task should still not have resumed.
5057
assert.ok(
51-
!api.getMessages().some(({ type, text }) => type === "say" && text?.includes("Parent task resumed")),
52-
"Parent task should not have resumed after subtask completion.",
58+
getMessage({
59+
api,
60+
taskId: parentTaskId,
61+
include: "Parent task resumed",
62+
exclude: "You are the parent task",
63+
}) === undefined,
64+
"Parent task should not have resumed after subtask cancellation",
5365
)
5466

5567
// Clean up - cancel all tasks.
56-
await api.cancelTask()
68+
await api.clearCurrentTask()
69+
await waitForCompletion({ api, taskId: parentTaskId })
5770
})
5871
})

e2e/src/suite/task.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ suite("Roo Code Task", () => {
44
test("Should handle prompt and response correctly", async function () {
55
const api = globalThis.api
66
await api.setConfiguration({ mode: "Ask", alwaysAllowModeSwitch: true, autoApprovalEnabled: true })
7-
await api.startNewTask("Hello world, what is your name? Respond with 'My name is ...'")
8-
await waitForMessage(api, { include: "My name is Roo" })
7+
const taskId = await api.startNewTask("Hello world, what is your name? Respond with 'My name is ...'")
8+
await waitForMessage({ api, taskId, include: "My name is Roo" })
99
})
1010
})

0 commit comments

Comments
 (0)