Skip to content

Commit 6bccb80

Browse files
committed
Merge branch 'main' into refactor-mcp
2 parents dfa809c + 5f19ea4 commit 6bccb80

File tree

128 files changed

+2185
-1602
lines changed

Some content is hidden

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

128 files changed

+2185
-1602
lines changed

CHANGELOG.md

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

3+
## [3.11.17] - 2025-04-14
4+
5+
- Improvements to OpenAI cache reporting and cost estimates (thanks @monotykamary and Cline!)
6+
- Visual improvements to the auto-approve toggles (thanks @sachasayan!)
7+
- Bugfix to diff apply logic (thanks @avtc for the test case!) and telemetry to track errors going forward
8+
- Fix race condition in capturing short-running terminal commands (thanks @KJ7LNW!)
9+
- Fix eslint error (thanks @nobu007!)
10+
11+
## [3.11.16] - 2025-04-14
12+
13+
- Add gpt-4.1, gpt-4.1-mini, and gpt-4.1-nano to the OpenAI provider
14+
- Include model ID in environment details and when exporting tasks (thanks @feifei325!)
15+
16+
## [3.11.15] - 2025-04-13
17+
18+
- Add ability to filter task history by workspace (thanks @samhvw8!)
19+
- Fix Node.js version in the .tool-versions file (thanks @bogdan0083!)
20+
- Fix duplicate suggested mentions for open tabs (thanks @samhvw8!)
21+
- Fix Bedrock ARN validation and token expiry issue when using profiles (thanks @vagadiya!)
22+
- Add Anthropic option to pass API token as Authorization header instead of X-Api-Key (thanks @mecab!)
23+
- Better documentation for adding new settings (thanks @KJ7LNW!)
24+
- Localize package.json (thanks @samhvw8!)
25+
- Add option to hide the welcome message and fix the background color for the new profile dialog (thanks @zhangtony239!)
26+
- Restore the focus ring for the VSCodeButton component (thanks @pokutuna!)
27+
328
## [3.11.14] - 2025-04-11
429

530
- Support symbolic links in rules folders to directories and other symbolic links (thanks @taisukeoe!)

README.md

Lines changed: 23 additions & 22 deletions
Large diffs are not rendered by default.

e2e/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"version": "0.1.0",
44
"private": true,
55
"scripts": {
6-
"lint": "eslint src --ext ts",
6+
"lint": "eslint src/**/*.ts",
77
"check-types": "tsc --noEmit",
88
"test": "npm run build && npx dotenvx run -f .env.local -- node ./out/runTest.js",
99
"ci": "npm run vscode-test && npm run test",

evals/apps/cli/package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"private": true,
44
"type": "module",
55
"scripts": {
6-
"lint": "eslint src --ext ts --max-warnings=0",
6+
"lint": "eslint src/**/*.ts --max-warnings=0",
77
"check-types": "tsc --noEmit",
88
"format": "prettier --write src",
99
"dev": "dotenvx run -f ../../.env -- tsx src/index.ts"
@@ -16,10 +16,12 @@
1616
"execa": "^9.5.2",
1717
"gluegun": "^5.1.2",
1818
"p-map": "^7.0.3",
19-
"p-wait-for": "^5.0.2"
19+
"p-wait-for": "^5.0.2",
20+
"ps-tree": "^1.2.0"
2021
},
2122
"devDependencies": {
2223
"@evals/eslint-config": "workspace:^",
23-
"@evals/typescript-config": "workspace:^"
24+
"@evals/typescript-config": "workspace:^",
25+
"@types/ps-tree": "^1.1.6"
2426
}
2527
}

evals/apps/cli/src/index.ts

Lines changed: 93 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import pMap from "p-map"
66
import pWaitFor from "p-wait-for"
77
import { execa, parseCommandString } from "execa"
88
import { build, filesystem, GluegunPrompt, GluegunToolbox } from "gluegun"
9+
import psTree from "ps-tree"
910

1011
import {
1112
type ExerciseLanguage,
@@ -36,8 +37,9 @@ import { getExercises } from "./exercises.js"
3637
type TaskResult = { success: boolean; retry: boolean }
3738
type TaskPromise = Promise<TaskResult>
3839

39-
const TASK_TIMEOUT = 10 * 60 * 1_000
40-
const UNIT_TEST_TIMEOUT = 60 * 1_000
40+
const TASK_START_DELAY = 10 * 1_000
41+
const TASK_TIMEOUT = 5 * 60 * 1_000
42+
const UNIT_TEST_TIMEOUT = 2 * 60 * 1_000
4143

4244
const testCommands: Record<ExerciseLanguage, { commands: string[]; timeout?: number; cwd?: string }> = {
4345
go: { commands: ["go test"] }, // timeout 15s bash -c "cd '$dir' && go test > /dev/null 2>&1"
@@ -98,13 +100,11 @@ const run = async (toolbox: GluegunToolbox) => {
98100
throw new Error("No tasks found.")
99101
}
100102

101-
console.log(await execa({ cwd: exercisesPath })`git config user.name "Roo Code"`)
102-
console.log(await execa({ cwd: exercisesPath })`git config user.email "[email protected]"`)
103-
console.log(await execa({ cwd: exercisesPath })`git checkout -f`)
104-
console.log(await execa({ cwd: exercisesPath })`git clean -fd`)
105-
console.log(
106-
await execa({ cwd: exercisesPath })`git checkout -b runs/${run.id}-${crypto.randomUUID().slice(0, 8)} main`,
107-
)
103+
await execa({ cwd: exercisesPath })`git config user.name "Roo Code"`
104+
await execa({ cwd: exercisesPath })`git config user.email "[email protected]"`
105+
await execa({ cwd: exercisesPath })`git checkout -f`
106+
await execa({ cwd: exercisesPath })`git clean -fd`
107+
await execa({ cwd: exercisesPath })`git checkout -b runs/${run.id}-${crypto.randomUUID().slice(0, 8)} main`
108108

109109
fs.writeFileSync(
110110
path.resolve(exercisesPath, "settings.json"),
@@ -145,11 +145,11 @@ const run = async (toolbox: GluegunToolbox) => {
145145
}
146146
}
147147

148-
let delay = 0
148+
let delay = TASK_START_DELAY
149149

150150
for (const task of tasks) {
151151
const promise = processTask(task, delay)
152-
delay = delay + 5_000
152+
delay = delay + TASK_START_DELAY
153153
runningPromises.push(promise)
154154
promise.then(() => processTaskResult(task, promise))
155155

@@ -162,10 +162,10 @@ const run = async (toolbox: GluegunToolbox) => {
162162
await Promise.all(runningPromises)
163163

164164
const result = await finishRun(run.id)
165-
console.log("[cli#run]", result)
165+
console.log(`${Date.now()} [cli#run]`, result)
166166

167-
console.log(await execa({ cwd: exercisesPath })`git add .`)
168-
console.log(await execa({ cwd: exercisesPath })`git commit -m ${`Run #${run.id}`} --no-verify`)
167+
await execa({ cwd: exercisesPath })`git add .`
168+
await execa({ cwd: exercisesPath })`git commit -m ${`Run #${run.id}`} --no-verify`
169169
}
170170

171171
const runExercise = async ({ run, task, server }: { run: Run; task: Task; server: IpcServer }): TaskPromise => {
@@ -180,9 +180,7 @@ const runExercise = async ({ run, task, server }: { run: Run; task: Task; server
180180
// Don't await execa and store result as subprocess.
181181
// subprocess.stdout.pipe(process.stdout)
182182

183-
// Sleep for a random amount of time before opening a new VSCode window.
184-
await new Promise((resolve) => setTimeout(resolve, 1_000 + Math.random() * 5_000))
185-
console.log(`Opening new VS Code window at ${workspacePath}`)
183+
console.log(`${Date.now()} [cli#runExercise] Opening new VS Code window at ${workspacePath}`)
186184

187185
await execa({
188186
env: {
@@ -192,15 +190,15 @@ const runExercise = async ({ run, task, server }: { run: Run; task: Task; server
192190
})`code --disable-workspace-trust -n ${workspacePath}`
193191

194192
// Give VSCode some time to spawn before connecting to its unix socket.
195-
await new Promise((resolve) => setTimeout(resolve, 1_000 + Math.random() * 4_000))
196-
console.log(`Connecting to ${taskSocketPath}`)
193+
await new Promise((resolve) => setTimeout(resolve, 3_000))
194+
console.log(`${Date.now()} [cli#runExercise] Connecting to ${taskSocketPath}`)
197195
const client = new IpcClient(taskSocketPath)
198196

199197
try {
200198
await pWaitFor(() => client.isReady, { interval: 250, timeout: 5_000 })
201199
// eslint-disable-next-line @typescript-eslint/no-unused-vars
202200
} catch (error) {
203-
console.log(`[cli#runExercise | ${language} / ${exercise}] unable to connect`)
201+
console.log(`${Date.now()} [cli#runExercise | ${language} / ${exercise}] unable to connect`)
204202
client.disconnect()
205203
return { success: false, retry: false }
206204
}
@@ -220,16 +218,20 @@ const runExercise = async ({ run, task, server }: { run: Run; task: Task; server
220218
client.on(IpcMessageType.TaskEvent, async (taskEvent) => {
221219
const { eventName, payload } = taskEvent
222220

223-
server.broadcast({
224-
type: IpcMessageType.TaskEvent,
225-
origin: IpcOrigin.Server,
226-
relayClientId: client.clientId!,
227-
data: { ...taskEvent, taskId: task.id },
228-
})
221+
if (taskEvent.eventName !== RooCodeEventName.Message) {
222+
server.broadcast({
223+
type: IpcMessageType.TaskEvent,
224+
origin: IpcOrigin.Server,
225+
relayClientId: client.clientId!,
226+
data: { ...taskEvent, taskId: task.id },
227+
})
228+
}
229229

230230
if (!ignoreEvents.includes(eventName)) {
231-
console.log(`[cli#runExercise | ${language} / ${exercise}] taskEvent -> ${eventName}`)
232-
console.log(payload)
231+
console.log(
232+
`${Date.now()} [cli#runExercise | ${language} / ${exercise}] taskEvent -> ${eventName}`,
233+
payload,
234+
)
233235
}
234236

235237
if (eventName === RooCodeEventName.TaskStarted) {
@@ -279,11 +281,11 @@ const runExercise = async ({ run, task, server }: { run: Run; task: Task; server
279281
})
280282

281283
client.on(IpcMessageType.Disconnect, async () => {
282-
console.log(`[cli#runExercise | ${language} / ${exercise}] disconnect`)
284+
console.log(`${Date.now()} [cli#runExercise | ${language} / ${exercise}] disconnect`)
283285
isClientDisconnected = true
284286
})
285287

286-
console.log(`[cli#runExercise | ${language} / ${exercise}] starting task`)
288+
console.log(`${Date.now()} [cli#runExercise | ${language} / ${exercise}] starting task`)
287289

288290
client.sendMessage({
289291
type: IpcMessageType.TaskCommand,
@@ -307,7 +309,7 @@ const runExercise = async ({ run, task, server }: { run: Run; task: Task; server
307309
await pWaitFor(() => !!taskFinishedAt || isClientDisconnected, { interval: 1_000, timeout: TASK_TIMEOUT })
308310
// eslint-disable-next-line @typescript-eslint/no-unused-vars
309311
} catch (error) {
310-
console.log(`[cli#runExercise | ${language} / ${exercise}] time limit reached`)
312+
console.log(`${Date.now()} [cli#runExercise | ${language} / ${exercise}] time limit reached`)
311313

312314
// Cancel the task.
313315
if (rooTaskId && !isClientDisconnected) {
@@ -351,17 +353,73 @@ const runUnitTest = async ({ task }: { task: Task }) => {
351353
let passed = true
352354

353355
for (const command of commands) {
354-
const timeout = cmd.timeout ?? UNIT_TEST_TIMEOUT
355-
356356
try {
357-
const result = await execa({ cwd, shell: true, reject: false, timeout })`${command}`
357+
console.log(
358+
`${Date.now()} [cli#runUnitTest | ${task.language} / ${task.exercise}] running "${command.join(" ")}"`,
359+
)
360+
361+
const subprocess = execa({ cwd, shell: true, reject: false })`${command}`
362+
363+
const timeout = setTimeout(async () => {
364+
const descendants = await new Promise<number[]>((resolve, reject) => {
365+
psTree(subprocess.pid!, (err, children) => {
366+
if (err) {
367+
reject(err)
368+
}
369+
370+
resolve(children.map((p) => parseInt(p.PID)))
371+
})
372+
})
373+
374+
console.log(
375+
`${Date.now()} [cli#runUnitTest | ${task.language} / ${task.exercise}] "${command.join(" ")}": unit tests timed out, killing ${subprocess.pid} + ${JSON.stringify(descendants)}`,
376+
)
377+
378+
if (descendants.length > 0) {
379+
for (const descendant of descendants) {
380+
try {
381+
console.log(
382+
`${Date.now()} [cli#runUnitTest | ${task.language} / ${task.exercise}] killing ${descendant}`,
383+
)
384+
385+
await execa`kill -9 ${descendant}`
386+
} catch (error) {
387+
console.error(
388+
`${Date.now()} [cli#runUnitTest | ${task.language} / ${task.exercise}] Error killing descendant processes:`,
389+
error,
390+
)
391+
}
392+
}
393+
}
394+
395+
console.log(
396+
`${Date.now()} [cli#runUnitTest | ${task.language} / ${task.exercise}] killing ${subprocess.pid}`,
397+
)
398+
399+
try {
400+
await execa`kill -9 ${subprocess.pid!}`
401+
} catch (error) {
402+
console.error(
403+
`${Date.now()} [cli#runUnitTest | ${task.language} / ${task.exercise}] Error killing process:`,
404+
error,
405+
)
406+
}
407+
}, UNIT_TEST_TIMEOUT)
408+
409+
const result = await subprocess
410+
411+
console.log(
412+
`${Date.now()} [cli#runUnitTest | ${task.language} / ${task.exercise}] "${command.join(" ")}" result -> ${JSON.stringify(result)}`,
413+
)
414+
415+
clearTimeout(timeout)
358416

359417
if (result.failed) {
360418
passed = false
361419
break
362420
}
363421
} catch (error) {
364-
console.log("[cli#runUnitTest]", error)
422+
console.log(`${Date.now()} [cli#runUnitTest | ${task.language} / ${task.exercise}]`, error)
365423
passed = false
366424
break
367425
}

0 commit comments

Comments
 (0)