diff --git a/.dockerignore b/.dockerignore
index eacfebecb2..11d03a1c54 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,18 +1,91 @@
-# Build artifacts
+# git
+.git
+
+# build artifacts
bin/
-!bin/roo-code-latest.vsix
dist/
**/dist/
out/
**/out/
+src/webview-ui/
-# Dependencies
+# dependencies
node_modules/
**/node_modules/
-# Test and development files
+# testing
coverage/
**/.vscode-test/
+**/mock/
+# devtools
knip.json
.husky/
+
+# monorepo
+.turbo/
+**/.turbo/
+
+# next.js
+**/.next/
+.vercel
+
+# Ignore common development files
+node_modules
+.git
+.gitignore
+.dockerignore
+.env*
+.vscode
+.idea
+
+# Ignore build artifacts
+dist
+build
+*.log
+*.tmp
+.cache
+coverage
+
+# Ignore OS files
+.DS_Store
+Thumbs.db
+
+# Ignore test files
+__tests__
+*.test.js
+*.spec.js
+*.test.ts
+*.spec.ts
+
+# Ignore development config files
+.eslintrc*
+.prettierrc*
+jest.config*
+
+# Ignore most directories except what we need for the build
+apps/
+evals/
+webview-ui/node_modules
+src/node_modules
+
+# Keep essential files for the build
+!README.md
+!CHANGELOG.md
+!package.json
+!pnpm-lock.yaml
+!pnpm-workspace.yaml
+!scripts/bootstrap.mjs
+!apps/web-evals/
+!src/
+!webview-ui/
+!packages/evals/.docker/entrypoints/runner.sh
+!packages/build/
+!packages/cloud/
+!packages/config-eslint/
+!packages/config-typescript/
+!packages/evals/
+!packages/ipc/
+!packages/telemetry/
+!packages/types/
+!locales/
diff --git a/apps/web-evals/package.json b/apps/web-evals/package.json
index 87e3d93c21..80f63ab9ec 100644
--- a/apps/web-evals/package.json
+++ b/apps/web-evals/package.json
@@ -5,7 +5,7 @@
"scripts": {
"lint": "next lint",
"check-types": "tsc -b",
- "dev": "next dev --turbopack",
+ "dev": "scripts/check-services.sh && next dev --turbopack",
"format": "prettier --write src",
"build": "next build",
"start": "next start"
@@ -25,7 +25,6 @@
"@radix-ui/react-tabs": "^1.1.3",
"@radix-ui/react-tooltip": "^1.1.8",
"@roo-code/evals": "workspace:^",
- "@roo-code/ipc": "workspace:^",
"@roo-code/types": "workspace:^",
"@tanstack/react-query": "^5.69.0",
"class-variance-authority": "^0.7.1",
@@ -36,11 +35,11 @@
"next": "^15.2.5",
"next-themes": "^0.4.6",
"p-map": "^7.0.3",
- "ps-tree": "^1.2.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.57.0",
"react-use": "^17.6.0",
+ "redis": "^5.5.5",
"sonner": "^2.0.5",
"tailwind-merge": "^3.3.0",
"tailwindcss-animate": "^1.0.7",
@@ -54,6 +53,7 @@
"@types/ps-tree": "^1.1.6",
"@types/react": "^18.3.23",
"@types/react-dom": "^18.3.5",
- "tailwindcss": "^4"
+ "tailwindcss": "^4",
+ "vitest": "^3.2.1"
}
}
diff --git a/apps/web-evals/scripts/check-services.sh b/apps/web-evals/scripts/check-services.sh
new file mode 100755
index 0000000000..fd1e74997c
--- /dev/null
+++ b/apps/web-evals/scripts/check-services.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+if ! docker info &> /dev/null; then
+ echo "โ Docker is not running. Please start Docker Desktop and try again."
+ exit 1
+fi
+
+if ! nc -z localhost 5432 2>/dev/null; then
+ echo "โ PostgreSQL is not running on port 5432"
+ echo "๐ก Start it with: pnpm --filter @roo-code/evals db:start"
+ exit 1
+fi
+
+if ! nc -z localhost 6379 2>/dev/null; then
+ echo "โ Redis is not running on port 6379"
+ echo "๐ก Start it with: pnpm --filter @roo-code/evals redis:start"
+ exit 1
+fi
+
+echo "โ
All required services are running"
diff --git a/apps/web-evals/src/app/api/runs/[id]/stream/route.ts b/apps/web-evals/src/app/api/runs/[id]/stream/route.ts
index 5b1de60710..3168974ecd 100644
--- a/apps/web-evals/src/app/api/runs/[id]/stream/route.ts
+++ b/apps/web-evals/src/app/api/runs/[id]/stream/route.ts
@@ -1,10 +1,10 @@
import type { NextRequest } from "next/server"
+import { taskEventSchema } from "@roo-code/types"
import { findRun } from "@roo-code/evals"
-import { IpcClient } from "@roo-code/ipc"
-import { IpcMessageType } from "@roo-code/types"
import { SSEStream } from "@/lib/server/sse-stream"
+import { redisClient } from "@/lib/server/redis"
export const dynamic = "force-dynamic"
@@ -13,26 +13,58 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
const requestId = crypto.randomUUID()
const stream = new SSEStream()
const run = await findRun(Number(id))
- const client = new IpcClient(run.socketPath, () => {})
+ const redis = await redisClient()
- const write = async (data: string | object) => {
- // console.log(`[stream#${requestId}] write`, data)
- const success = await stream.write(data)
+ let isStreamClosed = false
+ const channelName = `evals:${run.id}`
- if (!success) {
- client.disconnect()
+ const onMessage = async (data: string) => {
+ if (isStreamClosed || stream.isClosed) {
+ return
+ }
+
+ try {
+ const taskEvent = taskEventSchema.parse(JSON.parse(data))
+ // console.log(`[stream#${requestId}] task event -> ${taskEvent.eventName}`)
+ const writeSuccess = await stream.write(JSON.stringify(taskEvent))
+
+ if (!writeSuccess) {
+ await disconnect()
+ }
+ } catch (_error) {
+ console.error(`[stream#${requestId}] invalid task event:`, data)
+ }
+ }
+
+ const disconnect = async () => {
+ if (isStreamClosed) {
+ return
+ }
+
+ isStreamClosed = true
+
+ try {
+ await redis.unsubscribe(channelName)
+ console.log(`[stream#${requestId}] unsubscribed from ${channelName}`)
+ } catch (error) {
+ console.error(`[stream#${requestId}] error unsubscribing:`, error)
+ }
+
+ try {
+ await stream.close()
+ } catch (error) {
+ console.error(`[stream#${requestId}] error closing stream:`, error)
}
}
- console.log(`[stream#${requestId}] connect`)
- client.on(IpcMessageType.Connect, () => write("connect"))
- client.on(IpcMessageType.Disconnect, () => write("disconnect"))
- client.on(IpcMessageType.TaskEvent, write)
+ await redis.subscribe(channelName, onMessage)
request.signal.addEventListener("abort", () => {
console.log(`[stream#${requestId}] abort`)
- client.disconnect()
- stream.close().catch(() => {})
+
+ disconnect().catch((error) => {
+ console.error(`[stream#${requestId}] cleanup error:`, error)
+ })
})
return stream.getResponse()
diff --git a/apps/web-evals/src/app/api/runs/route.ts b/apps/web-evals/src/app/api/runs/route.ts
deleted file mode 100644
index b21bb3b655..0000000000
--- a/apps/web-evals/src/app/api/runs/route.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { NextResponse } from "next/server"
-
-import { createRun } from "@roo-code/evals"
-
-export async function POST(request: Request) {
- try {
- const run = await createRun(await request.json())
- return NextResponse.json({ run }, { status: 201 })
- } catch (error) {
- return NextResponse.json({ error: (error as Error).message }, { status: 500 })
- }
-}
diff --git a/apps/web-evals/src/app/api/tasks/route.ts b/apps/web-evals/src/app/api/tasks/route.ts
deleted file mode 100644
index 843c078b9b..0000000000
--- a/apps/web-evals/src/app/api/tasks/route.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { NextResponse } from "next/server"
-
-import { createTask } from "@roo-code/evals"
-
-export async function POST(request: Request) {
- try {
- const task = await createTask(await request.json())
- return NextResponse.json({ task }, { status: 201 })
- } catch (error) {
- return NextResponse.json({ error: (error as Error).message }, { status: 500 })
- }
-}
diff --git a/apps/web-evals/src/app/runs/[id]/connection-status.tsx b/apps/web-evals/src/app/runs/[id]/connection-status.tsx
index 60d6141a53..1505050b2d 100644
--- a/apps/web-evals/src/app/runs/[id]/connection-status.tsx
+++ b/apps/web-evals/src/app/runs/[id]/connection-status.tsx
@@ -1,29 +1,17 @@
"use client"
-import { useCallback } from "react"
-import { Skull } from "lucide-react"
-
-import { killProcessTree } from "@/lib/server/processes"
-import { EventSourceStatus } from "@/hooks/use-event-source"
-import { useProcessList } from "@/hooks/use-process-tree"
+import type { EventSourceStatus } from "@/hooks/use-event-source"
+import { useRunners } from "@/hooks/use-runners"
import { cn } from "@/lib/utils"
-import { Button } from "@/components/ui"
type ConnectionStatusProps = {
status: EventSourceStatus
- pid: number | null
+ runId: number
}
export const ConnectionStatus = (connectionStatus: ConnectionStatusProps) => {
- const { data: pids, isLoading } = useProcessList(connectionStatus.pid)
- const status = isLoading ? "loading" : pids === null ? "dead" : connectionStatus.status
-
- const onKill = useCallback(async () => {
- if (connectionStatus.pid) {
- await killProcessTree(connectionStatus.pid)
- window.location.reload()
- }
- }, [connectionStatus.pid])
+ const { data: runners, isLoading } = useRunners(connectionStatus.runId)
+ const status = isLoading ? "loading" : runners === null ? "dead" : connectionStatus.status
return (
@@ -52,16 +40,9 @@ export const ConnectionStatus = (connectionStatus: ConnectionStatusProps) => {
-
PIDs:
-
{connectionStatus.pid}
- {status === "connected" && (
- <>
-
{pids?.join(" ")}
-
- >
+
Runners:
+ {runners && runners.length > 0 && (
+
{runners?.join(", ")}
)}
diff --git a/apps/web-evals/src/app/runs/[id]/run.tsx b/apps/web-evals/src/app/runs/[id]/run.tsx
index 576fb12a2e..ba93b2940a 100644
--- a/apps/web-evals/src/app/runs/[id]/run.tsx
+++ b/apps/web-evals/src/app/runs/[id]/run.tsx
@@ -48,7 +48,7 @@ export function Run({ run }: { run: Run }) {
{run.model}
{run.description && {run.description}
}
- {!run.taskMetricsId && }
+ {!run.taskMetricsId && }
{!tasks ? (
diff --git a/apps/web-evals/src/app/runs/new/defaults.ts b/apps/web-evals/src/app/runs/new/defaults.ts
deleted file mode 100644
index b77460d36a..0000000000
--- a/apps/web-evals/src/app/runs/new/defaults.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import { RooCodeSettings } from "@roo-code/types"
-
-export const rooCodeDefaults: RooCodeSettings = {
- apiProvider: "openrouter",
- openRouterUseMiddleOutTransform: false,
-
- lastShownAnnouncementId: "may-21-2025-3-18",
-
- pinnedApiConfigs: {},
-
- autoApprovalEnabled: true,
- alwaysAllowReadOnly: true,
- alwaysAllowReadOnlyOutsideWorkspace: false,
- alwaysAllowWrite: true,
- alwaysAllowWriteOutsideWorkspace: false,
- writeDelayMs: 1000,
- alwaysAllowBrowser: true,
- alwaysApproveResubmit: true,
- requestDelaySeconds: 10,
- alwaysAllowMcp: true,
- alwaysAllowModeSwitch: true,
- alwaysAllowSubtasks: true,
- alwaysAllowExecute: true,
- allowedCommands: ["*"],
-
- browserToolEnabled: false,
- browserViewportSize: "900x600",
- screenshotQuality: 75,
- remoteBrowserEnabled: false,
-
- ttsEnabled: false,
- ttsSpeed: 1,
- soundEnabled: false,
- soundVolume: 0.5,
-
- terminalOutputLineLimit: 500,
- terminalShellIntegrationTimeout: 30000,
- terminalCommandDelay: 0,
- terminalPowershellCounter: false,
- terminalZshOhMy: true,
- terminalZshClearEolMark: true,
- terminalZshP10k: false,
- terminalZdotdir: true,
- terminalCompressProgressBar: true,
- terminalShellIntegrationDisabled: false,
-
- diffEnabled: true,
- fuzzyMatchThreshold: 1,
-
- enableCheckpoints: false,
-
- rateLimitSeconds: 0,
- maxOpenTabsContext: 20,
- maxWorkspaceFiles: 200,
- showRooIgnoredFiles: true,
- maxReadFileLine: -1, // -1 to enable full file reading.
-
- language: "en",
- telemetrySetting: "enabled",
-
- mcpEnabled: false,
-
- mode: "code",
-
- customModes: [],
-}
diff --git a/apps/web-evals/src/app/runs/new/new-run.tsx b/apps/web-evals/src/app/runs/new/new-run.tsx
index 535094fcd5..43190ca6d6 100644
--- a/apps/web-evals/src/app/runs/new/new-run.tsx
+++ b/apps/web-evals/src/app/runs/new/new-run.tsx
@@ -9,12 +9,13 @@ import fuzzysort from "fuzzysort"
import { toast } from "sonner"
import { X, Rocket, Check, ChevronsUpDown, SlidersHorizontal, Book, CircleCheck } from "lucide-react"
-import { globalSettingsSchema, providerSettingsSchema } from "@roo-code/types"
+import { globalSettingsSchema, providerSettingsSchema, EVALS_SETTINGS, getModelId } from "@roo-code/types"
import { createRun } from "@/lib/server/runs"
import {
createRunSchema as formSchema,
type CreateRun as FormValues,
+ MODEL_DEFAULT,
CONCURRENCY_MIN,
CONCURRENCY_MAX,
CONCURRENCY_DEFAULT,
@@ -51,26 +52,25 @@ import {
DialogFooter,
} from "@/components/ui"
-import { rooCodeDefaults } from "./defaults"
import { SettingsDiff } from "./settings-diff"
export function NewRun() {
const router = useRouter()
const [mode, setMode] = useState<"openrouter" | "settings">("openrouter")
-
const [modelSearchValue, setModelSearchValue] = useState("")
const [modelPopoverOpen, setModelPopoverOpen] = useState(false)
+
const modelSearchResultsRef = useRef