Просмотр исходного кода

Merge branch 'upstream-at-v3.19.3' into roo-v3.19.3

Kevin van Dijk 8 месяцев назад
Родитель
Сommit
7aac6ff4b8
100 измененных файлов с 4830 добавлено и 476 удалено
  1. 18 0
      .dockerignore
  2. 4 0
      .env.sample
  3. 1 1
      .nvmrc
  4. 1 1
      .tool-versions
  5. 13 0
      MONOREPO.md
  6. 1 1
      apps/vscode-e2e/package.json
  7. 1 1
      evals/.tool-versions
  8. 78 0
      evals/Dockerfile
  9. 46 17
      evals/apps/cli/src/index.ts
  10. 2 2
      evals/apps/web/package.json
  11. 8 1
      evals/package.json
  12. 1 2
      evals/packages/types/src/roo-code.ts
  13. 206 181
      evals/pnpm-lock.yaml
  14. 3 3
      evals/scripts/setup.sh
  15. 1 1
      package.json
  16. 1 1
      packages/build/package.json
  17. 11 1
      packages/build/src/__tests__/index.test.ts
  18. 4 4
      packages/build/src/esbuild.ts
  19. 8 0
      packages/build/vitest.config.ts
  20. 4 0
      packages/cloud/eslint.config.mjs
  21. 26 0
      packages/cloud/package.json
  22. 448 0
      packages/cloud/src/AuthService.ts
  23. 168 0
      packages/cloud/src/CloudService.ts
  24. 2 0
      packages/cloud/src/Config.ts
  25. 1 1
      packages/cloud/src/RefreshTimer.ts
  26. 137 0
      packages/cloud/src/SettingsService.ts
  27. 104 0
      packages/cloud/src/TelemetryClient.ts
  28. 50 0
      packages/cloud/src/__mocks__/vscode.ts
  29. 241 0
      packages/cloud/src/__tests__/CloudService.test.ts
  30. 22 22
      packages/cloud/src/__tests__/RefreshTimer.test.ts
  31. 429 0
      packages/cloud/src/__tests__/TelemetryClient.test.ts
  32. 1 0
      packages/cloud/src/index.ts
  33. 3 0
      packages/cloud/src/types.ts
  34. 5 0
      packages/cloud/tsconfig.json
  35. 13 0
      packages/cloud/vitest.config.ts
  36. 12 0
      packages/config-typescript/vscode-library.json
  37. 4 0
      packages/telemetry/eslint.config.mjs
  38. 25 0
      packages/telemetry/package.json
  39. 7 3
      packages/telemetry/src/BaseTelemetryClient.ts
  40. 2 12
      packages/telemetry/src/PostHogTelemetryClient.ts
  41. 31 32
      packages/telemetry/src/TelemetryService.ts
  42. 42 48
      packages/telemetry/src/__tests__/PostHogTelemetryClient.test.ts
  43. 3 0
      packages/telemetry/src/index.ts
  44. 5 0
      packages/telemetry/tsconfig.json
  45. 8 0
      packages/telemetry/vitest.config.ts
  46. 40 0
      packages/types/npm/package.json
  47. 4 6
      packages/types/package.json
  48. 1 1
      packages/types/src/__tests__/index.test.ts
  49. 54 0
      packages/types/src/cloud.ts
  50. 2 2
      packages/types/src/experiment.ts
  51. 4 0
      packages/types/src/global-settings.ts
  52. 3 0
      packages/types/src/index.ts
  53. 1 0
      packages/types/src/message.ts
  54. 4 0
      packages/types/src/provider-settings.ts
  55. 100 0
      packages/types/src/providers/anthropic.ts
  56. 432 0
      packages/types/src/providers/bedrock.ts
  57. 229 0
      packages/types/src/providers/chutes.ts
  58. 33 0
      packages/types/src/providers/deepseek.ts
  59. 221 0
      packages/types/src/providers/gemini.ts
  60. 20 0
      packages/types/src/providers/glama.ts
  61. 80 0
      packages/types/src/providers/groq.ts
  62. 17 0
      packages/types/src/providers/index.ts
  63. 48 0
      packages/types/src/providers/lite-llm.ts
  64. 1 0
      packages/types/src/providers/lm-studio.ts
  65. 59 0
      packages/types/src/providers/mistral.ts
  66. 200 0
      packages/types/src/providers/openai.ts
  67. 75 0
      packages/types/src/providers/openrouter.ts
  68. 19 0
      packages/types/src/providers/requesty.ts
  69. 14 0
      packages/types/src/providers/unbound.ts
  70. 225 0
      packages/types/src/providers/vertex.ts
  71. 161 0
      packages/types/src/providers/vscode-llm.ts
  72. 157 0
      packages/types/src/providers/xai.ts
  73. 48 13
      packages/types/src/telemetry.ts
  74. 1 0
      packages/types/src/vscode.ts
  75. 7 0
      packages/types/vitest.config.ts
  76. 79 66
      pnpm-lock.yaml
  77. 3 1
      renovate.json
  78. 3 2
      src/__mocks__/@modelcontextprotocol/sdk/client/streamableHttp.js
  79. 9 0
      src/activate/handleUri.ts
  80. 22 0
      src/activate/registerCommands.ts
  81. 2 2
      src/api/providers/__tests__/bedrock-custom-arn.test.ts
  82. 178 0
      src/api/providers/__tests__/bedrock-vpc-endpoint.test.ts
  83. 1 1
      src/api/providers/__tests__/chutes.test.ts
  84. 6 3
      src/api/providers/__tests__/deepseek.test.ts
  85. 1 2
      src/api/providers/__tests__/gemini.test.ts
  86. 1 1
      src/api/providers/__tests__/groq.test.ts
  87. 1 1
      src/api/providers/__tests__/lmstudio.test.ts
  88. 4 3
      src/api/providers/__tests__/xai.test.ts
  89. 9 4
      src/api/providers/anthropic-vertex.ts
  90. 8 3
      src/api/providers/anthropic.ts
  91. 1 0
      src/api/providers/base-provider.ts
  92. 15 12
      src/api/providers/bedrock.ts
  93. 3 1
      src/api/providers/chutes.ts
  94. 0 4
      src/api/providers/constants.ts
  95. 2 1
      src/api/providers/deepseek.ts
  96. 2 2
      src/api/providers/fetchers/__tests__/openrouter.spec.ts
  97. 9 1
      src/api/providers/fetchers/litellm.ts
  98. 5 4
      src/api/providers/fetchers/openrouter.ts
  99. 2 2
      src/api/providers/gemini.ts
  100. 3 3
      src/api/providers/glama.ts

+ 18 - 0
.dockerignore

@@ -0,0 +1,18 @@
+# Build artifacts
+bin/
+!bin/roo-code-latest.vsix
+dist/
+**/dist/
+out/
+**/out/
+
+# Dependencies
+node_modules/
+**/node_modules/
+
+# Test and development files
+coverage/
+**/.vscode-test/
+
+knip.json
+.husky/

+ 4 - 0
.env.sample

@@ -1 +1,5 @@
 POSTHOG_API_KEY=key-goes-here
+
+# Roo Code Cloud / Local Development
+CLERK_BASE_URL=https://epic-chamois-85.clerk.accounts.dev
+ROO_CODE_API_URL=http://localhost:3000

+ 1 - 1
.nvmrc

@@ -1 +1 @@
-v20.18.1
+v20.19.2

+ 1 - 1
.tool-versions

@@ -1 +1 @@
-nodejs 20.18.1
+nodejs 20.19.2

+ 13 - 0
MONOREPO.md

@@ -27,3 +27,16 @@ If things are in good working order then you should be able to build a vsix and
 pnpm build --out ../bin/kilo-code-main.vsix && \
   code --install-extension bin/kilo-code-main.vsix
 ```
+
+To fully stress the monorepo setup, run the following:
+
+```sh
+pnpm clean && pnpm lint
+pnpm clean && pnpm check-types
+pnpm clean && pnpm test
+pnpm clean && pnpm bundle
+pnpm clean && pnpm build
+pnpm clean && pnpm npx turbo watch:bundle
+pnpm clean && pnpm npx turbo watch:tsc
+cd apps/vscode-e2e && pnpm test:ci
+```

+ 1 - 1
apps/vscode-e2e/package.json

@@ -16,7 +16,7 @@
 		"@types/mocha": "^10.0.10",
 		"@types/node": "^22.14.1",
 		"@types/vscode": "^1.95.0",
-		"@vscode/test-cli": "^0.0.10",
+		"@vscode/test-cli": "^0.0.11",
 		"@vscode/test-electron": "^2.4.0",
 		"glob": "^11.0.1",
 		"mocha": "^11.1.0",

+ 1 - 1
evals/.tool-versions

@@ -1,4 +1,4 @@
 python 3.13.2
 golang 1.24.2
 rust 1.85.1
-nodejs 20.18.1
+nodejs 20.19.2

+ 78 - 0
evals/Dockerfile

@@ -0,0 +1,78 @@
+FROM node:20-slim AS base
+ ENV PNPM_HOME="/pnpm"
+ ENV PATH="$PNPM_HOME:$PATH"
+RUN corepack enable
+RUN npm install -g npm@latest
+RUN npm install -g npm-run-all
+# Install dependencies
+RUN apt update && apt install -y sudo curl git vim jq
+
+
+# Create a `vscode` user
+RUN useradd -m vscode -s /bin/bash && \
+  echo "vscode ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/vscode && \
+  chmod 0440 /etc/sudoers.d/vscode
+# Install VS Code
+# https://code.visualstudio.com/docs/setup/linux
+RUN apt install -y wget gpg apt-transport-https
+RUN wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > packages.microsoft.gpg
+RUN install -D -o root -g root -m 644 packages.microsoft.gpg /etc/apt/keyrings/packages.microsoft.gpg
+RUN echo "deb [arch=amd64,arm64,armhf signed-by=/etc/apt/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main" | tee /etc/apt/sources.list.d/vscode.list > /dev/null
+RUN rm -f packages.microsoft.gpg
+RUN apt update && apt install -y code
+# Install Xvfb
+RUN apt install -y xvfb
+# [cpp] Install cmake 3.28.3
+RUN apt install -y cmake
+# [go] Install Go 1.22.2
+RUN apt install -y golang-go
+# [java] Install Java 21
+RUN apt install -y default-jre
+# [python] Install Python 3.12.3 and uv 0.6.6
+RUN apt install -y python3 python3-venv python3-dev python3-pip
+# [rust] Install Rust 1.85
+RUN curl https://sh.rustup.rs -sSf | bash -s -- -y
+RUN echo 'source $HOME/.cargo/env' >> $HOME/.bashrc
+ WORKDIR /home/vscode
+ USER vscode
+
+ # Copy evals
+ RUN git clone https://github.com/RooCodeInc/Roo-Code-Evals.git evals
+
+ # Prepare evals
+ WORKDIR /home/vscode/evals/python
+ RUN curl -LsSf https://astral.sh/uv/install.sh | sh
+ RUN /home/vscode/.local/bin/uv sync
+
+ WORKDIR /home/vscode/repo/benchmark
+
+ # Install dependencies
+ COPY --chown=vscode:vscode ./evals/package.json ./evals/pnpm-lock.yaml ./evals/pnpm-workspace.yaml ./evals/.npmrc ./
+ RUN mkdir -p apps/cli apps/web \
+   config/eslint config/typescript \
+   packages/db packages/ipc packages/lib packages/types
+ COPY --chown=vscode:vscode ./evals/apps/cli/package.json          ./apps/cli/
+ COPY --chown=vscode:vscode ./evals/apps/web/package.json          ./apps/web/
+ COPY --chown=vscode:vscode ./evals/config/eslint/package.json     ./config/eslint/
+ COPY --chown=vscode:vscode ./evals/config/typescript/package.json ./config/typescript/
+ COPY --chown=vscode:vscode ./evals/packages/db/package.json       ./packages/db/
+ COPY --chown=vscode:vscode ./evals/packages/ipc/package.json      ./packages/ipc/
+ COPY --chown=vscode:vscode ./evals/packages/lib/package.json      ./packages/lib/
+ COPY --chown=vscode:vscode ./evals/packages/types/package.json    ./packages/types/
+ RUN pnpm install
+
+ # Copy & install extension
+ COPY --chown=vscode:vscode ./bin/roo-code-latest.vsix ./
+ RUN code --debug --install-extension ./roo-code-latest.vsix
+
+ # Copy application code
+ COPY --chown=vscode:vscode ./evals ./
+
+ # Copy environment variables
+ COPY --chown=vscode:vscode ./evals/.env ./
+
+ # Push database schema
+ RUN pnpm --filter @evals/db db:push
+
+ EXPOSE 3000
+ CMD ["pnpm", "web"]

+ 46 - 17
evals/apps/cli/src/index.ts

@@ -194,12 +194,31 @@ const runExercise = async ({ run, task, server }: { run: Run; task: Task; server
 
 	console.log(`${Date.now()} [cli#runExercise] Opening new VS Code window at ${workspacePath}`)
 
-	await execa({
+	const controller = new AbortController()
+	const cancelSignal = controller.signal
+
+	// If debugging:
+	// Use --wait --log trace or --verbose.
+	let codeCommand = `code --disable-workspace-trust`
+	const isDocker = fs.existsSync("/.dockerenv")
+
+	if (isDocker) {
+		if (run.concurrency > 1) {
+			throw new Error("Cannot run multiple tasks in parallel in Docker. Please set concurrency to 1.")
+		}
+		codeCommand = `xvfb-run --auto-servernum --server-num=1 ${codeCommand} --wait --log trace --disable-gpu --password-store="basic"`
+	}
+
+	const subprocess = execa({
 		env: {
 			ROO_CODE_IPC_SOCKET_PATH: taskSocketPath,
 		},
 		shell: "/bin/bash",
-	})`code --disable-workspace-trust -n ${workspacePath}`
+		cancelSignal,
+	})`${codeCommand} -n ${workspacePath}`
+
+	// If debugging:
+	// subprocess.stdout.pipe(process.stdout)
 
 	// Give VSCode some time to spawn before connecting to its unix socket.
 	await new Promise((resolve) => setTimeout(resolve, 3_000))
@@ -309,23 +328,30 @@ const runExercise = async ({ run, task, server }: { run: Run; task: Task; server
 
 	console.log(`${Date.now()} [cli#runExercise | ${language} / ${exercise}] starting task`)
 
-	client.sendMessage({
-		type: IpcMessageType.TaskCommand,
-		origin: IpcOrigin.Client,
-		clientId: client.clientId!,
-		data: {
-			commandName: TaskCommandName.StartNewTask,
+	if (client.isReady) {
+		client.sendMessage({
+			type: IpcMessageType.TaskCommand,
+			origin: IpcOrigin.Client,
+			clientId: client.clientId!,
 			data: {
-				configuration: {
-					...rooCodeDefaults,
-					openRouterApiKey: process.env.OPENROUTER_API_KEY!,
-					...run.settings,
+				commandName: TaskCommandName.StartNewTask,
+				data: {
+					configuration: {
+						...rooCodeDefaults,
+						openRouterApiKey: process.env.OPENROUTER_API_KEY!,
+						...run.settings,
+					},
+					text: prompt,
+					newTab: true,
 				},
-				text: prompt,
-				newTab: true,
 			},
-		},
-	})
+		})
+	} else {
+		console.log(`[cli#runExercise | ${language} / ${exercise}] unable to connect`)
+		client.disconnect()
+		taskFinishedAt = Date.now()
+		isClientDisconnected = true
+	}
 
 	try {
 		await pWaitFor(() => !!taskFinishedAt || isClientDisconnected, { interval: 1_000, timeout: TASK_TIMEOUT })
@@ -365,6 +391,9 @@ const runExercise = async ({ run, task, server }: { run: Run; task: Task; server
 		client.disconnect()
 	}
 
+	controller.abort()
+	await subprocess
+
 	return { success: !!taskFinishedAt }
 }
 
@@ -520,7 +549,7 @@ if (!fs.existsSync(extensionDevelopmentPath)) {
 
 if (!fs.existsSync(exercisesPath)) {
 	console.error(
-		`Exercises path does not exist. Please run "git clone https://github.com/cte/Roo-Code-Benchmark.git exercises".`,
+		`Exercises do not exist at ${exercisesPath}. Please run "git clone https://github.com/RooCodeInc/Roo-Code-Evals.git evals".`,
 	)
 	process.exit(1)
 }

+ 2 - 2
evals/apps/web/package.json

@@ -31,8 +31,8 @@
 		"clsx": "^2.1.1",
 		"cmdk": "^1.1.0",
 		"fuzzysort": "^3.1.0",
-		"lucide-react": "^0.510.0",
-		"next": "15.2.2",
+		"lucide-react": "^0.511.0",
+		"next": "15.3.3",
 		"next-themes": "^0.4.6",
 		"p-map": "^7.0.3",
 		"ps-tree": "^1.2.0",

+ 8 - 1
evals/package.json

@@ -10,7 +10,14 @@
 		"build": "turbo build --log-order grouped --output-logs new-only",
 		"web": "turbo dev --filter @evals/web",
 		"cli": "turbo dev --filter @evals/cli -- run",
-		"drizzle:studio": "pnpm --filter @evals/db db:studio"
+		"drizzle:studio": "pnpm --filter @evals/db db:studio",
+		"docker:build": "docker build -f Dockerfile -t roo-code-eval --progress=plain ..",
+		"docker:run": "touch /tmp/evals.db && docker run -d -it -p 3000:3000 -v /tmp/evals.db:/tmp/evals.db roo-code-eval",
+		"docker:start": "pnpm docker:build && pnpm docker:run",
+		"docker:shell": "docker exec -it $(docker ps --filter \"ancestor=roo-code-eval\" -q) /bin/bash",
+		"docker:stop": "docker stop $(docker ps --filter \"ancestor=roo-code-eval\" -q)",
+		"docker:rm": "docker rm $(docker ps -a --filter \"ancestor=roo-code-eval\" -q)",
+		"docker:clean": "pnpm docker:stop && pnpm docker:rm"
 	},
 	"devDependencies": {
 		"@dotenvx/dotenvx": "^1.41.0",

+ 1 - 2
evals/packages/types/src/roo-code.ts

@@ -297,7 +297,7 @@ export type CommandExecutionStatus = z.infer<typeof commandExecutionStatusSchema
  * ExperimentId
  */
 
-export const experimentIds = ["autoCondenseContext", "powerSteering", "autocomplete"] as const
+export const experimentIds = ["powerSteering", "autocomplete"] as const // kilocode_change: add autocomplete
 
 export const experimentIdsSchema = z.enum(experimentIds)
 
@@ -308,7 +308,6 @@ export type ExperimentId = z.infer<typeof experimentIdsSchema>
  */
 
 const experimentsSchema = z.object({
-	autoCondenseContext: z.boolean(),
 	powerSteering: z.boolean(),
 	autocomplete: z.boolean(),
 })

+ 206 - 181
evals/pnpm-lock.yaml

@@ -10,7 +10,7 @@ importers:
     devDependencies:
       '@dotenvx/dotenvx':
         specifier: ^1.41.0
-        version: 1.44.0
+        version: 1.44.1
       '@eslint/js':
         specifier: ^9.25.1
         version: 9.26.0
@@ -19,7 +19,7 @@ importers:
         version: 9.26.0([email protected])
       globals:
         specifier: ^16.0.0
-        version: 16.1.0
+        version: 16.2.0
       prettier:
         specifier: ^3.5.3
         version: 3.5.3
@@ -28,7 +28,7 @@ importers:
         version: 4.19.4
       turbo:
         specifier: ^2.5.2
-        version: 2.5.3
+        version: 2.5.4
       typescript:
         specifier: 5.8.3
         version: 5.8.3
@@ -142,11 +142,11 @@ importers:
         specifier: ^3.1.0
         version: 3.1.0
       lucide-react:
-        specifier: ^0.510.0
-        version: 0.510.0([email protected])
+        specifier: ^0.511.0
+        version: 0.511.0([email protected])
       next:
-        specifier: 15.2.2
-        version: 15.2.2([email protected]([email protected]))([email protected])
+        specifier: 15.3.3
+        version: 15.3.3([email protected]([email protected]))([email protected])
       next-themes:
         specifier: ^0.4.6
         version: 0.4.6([email protected]([email protected]))([email protected])
@@ -231,7 +231,7 @@ importers:
         version: 5.2.0([email protected]([email protected]))
       eslint-plugin-turbo:
         specifier: ^2.4.4
-        version: 2.5.3([email protected]([email protected]))([email protected].3)
+        version: 2.5.3([email protected]([email protected]))([email protected].4)
       globals:
         specifier: ^16.0.0
         version: 16.1.0
@@ -347,8 +347,8 @@ packages:
     resolution: {integrity: sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==}
     engines: {node: '>=6.9.0'}
 
-  '@dotenvx/[email protected].0':
-    resolution: {integrity: sha512-18Aa+7KP/L2Kj9lxmT4EJZnsCq/xGIHgzU26rdzsKMhjpeT3YY+qin/dNAnIaVHPZnee7kXpZL55M9htd30r7Q==}
+  '@dotenvx/[email protected].1':
+    resolution: {integrity: sha512-j1QImCqf/XJmhIjC1OPpgiZV9g370HG9MNT9s/CDwCKsoYzNCPEKK+GfsidahJx7yIlBbm+4dPLlGec+bKn7oA==}
     hasBin: true
 
   '@drizzle-team/[email protected]':
@@ -731,107 +731,118 @@ packages:
     resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
     engines: {node: '>=18.18'}
 
-  '@img/[email protected]3.5':
-    resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==}
+  '@img/[email protected]4.2':
+    resolution: {integrity: sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==}
     engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
     cpu: [arm64]
     os: [darwin]
 
-  '@img/[email protected]3.5':
-    resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==}
+  '@img/[email protected]4.2':
+    resolution: {integrity: sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==}
     engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
     cpu: [x64]
     os: [darwin]
 
-  '@img/sharp-libvips-darwin-arm64@1.0.4':
-    resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==}
+  '@img/sharp-libvips-darwin-arm64@1.1.0':
+    resolution: {integrity: sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==}
     cpu: [arm64]
     os: [darwin]
 
-  '@img/sharp-libvips-darwin-x64@1.0.4':
-    resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==}
+  '@img/sharp-libvips-darwin-x64@1.1.0':
+    resolution: {integrity: sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==}
     cpu: [x64]
     os: [darwin]
 
-  '@img/sharp-libvips-linux-arm64@1.0.4':
-    resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
+  '@img/sharp-libvips-linux-arm64@1.1.0':
+    resolution: {integrity: sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==}
     cpu: [arm64]
     os: [linux]
 
-  '@img/sharp-libvips-linux-arm@1.0.5':
-    resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
+  '@img/sharp-libvips-linux-arm@1.1.0':
+    resolution: {integrity: sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==}
     cpu: [arm]
     os: [linux]
 
-  '@img/[email protected]':
-    resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==}
+  '@img/[email protected]':
+    resolution: {integrity: sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==}
+    cpu: [ppc64]
+    os: [linux]
+
+  '@img/[email protected]':
+    resolution: {integrity: sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==}
     cpu: [s390x]
     os: [linux]
 
-  '@img/[email protected]':
-    resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
+  '@img/sharp-libvips-linux-x64@1.1.0':
+    resolution: {integrity: sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==}
     cpu: [x64]
     os: [linux]
 
-  '@img/sharp-libvips-linuxmusl-arm64@1.0.4':
-    resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
+  '@img/sharp-libvips-linuxmusl-arm64@1.1.0':
+    resolution: {integrity: sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==}
     cpu: [arm64]
     os: [linux]
 
-  '@img/sharp-libvips-linuxmusl-x64@1.0.4':
-    resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
+  '@img/sharp-libvips-linuxmusl-x64@1.1.0':
+    resolution: {integrity: sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==}
     cpu: [x64]
     os: [linux]
 
-  '@img/[email protected]3.5':
-    resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
+  '@img/[email protected]4.2':
+    resolution: {integrity: sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==}
     engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
     cpu: [arm64]
     os: [linux]
 
-  '@img/[email protected]3.5':
-    resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
+  '@img/[email protected]4.2':
+    resolution: {integrity: sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==}
     engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
     cpu: [arm]
     os: [linux]
 
-  '@img/[email protected]3.5':
-    resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==}
+  '@img/[email protected]4.2':
+    resolution: {integrity: sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==}
     engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
     cpu: [s390x]
     os: [linux]
 
-  '@img/[email protected]3.5':
-    resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
+  '@img/[email protected]4.2':
+    resolution: {integrity: sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==}
     engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
     cpu: [x64]
     os: [linux]
 
-  '@img/[email protected]3.5':
-    resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
+  '@img/[email protected]4.2':
+    resolution: {integrity: sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==}
     engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
     cpu: [arm64]
     os: [linux]
 
-  '@img/[email protected]3.5':
-    resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
+  '@img/[email protected]4.2':
+    resolution: {integrity: sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==}
     engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
     cpu: [x64]
     os: [linux]
 
-  '@img/[email protected]3.5':
-    resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==}
+  '@img/[email protected]4.2':
+    resolution: {integrity: sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==}
     engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
     cpu: [wasm32]
 
-  '@img/[email protected]':
-    resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==}
+  '@img/[email protected]':
+    resolution: {integrity: sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==}
+    engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+    cpu: [arm64]
+    os: [win32]
+
+  '@img/[email protected]':
+    resolution: {integrity: sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==}
     engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
     cpu: [ia32]
     os: [win32]
 
-  '@img/[email protected]':
-    resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==}
+  '@img/[email protected]4.2':
+    resolution: {integrity: sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==}
     engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
     cpu: [x64]
     os: [win32]
@@ -926,56 +937,56 @@ packages:
   '@neon-rs/[email protected]':
     resolution: {integrity: sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==}
 
-  '@next/env@15.2.2':
-    resolution: {integrity: sha512-yWgopCfA9XDR8ZH3taB5nRKtKJ1Q5fYsTOuYkzIIoS8TJ0UAUKAGF73JnGszbjk2ufAQDj6mDdgsJAFx5CLtYQ==}
+  '@next/env@15.3.3':
+    resolution: {integrity: sha512-OdiMrzCl2Xi0VTjiQQUK0Xh7bJHnOuET2s+3V+Y40WJBAXrJeGA3f+I8MZJ/YQ3mVGi5XGR1L66oFlgqXhQ4Vw==}
 
   '@next/[email protected]':
     resolution: {integrity: sha512-ijVRTXBgnHT33aWnDtmlG+LJD+5vhc9AKTJPquGG5NKXjpKNjc62woIhFtrAcWdBobt8kqjCoaJ0q6sDQoX7aQ==}
 
-  '@next/swc-darwin-arm64@15.2.2':
-    resolution: {integrity: sha512-HNBRnz+bkZ+KfyOExpUxTMR0Ow8nkkcE6IlsdEa9W/rI7gefud19+Sn1xYKwB9pdCdxIP1lPru/ZfjfA+iT8pw==}
+  '@next/swc-darwin-arm64@15.3.3':
+    resolution: {integrity: sha512-WRJERLuH+O3oYB4yZNVahSVFmtxRNjNF1I1c34tYMoJb0Pve+7/RaLAJJizyYiFhjYNGHRAE1Ri2Fd23zgDqhg==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [darwin]
 
-  '@next/swc-darwin-x64@15.2.2':
-    resolution: {integrity: sha512-mJOUwp7al63tDpLpEFpKwwg5jwvtL1lhRW2fI1Aog0nYCPAhxbJsaZKdoVyPZCy8MYf/iQVNDuk/+i29iLCzIA==}
+  '@next/swc-darwin-x64@15.3.3':
+    resolution: {integrity: sha512-XHdzH/yBc55lu78k/XwtuFR/ZXUTcflpRXcsu0nKmF45U96jt1tsOZhVrn5YH+paw66zOANpOnFQ9i6/j+UYvw==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [darwin]
 
-  '@next/swc-linux-arm64-gnu@15.2.2':
-    resolution: {integrity: sha512-5ZZ0Zwy3SgMr7MfWtRE7cQWVssfOvxYfD9O7XHM7KM4nrf5EOeqwq67ZXDgo86LVmffgsu5tPO57EeFKRnrfSQ==}
+  '@next/swc-linux-arm64-gnu@15.3.3':
+    resolution: {integrity: sha512-VZ3sYL2LXB8znNGcjhocikEkag/8xiLgnvQts41tq6i+wql63SMS1Q6N8RVXHw5pEUjiof+II3HkDd7GFcgkzw==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [linux]
 
-  '@next/swc-linux-arm64-musl@15.2.2':
-    resolution: {integrity: sha512-cgKWBuFMLlJ4TWcFHl1KOaVVUAF8vy4qEvX5KsNd0Yj5mhu989QFCq1WjuaEbv/tO1ZpsQI6h/0YR8bLwEi+nA==}
+  '@next/swc-linux-arm64-musl@15.3.3':
+    resolution: {integrity: sha512-h6Y1fLU4RWAp1HPNJWDYBQ+e3G7sLckyBXhmH9ajn8l/RSMnhbuPBV/fXmy3muMcVwoJdHL+UtzRzs0nXOf9SA==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [linux]
 
-  '@next/swc-linux-x64-gnu@15.2.2':
-    resolution: {integrity: sha512-c3kWSOSsVL8rcNBBfOq1+/j2PKs2nsMwJUV4icUxRgGBwUOfppeh7YhN5s79enBQFU+8xRgVatFkhHU1QW7yUA==}
+  '@next/swc-linux-x64-gnu@15.3.3':
+    resolution: {integrity: sha512-jJ8HRiF3N8Zw6hGlytCj5BiHyG/K+fnTKVDEKvUCyiQ/0r5tgwO7OgaRiOjjRoIx2vwLR+Rz8hQoPrnmFbJdfw==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [linux]
 
-  '@next/swc-linux-x64-musl@15.2.2':
-    resolution: {integrity: sha512-PXTW9PLTxdNlVYgPJ0equojcq1kNu5NtwcNjRjHAB+/sdoKZ+X8FBu70fdJFadkxFIGekQTyRvPMFF+SOJaQjw==}
+  '@next/swc-linux-x64-musl@15.3.3':
+    resolution: {integrity: sha512-HrUcTr4N+RgiiGn3jjeT6Oo208UT/7BuTr7K0mdKRBtTbT4v9zJqCDKO97DUqqoBK1qyzP1RwvrWTvU6EPh/Cw==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [linux]
 
-  '@next/swc-win32-arm64-msvc@15.2.2':
-    resolution: {integrity: sha512-nG644Es5llSGEcTaXhnGWR/aThM/hIaz0jx4MDg4gWC8GfTCp8eDBWZ77CVuv2ha/uL9Ce+nPTfYkSLG67/sHg==}
+  '@next/swc-win32-arm64-msvc@15.3.3':
+    resolution: {integrity: sha512-SxorONgi6K7ZUysMtRF3mIeHC5aA3IQLmKFQzU0OuhuUYwpOBc1ypaLJLP5Bf3M9k53KUUUj4vTPwzGvl/NwlQ==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [win32]
 
-  '@next/swc-win32-x64-msvc@15.2.2':
-    resolution: {integrity: sha512-52nWy65S/R6/kejz3jpvHAjZDPKIbEQu4x9jDBzmB9jJfuOy5rspjKu4u77+fI4M/WzLXrrQd57hlFGzz1ubcQ==}
+  '@next/swc-win32-x64-msvc@15.3.3':
+    resolution: {integrity: sha512-4QZG6F8enl9/S2+yIiOiju0iCTFd93d8VC1q9LZS4p/Xuk81W2QDjCFeoogmrWWkAD59z8ZxepBQap2dKS5ruw==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [win32]
@@ -2213,8 +2224,8 @@ packages:
     resolution: {integrity: sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==}
     engines: {node: '>=6.0.0'}
 
-  [email protected]4:
-    resolution: {integrity: sha512-eJAgf9pdv214Hn98FlUzclRMYWF7WfoLlkS9nWMTm1qcCwn6Ad4EGD9lr9HXMBfSrZhYQujRE+p0adPRkctC6A==}
+  [email protected]5:
+    resolution: {integrity: sha512-r6kEJXDKecVOCj2nLMuXK/FCPeurW33+3JRpfXVbjLja3XUYFfD9I/JBreH6sUyzcm3G/YQboBjMla6poKeSdA==}
     engines: {bun: '>=1', deno: '>=2', node: '>=16'}
 
   [email protected]:
@@ -2451,8 +2462,8 @@ packages:
   [email protected]:
     resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
 
-  [email protected].4:
-    resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==}
+  [email protected].5:
+    resolution: {integrity: sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==}
     peerDependencies:
       picomatch: ^3 || ^4
     peerDependenciesMeta:
@@ -2596,6 +2607,10 @@ packages:
     resolution: {integrity: sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==}
     engines: {node: '>=18'}
 
+  [email protected]:
+    resolution: {integrity: sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg==}
+    engines: {node: '>=18'}
+
   [email protected]:
     resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
     engines: {node: '>= 0.4'}
@@ -3039,8 +3054,8 @@ packages:
     resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
     engines: {node: '>=10'}
 
-  [email protected]0.0:
-    resolution: {integrity: sha512-p8SQRAMVh7NhsAIETokSqDrc5CHnDLbV29mMnzaXx+Vc/hnqQzwI2r0FMWCcoTXnbw2KEjy48xwpGdEL+ck06Q==}
+  [email protected]1.0:
+    resolution: {integrity: sha512-VK5a2ydJ7xm8GvBeKLS9mu1pVK6ucef9780JVUjw6bAjJL/QXnd4Y0p7SPeOUMC27YhzNCZvm5d/QX0Tp3rc0w==}
     peerDependencies:
       react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
 
@@ -3144,8 +3159,8 @@ packages:
       react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
       react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
 
-  next@15.2.2:
-    resolution: {integrity: sha512-dgp8Kcx5XZRjMw2KNwBtUzhngRaURPioxoNIVl5BOyJbhi9CUgEtKDO7fx5wh8Z8vOVX1nYZ9meawJoRrlASYA==}
+  next@15.3.3:
+    resolution: {integrity: sha512-JqNj29hHNmCLtNvd090SyRbXJiivQ+58XjCcrC50Crb5g5u2zi7Y2YivbsEfzk6AtVI80akdOQbaMZwWB1Hthw==}
     engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
     hasBin: true
     peerDependencies:
@@ -3587,8 +3602,8 @@ packages:
   [email protected]:
     resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
 
-  [email protected]3.5:
-    resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==}
+  [email protected]4.2:
+    resolution: {integrity: sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==}
     engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
 
   [email protected]:
@@ -3845,38 +3860,38 @@ packages:
     engines: {node: '>=18.0.0'}
     hasBin: true
 
-  [email protected].3:
-    resolution: {integrity: sha512-YSItEVBUIvAGPUDpAB9etEmSqZI3T6BHrkBkeSErvICXn3dfqXUfeLx35LfptLDEbrzFUdwYFNmt8QXOwe9yaw==}
+  [email protected].4:
+    resolution: {integrity: sha512-ah6YnH2dErojhFooxEzmvsoZQTMImaruZhFPfMKPBq8sb+hALRdvBNLqfc8NWlZq576FkfRZ/MSi4SHvVFT9PQ==}
     cpu: [x64]
     os: [darwin]
 
-  [email protected].3:
-    resolution: {integrity: sha512-5PefrwHd42UiZX7YA9m1LPW6x9YJBDErXmsegCkVp+GjmWrADfEOxpFrGQNonH3ZMj77WZB2PVE5Aw3gA+IOhg==}
+  [email protected].4:
+    resolution: {integrity: sha512-2+Nx6LAyuXw2MdXb7pxqle3MYignLvS7OwtsP9SgtSBaMlnNlxl9BovzqdYAgkUW3AsYiQMJ/wBRb7d+xemM5A==}
     cpu: [arm64]
     os: [darwin]
 
-  [email protected].3:
-    resolution: {integrity: sha512-M9xigFgawn5ofTmRzvjjLj3Lqc05O8VHKuOlWNUlnHPUltFquyEeSkpQNkE/vpPdOR14AzxqHbhhxtfS4qvb1w==}
+  [email protected].4:
+    resolution: {integrity: sha512-5May2kjWbc8w4XxswGAl74GZ5eM4Gr6IiroqdLhXeXyfvWEdm2mFYCSWOzz0/z5cAgqyGidF1jt1qzUR8hTmOA==}
     cpu: [x64]
     os: [linux]
 
-  [email protected].3:
-    resolution: {integrity: sha512-auJRbYZ8SGJVqvzTikpg1bsRAsiI9Tk0/SDkA5Xgg0GdiHDH/BOzv1ZjDE2mjmlrO/obr19Dw+39OlMhwLffrw==}
+  [email protected].4:
+    resolution: {integrity: sha512-/2yqFaS3TbfxV3P5yG2JUI79P7OUQKOUvAnx4MV9Bdz6jqHsHwc9WZPpO4QseQm+NvmgY6ICORnoVPODxGUiJg==}
     cpu: [arm64]
     os: [linux]
 
-  [email protected].3:
-    resolution: {integrity: sha512-arLQYohuHtIEKkmQSCU9vtrKUg+/1TTstWB9VYRSsz+khvg81eX6LYHtXJfH/dK7Ho6ck+JaEh5G+QrE1jEmCQ==}
+  [email protected].4:
+    resolution: {integrity: sha512-EQUO4SmaCDhO6zYohxIjJpOKRN3wlfU7jMAj3CgcyTPvQR/UFLEKAYHqJOnJtymbQmiiM/ihX6c6W6Uq0yC7mA==}
     cpu: [x64]
     os: [win32]
 
-  [email protected].3:
-    resolution: {integrity: sha512-3JPn66HAynJ0gtr6H+hjY4VHpu1RPKcEwGATvGUTmLmYSYBQieVlnGDRMMoYN066YfyPqnNGCfhYbXfH92Cm0g==}
+  [email protected].4:
+    resolution: {integrity: sha512-oQ8RrK1VS8lrxkLriotFq+PiF7iiGgkZtfLKF4DDKsmdbPo0O9R2mQxm7jHLuXraRCuIQDWMIw6dpcr7Iykf4A==}
     cpu: [arm64]
     os: [win32]
 
-  [email protected].3:
-    resolution: {integrity: sha512-iHuaNcq5GZZnr3XDZNuu2LSyCzAOPwDuo5Qt+q64DfsTP1i3T2bKfxJhni2ZQxsvAoxRbuUK5QetJki4qc5aYA==}
+  [email protected].4:
+    resolution: {integrity: sha512-kc8ZibdRcuWUG1pbYSBFWqmIjynlD8Lp7IB6U3vIzvOv9VG+6Sp8bzyeBWE3Oi8XV5KsQrznyRTBPvrf99E4mA==}
     hasBin: true
 
   [email protected]:
@@ -4170,13 +4185,13 @@ snapshots:
 
   '@babel/[email protected]': {}
 
-  '@dotenvx/[email protected].0':
+  '@dotenvx/[email protected].1':
     dependencies:
       commander: 11.1.0
       dotenv: 16.5.0
-      eciesjs: 0.4.14
+      eciesjs: 0.4.15
       execa: 5.1.1
-      fdir: 6.4.4([email protected])
+      fdir: 6.4.5([email protected])
       ignore: 5.3.2
       object-treeify: 1.1.33
       picomatch: 4.0.2
@@ -4423,79 +4438,85 @@ snapshots:
 
   '@humanwhocodes/[email protected]': {}
 
-  '@img/[email protected]3.5':
+  '@img/[email protected]4.2':
     optionalDependencies:
-      '@img/sharp-libvips-darwin-arm64': 1.0.4
+      '@img/sharp-libvips-darwin-arm64': 1.1.0
     optional: true
 
-  '@img/[email protected]3.5':
+  '@img/[email protected]4.2':
     optionalDependencies:
-      '@img/sharp-libvips-darwin-x64': 1.0.4
+      '@img/sharp-libvips-darwin-x64': 1.1.0
+    optional: true
+
+  '@img/[email protected]':
     optional: true
 
-  '@img/[email protected]':
+  '@img/sharp-libvips-darwin-[email protected]':
     optional: true
 
-  '@img/sharp-libvips-[email protected]':
+  '@img/sharp-libvips-[email protected]':
     optional: true
 
-  '@img/sharp-libvips-linux-arm[email protected]':
+  '@img/sharp-libvips-linux-arm@1.1.0':
     optional: true
 
-  '@img/sharp-libvips-linux-[email protected]':
+  '@img/sharp-libvips-linux-[email protected]':
     optional: true
 
-  '@img/sharp-libvips-linux-s390x@1.0.4':
+  '@img/sharp-libvips-linux-s390x@1.1.0':
     optional: true
 
-  '@img/sharp-libvips-linux-x64@1.0.4':
+  '@img/sharp-libvips-linux-x64@1.1.0':
     optional: true
 
-  '@img/sharp-libvips-linuxmusl-arm64@1.0.4':
+  '@img/sharp-libvips-linuxmusl-arm64@1.1.0':
     optional: true
 
-  '@img/sharp-libvips-linuxmusl-x64@1.0.4':
+  '@img/sharp-libvips-linuxmusl-x64@1.1.0':
     optional: true
 
-  '@img/[email protected]3.5':
+  '@img/[email protected]4.2':
     optionalDependencies:
-      '@img/sharp-libvips-linux-arm64': 1.0.4
+      '@img/sharp-libvips-linux-arm64': 1.1.0
     optional: true
 
-  '@img/[email protected]3.5':
+  '@img/[email protected]4.2':
     optionalDependencies:
-      '@img/sharp-libvips-linux-arm': 1.0.5
+      '@img/sharp-libvips-linux-arm': 1.1.0
     optional: true
 
-  '@img/[email protected]3.5':
+  '@img/[email protected]4.2':
     optionalDependencies:
-      '@img/sharp-libvips-linux-s390x': 1.0.4
+      '@img/sharp-libvips-linux-s390x': 1.1.0
     optional: true
 
-  '@img/[email protected]3.5':
+  '@img/[email protected]4.2':
     optionalDependencies:
-      '@img/sharp-libvips-linux-x64': 1.0.4
+      '@img/sharp-libvips-linux-x64': 1.1.0
     optional: true
 
-  '@img/[email protected]3.5':
+  '@img/[email protected]4.2':
     optionalDependencies:
-      '@img/sharp-libvips-linuxmusl-arm64': 1.0.4
+      '@img/sharp-libvips-linuxmusl-arm64': 1.1.0
     optional: true
 
-  '@img/[email protected]3.5':
+  '@img/[email protected]4.2':
     optionalDependencies:
-      '@img/sharp-libvips-linuxmusl-x64': 1.0.4
+      '@img/sharp-libvips-linuxmusl-x64': 1.1.0
     optional: true
 
-  '@img/[email protected]3.5':
+  '@img/[email protected]4.2':
     dependencies:
       '@emnapi/runtime': 1.4.3
     optional: true
 
-  '@img/[email protected]':
+  '@img/[email protected]':
+    optional: true
+
+  '@img/[email protected]':
     optional: true
 
-  '@img/[email protected]':
+  '@img/[email protected]4.2':
     optional: true
 
   '@isaacs/[email protected]':
@@ -4598,34 +4619,34 @@ snapshots:
 
   '@neon-rs/[email protected]': {}
 
-  '@next/env@15.2.2': {}
+  '@next/env@15.3.3': {}
 
   '@next/[email protected]':
     dependencies:
       fast-glob: 3.3.1
 
-  '@next/swc-darwin-arm64@15.2.2':
+  '@next/swc-darwin-arm64@15.3.3':
     optional: true
 
-  '@next/swc-darwin-x64@15.2.2':
+  '@next/swc-darwin-x64@15.3.3':
     optional: true
 
-  '@next/swc-linux-arm64-gnu@15.2.2':
+  '@next/swc-linux-arm64-gnu@15.3.3':
     optional: true
 
-  '@next/swc-linux-arm64-musl@15.2.2':
+  '@next/swc-linux-arm64-musl@15.3.3':
     optional: true
 
-  '@next/swc-linux-x64-gnu@15.2.2':
+  '@next/swc-linux-x64-gnu@15.3.3':
     optional: true
 
-  '@next/swc-linux-x64-musl@15.2.2':
+  '@next/swc-linux-x64-musl@15.3.3':
     optional: true
 
-  '@next/swc-win32-arm64-msvc@15.2.2':
+  '@next/swc-win32-arm64-msvc@15.3.3':
     optional: true
 
-  '@next/swc-win32-x64-msvc@15.2.2':
+  '@next/swc-win32-x64-msvc@15.3.3':
     optional: true
 
   '@noble/[email protected]': {}
@@ -5804,7 +5825,7 @@ snapshots:
 
   [email protected]: {}
 
-  [email protected]4:
+  [email protected]5:
     dependencies:
       '@ecies/ciphers': 0.2.3(@noble/[email protected])
       '@noble/ciphers': 1.3.0
@@ -6038,11 +6059,11 @@ snapshots:
       string.prototype.matchall: 4.0.12
       string.prototype.repeat: 1.0.0
 
-  [email protected]([email protected]([email protected]))([email protected].3):
+  [email protected]([email protected]([email protected]))([email protected].4):
     dependencies:
       dotenv: 16.0.3
       eslint: 9.26.0([email protected])
-      turbo: 2.5.3
+      turbo: 2.5.4
 
   [email protected]:
     dependencies:
@@ -6144,7 +6165,7 @@ snapshots:
 
   [email protected]:
     dependencies:
-      cross-spawn: 7.0.3
+      cross-spawn: 7.0.6
       get-stream: 6.0.1
       human-signals: 2.1.0
       is-stream: 2.0.1
@@ -6237,7 +6258,7 @@ snapshots:
     dependencies:
       reusify: 1.1.0
 
-  [email protected].4([email protected]):
+  [email protected].5([email protected]):
     optionalDependencies:
       picomatch: 4.0.2
 
@@ -6386,6 +6407,8 @@ snapshots:
 
   [email protected]: {}
 
+  [email protected]: {}
+
   [email protected]:
     dependencies:
       define-properties: 1.2.1
@@ -6808,7 +6831,7 @@ snapshots:
     dependencies:
       yallist: 4.0.0
 
-  [email protected]0.0([email protected]):
+  [email protected]1.0([email protected]):
     dependencies:
       react: 19.1.0
 
@@ -6891,9 +6914,9 @@ snapshots:
       react: 19.1.0
       react-dom: 19.1.0([email protected])
 
-  next@15.2.2([email protected]([email protected]))([email protected]):
+  next@15.3.3([email protected]([email protected]))([email protected]):
     dependencies:
-      '@next/env': 15.2.2
+      '@next/env': 15.3.3
       '@swc/counter': 0.1.3
       '@swc/helpers': 0.5.15
       busboy: 1.6.0
@@ -6903,15 +6926,15 @@ snapshots:
       react-dom: 19.1.0([email protected])
       styled-jsx: 5.1.6([email protected])
     optionalDependencies:
-      '@next/swc-darwin-arm64': 15.2.2
-      '@next/swc-darwin-x64': 15.2.2
-      '@next/swc-linux-arm64-gnu': 15.2.2
-      '@next/swc-linux-arm64-musl': 15.2.2
-      '@next/swc-linux-x64-gnu': 15.2.2
-      '@next/swc-linux-x64-musl': 15.2.2
-      '@next/swc-win32-arm64-msvc': 15.2.2
-      '@next/swc-win32-x64-msvc': 15.2.2
-      sharp: 0.33.5
+      '@next/swc-darwin-arm64': 15.3.3
+      '@next/swc-darwin-x64': 15.3.3
+      '@next/swc-linux-arm64-gnu': 15.3.3
+      '@next/swc-linux-arm64-musl': 15.3.3
+      '@next/swc-linux-x64-gnu': 15.3.3
+      '@next/swc-linux-x64-musl': 15.3.3
+      '@next/swc-win32-arm64-msvc': 15.3.3
+      '@next/swc-win32-x64-msvc': 15.3.3
+      sharp: 0.34.2
     transitivePeerDependencies:
       - '@babel/core'
       - babel-plugin-macros
@@ -7399,31 +7422,33 @@ snapshots:
 
   [email protected]: {}
 
-  [email protected]3.5:
+  [email protected]4.2:
     dependencies:
       color: 4.2.3
       detect-libc: 2.0.4
       semver: 7.7.2
     optionalDependencies:
-      '@img/sharp-darwin-arm64': 0.33.5
-      '@img/sharp-darwin-x64': 0.33.5
-      '@img/sharp-libvips-darwin-arm64': 1.0.4
-      '@img/sharp-libvips-darwin-x64': 1.0.4
-      '@img/sharp-libvips-linux-arm': 1.0.5
-      '@img/sharp-libvips-linux-arm64': 1.0.4
-      '@img/sharp-libvips-linux-s390x': 1.0.4
-      '@img/sharp-libvips-linux-x64': 1.0.4
-      '@img/sharp-libvips-linuxmusl-arm64': 1.0.4
-      '@img/sharp-libvips-linuxmusl-x64': 1.0.4
-      '@img/sharp-linux-arm': 0.33.5
-      '@img/sharp-linux-arm64': 0.33.5
-      '@img/sharp-linux-s390x': 0.33.5
-      '@img/sharp-linux-x64': 0.33.5
-      '@img/sharp-linuxmusl-arm64': 0.33.5
-      '@img/sharp-linuxmusl-x64': 0.33.5
-      '@img/sharp-wasm32': 0.33.5
-      '@img/sharp-win32-ia32': 0.33.5
-      '@img/sharp-win32-x64': 0.33.5
+      '@img/sharp-darwin-arm64': 0.34.2
+      '@img/sharp-darwin-x64': 0.34.2
+      '@img/sharp-libvips-darwin-arm64': 1.1.0
+      '@img/sharp-libvips-darwin-x64': 1.1.0
+      '@img/sharp-libvips-linux-arm': 1.1.0
+      '@img/sharp-libvips-linux-arm64': 1.1.0
+      '@img/sharp-libvips-linux-ppc64': 1.1.0
+      '@img/sharp-libvips-linux-s390x': 1.1.0
+      '@img/sharp-libvips-linux-x64': 1.1.0
+      '@img/sharp-libvips-linuxmusl-arm64': 1.1.0
+      '@img/sharp-libvips-linuxmusl-x64': 1.1.0
+      '@img/sharp-linux-arm': 0.34.2
+      '@img/sharp-linux-arm64': 0.34.2
+      '@img/sharp-linux-s390x': 0.34.2
+      '@img/sharp-linux-x64': 0.34.2
+      '@img/sharp-linuxmusl-arm64': 0.34.2
+      '@img/sharp-linuxmusl-x64': 0.34.2
+      '@img/sharp-wasm32': 0.34.2
+      '@img/sharp-win32-arm64': 0.34.2
+      '@img/sharp-win32-ia32': 0.34.2
+      '@img/sharp-win32-x64': 0.34.2
     optional: true
 
   [email protected]:
@@ -7645,7 +7670,7 @@ snapshots:
 
   [email protected]:
     dependencies:
-      fdir: 6.4.4([email protected])
+      fdir: 6.4.5([email protected])
       picomatch: 4.0.2
 
   [email protected]: {}
@@ -7677,32 +7702,32 @@ snapshots:
     optionalDependencies:
       fsevents: 2.3.3
 
-  [email protected].3:
+  [email protected].4:
     optional: true
 
-  [email protected].3:
+  [email protected].4:
     optional: true
 
-  [email protected].3:
+  [email protected].4:
     optional: true
 
-  [email protected].3:
+  [email protected].4:
     optional: true
 
-  [email protected].3:
+  [email protected].4:
     optional: true
 
-  [email protected].3:
+  [email protected].4:
     optional: true
 
-  [email protected].3:
+  [email protected].4:
     optionalDependencies:
-      turbo-darwin-64: 2.5.3
-      turbo-darwin-arm64: 2.5.3
-      turbo-linux-64: 2.5.3
-      turbo-linux-arm64: 2.5.3
-      turbo-windows-64: 2.5.3
-      turbo-windows-arm64: 2.5.3
+      turbo-darwin-64: 2.5.4
+      turbo-darwin-arm64: 2.5.4
+      turbo-linux-64: 2.5.4
+      turbo-linux-arm64: 2.5.4
+      turbo-windows-64: 2.5.4
+      turbo-windows-arm64: 2.5.4
 
   [email protected]:
     dependencies:
@@ -7830,7 +7855,7 @@ snapshots:
   [email protected](@types/[email protected])([email protected])([email protected])([email protected]):
     dependencies:
       esbuild: 0.25.4
-      fdir: 6.4.4([email protected])
+      fdir: 6.4.5([email protected])
       picomatch: 4.0.2
       postcss: 8.5.3
       rollup: 4.40.2

+ 3 - 3
evals/scripts/setup.sh

@@ -176,8 +176,8 @@ for i in "${!options[@]}"; do
   case "${plugin}" in
   "nodejs")
     if ! command -v node &>/dev/null; then
-      asdf install nodejs 20.18.1 || exit 1
-      asdf set nodejs 20.18.1 || exit 1
+      asdf install nodejs 20.19.2 || exit 1
+      asdf set nodejs 20.19.2 || exit 1
       NODE_VERSION=$(node --version)
       echo "✅ Node.js is installed ($NODE_VERSION)"
     else
@@ -185,7 +185,7 @@ for i in "${!options[@]}"; do
       echo "✅ Node.js is installed ($NODE_VERSION)"
     fi
 
-    if [[ $(node --version) != "v20.18.1" ]]; then
+    if [[ $(node --version) != "v20.19.2" ]]; then
       NODE_VERSION=$(node --version)
       echo "🚨 You have the wrong version of node installed ($NODE_VERSION)."
       echo "💡 If you are using nvm then run 'nvm install' to install the version specified by the repo's .nvmrc."

+ 1 - 1
package.json

@@ -2,7 +2,7 @@
 	"name": "kilo-code",
 	"packageManager": "[email protected]",
 	"engines": {
-		"node": "20.18.1"
+		"node": "20.19.2"
 	},
 	"scripts": {
 		"preinstall": "node scripts/bootstrap.mjs",

+ 1 - 1
packages/build/package.json

@@ -8,7 +8,7 @@
 	"scripts": {
 		"lint": "eslint src --ext=ts --max-warnings=0",
 		"check-types": "tsc --noEmit",
-		"test": "vitest --globals --run",
+		"test": "vitest run",
 		"build": "tsc",
 		"clean": "rimraf dist .turbo"
 	},

+ 11 - 1
packages/build/src/__tests__/index.test.ts

@@ -1,4 +1,4 @@
-// npx vitest --globals run src/__tests__/index.test.ts
+// npx vitest run src/__tests__/index.test.ts
 
 import { generatePackageJson } from "../index.js"
 
@@ -67,6 +67,11 @@ describe("generatePackageJson", () => {
 								group: "navigation@6",
 								when: "activeWebviewPanelId == roo-cline.TabPanelProvider",
 							},
+							{
+								command: "roo-cline.accountButtonClicked",
+								group: "navigation@6",
+								when: "activeWebviewPanelId == roo-cline.TabPanelProvider && config.roo-cline.rooCodeCloudEnabled",
+							},
 						],
 					},
 					submenus: [
@@ -175,6 +180,11 @@ describe("generatePackageJson", () => {
 							group: "navigation@6",
 							when: "activeWebviewPanelId == roo-code-nightly.TabPanelProvider",
 						},
+						{
+							command: "roo-code-nightly.accountButtonClicked",
+							group: "navigation@6",
+							when: "activeWebviewPanelId == roo-code-nightly.TabPanelProvider && config.roo-code-nightly.rooCodeCloudEnabled",
+						},
 					],
 				},
 				submenus: [

+ 4 - 4
packages/build/src/esbuild.ts

@@ -213,12 +213,12 @@ function transformArrayRecord<T>(obj: Record<string, any[]>, from: string, to: s
 	return Object.entries(obj).reduce(
 		(acc, [key, ary]) => ({
 			...acc,
-			[key.replace(from, to)]: ary.map((item) => {
+			[key.replaceAll(from, to)]: ary.map((item) => {
 				const transformedItem = { ...item }
 
 				for (const prop of props) {
 					if (prop in item && typeof item[prop] === "string") {
-						transformedItem[prop] = item[prop].replace(from, to)
+						transformedItem[prop] = item[prop].replaceAll(from, to)
 					}
 				}
 
@@ -232,7 +232,7 @@ function transformArrayRecord<T>(obj: Record<string, any[]>, from: string, to: s
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
 function transformArray<T>(arr: any[], from: string, to: string, idProp: string): T[] {
 	return arr.map(({ [idProp]: id, ...rest }) => ({
-		[idProp]: id.replace(from, to),
+		[idProp]: id.replaceAll(from, to),
 		...rest,
 	}))
 }
@@ -242,7 +242,7 @@ function transformRecord<T>(obj: Record<string, any>, from: string, to: string):
 	return Object.entries(obj).reduce(
 		(acc, [key, value]) => ({
 			...acc,
-			[key.replace(from, to)]: value,
+			[key.replaceAll(from, to)]: value,
 		}),
 		{} as T,
 	)

+ 8 - 0
packages/build/vitest.config.ts

@@ -0,0 +1,8 @@
+import { defineConfig } from "vitest/config"
+
+export default defineConfig({
+	test: {
+		globals: true,
+		environment: "node",
+	},
+})

+ 4 - 0
packages/cloud/eslint.config.mjs

@@ -0,0 +1,4 @@
+import { config } from "@roo-code/config-eslint/base"
+
+/** @type {import("eslint").Linter.Config} */
+export default [...config]

+ 26 - 0
packages/cloud/package.json

@@ -0,0 +1,26 @@
+{
+	"name": "@roo-code/cloud",
+	"description": "Roo Code Cloud VSCode integration.",
+	"version": "0.0.0",
+	"type": "module",
+	"exports": "./src/index.ts",
+	"scripts": {
+		"lint": "eslint src --ext=ts --max-warnings=0",
+		"check-types": "tsc --noEmit",
+		"test": "vitest run",
+		"clean": "rimraf dist .turbo"
+	},
+	"dependencies": {
+		"@roo-code/telemetry": "workspace:^",
+		"@roo-code/types": "workspace:^",
+		"axios": "^1.7.4",
+		"zod": "^3.24.2"
+	},
+	"devDependencies": {
+		"@roo-code/config-eslint": "workspace:^",
+		"@roo-code/config-typescript": "workspace:^",
+		"@types/node": "^22.15.20",
+		"@types/vscode": "^1.84.0",
+		"vitest": "^3.1.3"
+	}
+}

+ 448 - 0
packages/cloud/src/AuthService.ts

@@ -0,0 +1,448 @@
+import crypto from "crypto"
+import EventEmitter from "events"
+
+import axios from "axios"
+import * as vscode from "vscode"
+import { z } from "zod"
+
+import type { CloudUserInfo } from "@roo-code/types"
+
+import { getClerkBaseUrl, getRooCodeApiUrl } from "./Config"
+import { RefreshTimer } from "./RefreshTimer"
+
+export interface AuthServiceEvents {
+	"active-session": [data: { previousState: AuthState }]
+	"logged-out": [data: { previousState: AuthState }]
+	"user-info": [data: { userInfo: CloudUserInfo }]
+}
+
+const authCredentialsSchema = z.object({
+	clientToken: z.string().min(1, "Client token cannot be empty"),
+	sessionId: z.string().min(1, "Session ID cannot be empty"),
+})
+
+type AuthCredentials = z.infer<typeof authCredentialsSchema>
+
+const AUTH_CREDENTIALS_KEY = "clerk-auth-credentials"
+const AUTH_STATE_KEY = "clerk-auth-state"
+
+type AuthState = "initializing" | "logged-out" | "active-session" | "inactive-session"
+
+export class AuthService extends EventEmitter<AuthServiceEvents> {
+	private context: vscode.ExtensionContext
+	private timer: RefreshTimer
+	private state: AuthState = "initializing"
+
+	private credentials: AuthCredentials | null = null
+	private sessionToken: string | null = null
+	private userInfo: CloudUserInfo | null = null
+
+	constructor(context: vscode.ExtensionContext) {
+		super()
+
+		this.context = context
+
+		this.timer = new RefreshTimer({
+			callback: async () => {
+				await this.refreshSession()
+				return true
+			},
+			successInterval: 50_000,
+			initialBackoffMs: 1_000,
+			maxBackoffMs: 300_000,
+		})
+	}
+
+	private async handleCredentialsChange(): Promise<void> {
+		try {
+			const credentials = await this.loadCredentials()
+
+			if (credentials) {
+				if (
+					this.credentials === null ||
+					this.credentials.clientToken !== credentials.clientToken ||
+					this.credentials.sessionId !== credentials.sessionId
+				) {
+					this.transitionToInactiveSession(credentials)
+				}
+			} else {
+				if (this.state !== "logged-out") {
+					this.transitionToLoggedOut()
+				}
+			}
+		} catch (error) {
+			console.error("[auth] Error handling credentials change:", error)
+		}
+	}
+
+	private transitionToLoggedOut(): void {
+		this.timer.stop()
+
+		const previousState = this.state
+
+		this.credentials = null
+		this.sessionToken = null
+		this.userInfo = null
+		this.state = "logged-out"
+
+		this.emit("logged-out", { previousState })
+
+		console.log("[auth] Transitioned to logged-out state")
+	}
+
+	private transitionToInactiveSession(credentials: AuthCredentials): void {
+		this.credentials = credentials
+		this.state = "inactive-session"
+
+		this.sessionToken = null
+		this.userInfo = null
+
+		this.timer.start()
+
+		console.log("[auth] Transitioned to inactive-session state")
+	}
+
+	/**
+	 * Initialize the auth state
+	 *
+	 * This method loads tokens from storage and determines the current auth state.
+	 * It also starts the refresh timer if we have an active session.
+	 */
+	public async initialize(): Promise<void> {
+		if (this.state !== "initializing") {
+			console.log("[auth] initialize() called after already initialized")
+			return
+		}
+
+		await this.handleCredentialsChange()
+
+		this.context.subscriptions.push(
+			this.context.secrets.onDidChange((e) => {
+				if (e.key === AUTH_CREDENTIALS_KEY) {
+					this.handleCredentialsChange()
+				}
+			}),
+		)
+	}
+
+	private async storeCredentials(credentials: AuthCredentials): Promise<void> {
+		await this.context.secrets.store(AUTH_CREDENTIALS_KEY, JSON.stringify(credentials))
+	}
+
+	private async loadCredentials(): Promise<AuthCredentials | null> {
+		const credentialsJson = await this.context.secrets.get(AUTH_CREDENTIALS_KEY)
+		if (!credentialsJson) return null
+
+		try {
+			const parsedJson = JSON.parse(credentialsJson)
+			return authCredentialsSchema.parse(parsedJson)
+		} catch (error) {
+			if (error instanceof z.ZodError) {
+				console.error("[auth] Invalid credentials format:", error.errors)
+			} else {
+				console.error("[auth] Failed to parse stored credentials:", error)
+			}
+			return null
+		}
+	}
+
+	private async clearCredentials(): Promise<void> {
+		await this.context.secrets.delete(AUTH_CREDENTIALS_KEY)
+	}
+
+	/**
+	 * Start the login process
+	 *
+	 * This method initiates the authentication flow by generating a state parameter
+	 * and opening the browser to the authorization URL.
+	 */
+	public async login(): Promise<void> {
+		try {
+			// Generate a cryptographically random state parameter.
+			const state = crypto.randomBytes(16).toString("hex")
+			await this.context.globalState.update(AUTH_STATE_KEY, state)
+			const packageJSON = this.context.extension?.packageJSON
+			const publisher = packageJSON?.publisher ?? "RooVeterinaryInc"
+			const name = packageJSON?.name ?? "roo-cline"
+			const params = new URLSearchParams({
+				state,
+				auth_redirect: `${vscode.env.uriScheme}://${publisher}.${name}`,
+			})
+			const url = `${getRooCodeApiUrl()}/extension/sign-in?${params.toString()}`
+			await vscode.env.openExternal(vscode.Uri.parse(url))
+		} catch (error) {
+			console.error(`[auth] Error initiating Roo Code Cloud auth: ${error}`)
+			throw new Error(`Failed to initiate Roo Code Cloud authentication: ${error}`)
+		}
+	}
+
+	/**
+	 * Handle the callback from Roo Code Cloud
+	 *
+	 * This method is called when the user is redirected back to the extension
+	 * after authenticating with Roo Code Cloud.
+	 *
+	 * @param code The authorization code from the callback
+	 * @param state The state parameter from the callback
+	 */
+	public async handleCallback(code: string | null, state: string | null): Promise<void> {
+		if (!code || !state) {
+			vscode.window.showInformationMessage("Invalid Roo Code Cloud sign in url")
+			return
+		}
+
+		try {
+			// Validate state parameter to prevent CSRF attacks.
+			const storedState = this.context.globalState.get(AUTH_STATE_KEY)
+
+			if (state !== storedState) {
+				console.log("[auth] State mismatch in callback")
+				throw new Error("Invalid state parameter. Authentication request may have been tampered with.")
+			}
+
+			const { credentials } = await this.clerkSignIn(code)
+
+			await this.storeCredentials(credentials)
+
+			vscode.window.showInformationMessage("Successfully authenticated with Roo Code Cloud")
+			console.log("[auth] Successfully authenticated with Roo Code Cloud")
+		} catch (error) {
+			console.log(`[auth] Error handling Roo Code Cloud callback: ${error}`)
+			const previousState = this.state
+			this.state = "logged-out"
+			this.emit("logged-out", { previousState })
+			throw new Error(`Failed to handle Roo Code Cloud callback: ${error}`)
+		}
+	}
+
+	/**
+	 * Log out
+	 *
+	 * This method removes all stored tokens and stops the refresh timer.
+	 */
+	public async logout(): Promise<void> {
+		const oldCredentials = this.credentials
+
+		try {
+			// Clear credentials from storage - onDidChange will handle state transitions
+			await this.clearCredentials()
+			await this.context.globalState.update(AUTH_STATE_KEY, undefined)
+
+			if (oldCredentials) {
+				try {
+					await this.clerkLogout(oldCredentials)
+				} catch (error) {
+					console.error("[auth] Error calling clerkLogout:", error)
+				}
+			}
+
+			vscode.window.showInformationMessage("Logged out from Roo Code Cloud")
+			console.log("[auth] Logged out from Roo Code Cloud")
+		} catch (error) {
+			console.log(`[auth] Error logging out from Roo Code Cloud: ${error}`)
+			throw new Error(`Failed to log out from Roo Code Cloud: ${error}`)
+		}
+	}
+
+	public getState(): AuthState {
+		return this.state
+	}
+
+	public getSessionToken(): string | undefined {
+		if (this.state === "active-session" && this.sessionToken) {
+			return this.sessionToken
+		}
+
+		return
+	}
+
+	/**
+	 * Check if the user is authenticated
+	 *
+	 * @returns True if the user is authenticated (has an active or inactive session)
+	 */
+	public isAuthenticated(): boolean {
+		return this.state === "active-session" || this.state === "inactive-session"
+	}
+
+	public hasActiveSession(): boolean {
+		return this.state === "active-session"
+	}
+
+	/**
+	 * Refresh the session
+	 *
+	 * This method refreshes the session token using the client token.
+	 */
+	private async refreshSession(): Promise<void> {
+		if (!this.credentials) {
+			console.log("[auth] Cannot refresh session: missing credentials")
+			this.state = "inactive-session"
+			return
+		}
+
+		const previousState = this.state
+		this.sessionToken = await this.clerkCreateSessionToken()
+		this.state = "active-session"
+
+		if (previousState !== "active-session") {
+			console.log("[auth] Transitioned to active-session state")
+			this.emit("active-session", { previousState })
+			this.fetchUserInfo()
+		}
+	}
+
+	private async fetchUserInfo(): Promise<void> {
+		if (!this.credentials) {
+			return
+		}
+
+		this.userInfo = await this.clerkMe()
+		this.emit("user-info", { userInfo: this.userInfo })
+	}
+
+	/**
+	 * Extract user information from the ID token
+	 *
+	 * @returns User information from ID token claims or null if no ID token available
+	 */
+	public getUserInfo(): CloudUserInfo | null {
+		return this.userInfo
+	}
+
+	private async clerkSignIn(ticket: string): Promise<{ credentials: AuthCredentials; sessionToken: string }> {
+		const formData = new URLSearchParams()
+		formData.append("strategy", "ticket")
+		formData.append("ticket", ticket)
+
+		const response = await axios.post(`${getClerkBaseUrl()}/v1/client/sign_ins`, formData, {
+			headers: {
+				"Content-Type": "application/x-www-form-urlencoded",
+				"User-Agent": this.userAgent(),
+			},
+		})
+
+		// 3. Extract the client token from the Authorization header.
+		const clientToken = response.headers.authorization
+
+		if (!clientToken) {
+			throw new Error("No authorization header found in the response")
+		}
+
+		// 4. Find the session using created_session_id and extract the JWT.
+		const sessionId = response.data?.response?.created_session_id
+
+		if (!sessionId) {
+			throw new Error("No session ID found in the response")
+		}
+
+		// Find the session in the client sessions array.
+		const session = response.data?.client?.sessions?.find((s: { id: string }) => s.id === sessionId)
+
+		if (!session) {
+			throw new Error("Session not found in the response")
+		}
+
+		// Extract the session token (JWT) and store it.
+		const sessionToken = session.last_active_token?.jwt
+
+		if (!sessionToken) {
+			throw new Error("Session does not have a token")
+		}
+
+		const credentials = authCredentialsSchema.parse({ clientToken, sessionId })
+
+		return { credentials, sessionToken }
+	}
+
+	private async clerkCreateSessionToken(): Promise<string> {
+		const formData = new URLSearchParams()
+		formData.append("_is_native", "1")
+
+		const response = await axios.post(
+			`${getClerkBaseUrl()}/v1/client/sessions/${this.credentials!.sessionId}/tokens`,
+			formData,
+			{
+				headers: {
+					"Content-Type": "application/x-www-form-urlencoded",
+					Authorization: `Bearer ${this.credentials!.clientToken}`,
+					"User-Agent": this.userAgent(),
+				},
+			},
+		)
+
+		const sessionToken = response.data?.jwt
+
+		if (!sessionToken) {
+			throw new Error("No JWT found in refresh response")
+		}
+
+		return sessionToken
+	}
+
+	private async clerkMe(): Promise<CloudUserInfo> {
+		const response = await axios.get(`${getClerkBaseUrl()}/v1/me`, {
+			headers: {
+				Authorization: `Bearer ${this.credentials!.clientToken}`,
+				"User-Agent": this.userAgent(),
+			},
+		})
+
+		const userData = response.data?.response
+
+		if (!userData) {
+			throw new Error("No response user data")
+		}
+
+		const userInfo: CloudUserInfo = {}
+
+		userInfo.name = `${userData?.first_name} ${userData?.last_name}`
+		const primaryEmailAddressId = userData?.primary_email_address_id
+		const emailAddresses = userData?.email_addresses
+
+		if (primaryEmailAddressId && emailAddresses) {
+			userInfo.email = emailAddresses.find(
+				(email: { id: string }) => primaryEmailAddressId === email?.id,
+			)?.email_address
+		}
+
+		userInfo.picture = userData?.image_url
+		return userInfo
+	}
+
+	private async clerkLogout(credentials: AuthCredentials): Promise<void> {
+		const formData = new URLSearchParams()
+		formData.append("_is_native", "1")
+
+		await axios.post(`${getClerkBaseUrl()}/v1/client/sessions/${credentials.sessionId}/remove`, formData, {
+			headers: {
+				Authorization: `Bearer ${credentials.clientToken}`,
+				"User-Agent": this.userAgent(),
+			},
+		})
+	}
+
+	private userAgent(): string {
+		return `Roo-Code ${this.context.extension?.packageJSON?.version}`
+	}
+
+	private static _instance: AuthService | null = null
+
+	static get instance() {
+		if (!this._instance) {
+			throw new Error("AuthService not initialized")
+		}
+
+		return this._instance
+	}
+
+	static async createInstance(context: vscode.ExtensionContext) {
+		if (this._instance) {
+			throw new Error("AuthService instance already created")
+		}
+
+		this._instance = new AuthService(context)
+		await this._instance.initialize()
+		return this._instance
+	}
+}

+ 168 - 0
packages/cloud/src/CloudService.ts

@@ -0,0 +1,168 @@
+import * as vscode from "vscode"
+
+import type { CloudUserInfo, TelemetryEvent, OrganizationAllowList } from "@roo-code/types"
+import { TelemetryService } from "@roo-code/telemetry"
+
+import { CloudServiceCallbacks } from "./types"
+import { AuthService } from "./AuthService"
+import { SettingsService } from "./SettingsService"
+import { TelemetryClient } from "./TelemetryClient"
+
+export class CloudService {
+	private static _instance: CloudService | null = null
+
+	private context: vscode.ExtensionContext
+	private callbacks: CloudServiceCallbacks
+	private authListener: () => void
+	private authService: AuthService | null = null
+	private settingsService: SettingsService | null = null
+	private telemetryClient: TelemetryClient | null = null
+	private isInitialized = false
+
+	private constructor(context: vscode.ExtensionContext, callbacks: CloudServiceCallbacks) {
+		this.context = context
+		this.callbacks = callbacks
+		this.authListener = () => {
+			this.callbacks.stateChanged?.()
+		}
+	}
+
+	public async initialize(): Promise<void> {
+		if (this.isInitialized) {
+			return
+		}
+
+		try {
+			this.authService = await AuthService.createInstance(this.context)
+
+			this.authService.on("active-session", this.authListener)
+			this.authService.on("logged-out", this.authListener)
+			this.authService.on("user-info", this.authListener)
+
+			this.settingsService = await SettingsService.createInstance(this.context, () =>
+				this.callbacks.stateChanged?.(),
+			)
+
+			this.telemetryClient = new TelemetryClient(this.authService, this.settingsService)
+
+			try {
+				TelemetryService.instance.register(this.telemetryClient)
+			} catch (error) {
+				console.warn("[CloudService] Failed to register TelemetryClient:", error)
+			}
+
+			this.isInitialized = true
+		} catch (error) {
+			console.error("[CloudService] Failed to initialize:", error)
+			throw new Error(`Failed to initialize CloudService: ${error}`)
+		}
+	}
+
+	// AuthService
+
+	public async login(): Promise<void> {
+		this.ensureInitialized()
+		return this.authService!.login()
+	}
+
+	public async logout(): Promise<void> {
+		this.ensureInitialized()
+		return this.authService!.logout()
+	}
+
+	public isAuthenticated(): boolean {
+		this.ensureInitialized()
+		return this.authService!.isAuthenticated()
+	}
+
+	public hasActiveSession(): boolean {
+		this.ensureInitialized()
+		return this.authService!.hasActiveSession()
+	}
+
+	public getUserInfo(): CloudUserInfo | null {
+		this.ensureInitialized()
+		return this.authService!.getUserInfo()
+	}
+
+	public getAuthState(): string {
+		this.ensureInitialized()
+		return this.authService!.getState()
+	}
+
+	public async handleAuthCallback(code: string | null, state: string | null): Promise<void> {
+		this.ensureInitialized()
+		return this.authService!.handleCallback(code, state)
+	}
+
+	// SettingsService
+
+	public getAllowList(): OrganizationAllowList {
+		this.ensureInitialized()
+		return this.settingsService!.getAllowList()
+	}
+
+	// TelemetryClient
+
+	public captureEvent(event: TelemetryEvent): void {
+		this.ensureInitialized()
+		this.telemetryClient!.capture(event)
+	}
+
+	// Lifecycle
+
+	public dispose(): void {
+		if (this.authService) {
+			this.authService.off("active-session", this.authListener)
+			this.authService.off("logged-out", this.authListener)
+			this.authService.off("user-info", this.authListener)
+		}
+		if (this.settingsService) {
+			this.settingsService.dispose()
+		}
+
+		this.isInitialized = false
+	}
+
+	private ensureInitialized(): void {
+		if (!this.isInitialized || !this.authService || !this.settingsService || !this.telemetryClient) {
+			throw new Error("CloudService not initialized.")
+		}
+	}
+
+	static get instance(): CloudService {
+		if (!this._instance) {
+			throw new Error("CloudService not initialized")
+		}
+
+		return this._instance
+	}
+
+	static async createInstance(
+		context: vscode.ExtensionContext,
+		callbacks: CloudServiceCallbacks = {},
+	): Promise<CloudService> {
+		if (this._instance) {
+			throw new Error("CloudService instance already created")
+		}
+
+		this._instance = new CloudService(context, callbacks)
+		await this._instance.initialize()
+		return this._instance
+	}
+
+	static hasInstance(): boolean {
+		return this._instance !== null && this._instance.isInitialized
+	}
+
+	static resetInstance(): void {
+		if (this._instance) {
+			this._instance.dispose()
+			this._instance = null
+		}
+	}
+
+	static isEnabled(): boolean {
+		return !!this._instance?.isAuthenticated()
+	}
+}

+ 2 - 0
packages/cloud/src/Config.ts

@@ -0,0 +1,2 @@
+export const getClerkBaseUrl = () => process.env.CLERK_BASE_URL || "https://clerk.roocode.com"
+export const getRooCodeApiUrl = () => process.env.ROO_CODE_API_URL || "https://app.roocode.com"

+ 1 - 1
src/utils/refresh-timer.ts → packages/cloud/src/RefreshTimer.ts

@@ -146,7 +146,7 @@ export class RefreshTimer {
 			const result = await this.callback()
 
 			this.scheduleNextAttempt(result)
-		} catch (error) {
+		} catch (_error) {
 			// Treat errors as failed attempts
 			this.scheduleNextAttempt(false)
 		}

+ 137 - 0
packages/cloud/src/SettingsService.ts

@@ -0,0 +1,137 @@
+import * as vscode from "vscode"
+
+import {
+	ORGANIZATION_ALLOW_ALL,
+	OrganizationAllowList,
+	OrganizationSettings,
+	organizationSettingsSchema,
+} from "@roo-code/types"
+
+import { getRooCodeApiUrl } from "./Config"
+import { AuthService } from "./AuthService"
+import { RefreshTimer } from "./RefreshTimer"
+
+const ORGANIZATION_SETTINGS_CACHE_KEY = "organization-settings"
+
+export class SettingsService {
+	private static _instance: SettingsService | null = null
+
+	private context: vscode.ExtensionContext
+	private authService: AuthService
+	private settings: OrganizationSettings | undefined = undefined
+	private timer: RefreshTimer
+
+	private constructor(context: vscode.ExtensionContext, authService: AuthService, callback: () => void) {
+		this.context = context
+		this.authService = authService
+
+		this.timer = new RefreshTimer({
+			callback: async () => {
+				await this.fetchSettings(callback)
+				return true
+			},
+			successInterval: 30000,
+			initialBackoffMs: 1000,
+			maxBackoffMs: 30000,
+		})
+	}
+
+	public initialize(): void {
+		this.loadCachedSettings()
+
+		this.authService.on("active-session", () => {
+			this.timer.start()
+		})
+
+		this.authService.on("logged-out", () => {
+			this.timer.stop()
+			this.removeSettings()
+		})
+
+		if (this.authService.hasActiveSession()) {
+			this.timer.start()
+		}
+	}
+
+	private async fetchSettings(callback: () => void): Promise<void> {
+		const token = this.authService.getSessionToken()
+
+		if (!token) {
+			return
+		}
+
+		try {
+			const response = await fetch(`${getRooCodeApiUrl()}/api/organization-settings`, {
+				headers: {
+					Authorization: `Bearer ${token}`,
+				},
+			})
+
+			if (!response.ok) {
+				console.error(`Failed to fetch organization settings: ${response.status} ${response.statusText}`)
+				return
+			}
+
+			const data = await response.json()
+			const result = organizationSettingsSchema.safeParse(data)
+
+			if (!result.success) {
+				console.error("Invalid organization settings format:", result.error)
+				return
+			}
+
+			const newSettings = result.data
+
+			if (!this.settings || this.settings.version !== newSettings.version) {
+				this.settings = newSettings
+				await this.cacheSettings()
+				callback()
+			}
+		} catch (error) {
+			console.error("Error fetching organization settings:", error)
+		}
+	}
+
+	private async cacheSettings(): Promise<void> {
+		await this.context.globalState.update(ORGANIZATION_SETTINGS_CACHE_KEY, this.settings)
+	}
+
+	private loadCachedSettings(): void {
+		this.settings = this.context.globalState.get<OrganizationSettings>(ORGANIZATION_SETTINGS_CACHE_KEY)
+	}
+
+	public getAllowList(): OrganizationAllowList {
+		return this.settings?.allowList || ORGANIZATION_ALLOW_ALL
+	}
+
+	public getSettings(): OrganizationSettings | undefined {
+		return this.settings
+	}
+
+	public async removeSettings(): Promise<void> {
+		this.settings = undefined
+		await this.cacheSettings()
+	}
+
+	public dispose(): void {
+		this.timer.stop()
+	}
+
+	static get instance() {
+		if (!this._instance) {
+			throw new Error("SettingsService not initialized")
+		}
+
+		return this._instance
+	}
+
+	static async createInstance(context: vscode.ExtensionContext, callback: () => void) {
+		if (this._instance) {
+			throw new Error("SettingsService instance already created")
+		}
+
+		this._instance = new SettingsService(context, AuthService.instance, callback)
+		this._instance.initialize()
+		return this._instance
+	}
+}

+ 104 - 0
packages/cloud/src/TelemetryClient.ts

@@ -0,0 +1,104 @@
+import { TelemetryEventName, type TelemetryEvent, rooCodeTelemetryEventSchema } from "@roo-code/types"
+import { BaseTelemetryClient } from "@roo-code/telemetry"
+
+import { getRooCodeApiUrl } from "./Config"
+import { AuthService } from "./AuthService"
+import { SettingsService } from "./SettingsService"
+
+export class TelemetryClient extends BaseTelemetryClient {
+	constructor(
+		private authService: AuthService,
+		private settingsService: SettingsService,
+		debug = false,
+	) {
+		super(
+			{
+				type: "exclude",
+				events: [TelemetryEventName.TASK_CONVERSATION_MESSAGE],
+			},
+			debug,
+		)
+	}
+
+	private async fetch(path: string, options: RequestInit) {
+		if (!this.authService.isAuthenticated()) {
+			return
+		}
+
+		const token = this.authService.getSessionToken()
+
+		if (!token) {
+			console.error(`[TelemetryClient#fetch] Unauthorized: No session token available.`)
+			return
+		}
+
+		const response = await fetch(`${getRooCodeApiUrl()}/api/${path}`, {
+			...options,
+			headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
+		})
+
+		if (!response.ok) {
+			console.error(
+				`[TelemetryClient#fetch] ${options.method} ${path} -> ${response.status} ${response.statusText}`,
+			)
+		}
+	}
+
+	public override async capture(event: TelemetryEvent) {
+		if (!this.isTelemetryEnabled() || !this.isEventCapturable(event.event)) {
+			if (this.debug) {
+				console.info(`[TelemetryClient#capture] Skipping event: ${event.event}`)
+			}
+
+			return
+		}
+
+		const payload = {
+			type: event.event,
+			properties: await this.getEventProperties(event),
+		}
+
+		if (this.debug) {
+			console.info(`[TelemetryClient#capture] ${JSON.stringify(payload)}`)
+		}
+
+		const result = rooCodeTelemetryEventSchema.safeParse(payload)
+
+		if (!result.success) {
+			console.error(
+				`[TelemetryClient#capture] Invalid telemetry event: ${result.error.message} - ${JSON.stringify(payload)}`,
+			)
+
+			return
+		}
+
+		try {
+			await this.fetch(`events`, { method: "POST", body: JSON.stringify(result.data) })
+		} catch (error) {
+			console.error(`[TelemetryClient#capture] Error sending telemetry event: ${error}`)
+		}
+	}
+
+	public override updateTelemetryState(_didUserOptIn: boolean) {}
+
+	public override isTelemetryEnabled(): boolean {
+		return true
+	}
+
+	protected override isEventCapturable(eventName: TelemetryEventName): boolean {
+		// Ensure that this event type is supported by the telemetry client
+		if (!super.isEventCapturable(eventName)) {
+			return false
+		}
+
+		// Only record message telemetry if a cloud account is present and explicitly configured to record messages
+		if (eventName === TelemetryEventName.TASK_MESSAGE) {
+			return this.settingsService.getSettings()?.cloudSettings?.recordTaskMessages || false
+		}
+
+		// Other telemetry types are capturable at this point
+		return true
+	}
+
+	public override async shutdown() {}
+}

+ 50 - 0
packages/cloud/src/__mocks__/vscode.ts

@@ -0,0 +1,50 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import { vi } from "vitest"
+
+export const window = {
+	showInformationMessage: vi.fn(),
+	showErrorMessage: vi.fn(),
+}
+
+export const env = {
+	openExternal: vi.fn(),
+}
+
+export const Uri = {
+	parse: vi.fn((uri: string) => ({ toString: () => uri })),
+}
+
+export interface ExtensionContext {
+	secrets: {
+		get: (key: string) => Promise<string | undefined>
+		store: (key: string, value: string) => Promise<void>
+		delete: (key: string) => Promise<void>
+	}
+	globalState: {
+		get: <T>(key: string) => T | undefined
+		update: (key: string, value: any) => Promise<void>
+	}
+	extension?: {
+		packageJSON?: {
+			version?: string
+		}
+	}
+}
+
+// Mock implementation for tests
+export const mockExtensionContext: ExtensionContext = {
+	secrets: {
+		get: vi.fn().mockResolvedValue(undefined),
+		store: vi.fn().mockResolvedValue(undefined),
+		delete: vi.fn().mockResolvedValue(undefined),
+	},
+	globalState: {
+		get: vi.fn().mockReturnValue(undefined),
+		update: vi.fn().mockResolvedValue(undefined),
+	},
+	extension: {
+		packageJSON: {
+			version: "1.0.0",
+		},
+	},
+}

+ 241 - 0
packages/cloud/src/__tests__/CloudService.test.ts

@@ -0,0 +1,241 @@
+// npx vitest run src/__tests__/CloudService.test.ts
+
+import * as vscode from "vscode"
+
+import { CloudService } from "../CloudService"
+import { AuthService } from "../AuthService"
+import { SettingsService } from "../SettingsService"
+import { TelemetryService } from "@roo-code/telemetry"
+import { CloudServiceCallbacks } from "../types"
+
+vi.mock("vscode", () => ({
+	ExtensionContext: vi.fn(),
+	window: {
+		showInformationMessage: vi.fn(),
+		showErrorMessage: vi.fn(),
+	},
+	env: {
+		openExternal: vi.fn(),
+	},
+	Uri: {
+		parse: vi.fn(),
+	},
+}))
+
+vi.mock("@roo-code/telemetry")
+
+vi.mock("../AuthService")
+
+vi.mock("../SettingsService")
+
+describe("CloudService", () => {
+	let mockContext: vscode.ExtensionContext
+	let mockAuthService: {
+		initialize: ReturnType<typeof vi.fn>
+		login: ReturnType<typeof vi.fn>
+		logout: ReturnType<typeof vi.fn>
+		isAuthenticated: ReturnType<typeof vi.fn>
+		hasActiveSession: ReturnType<typeof vi.fn>
+		getUserInfo: ReturnType<typeof vi.fn>
+		getState: ReturnType<typeof vi.fn>
+		getSessionToken: ReturnType<typeof vi.fn>
+		handleCallback: ReturnType<typeof vi.fn>
+		on: ReturnType<typeof vi.fn>
+		off: ReturnType<typeof vi.fn>
+		once: ReturnType<typeof vi.fn>
+		emit: ReturnType<typeof vi.fn>
+	}
+	let mockSettingsService: {
+		initialize: ReturnType<typeof vi.fn>
+		getSettings: ReturnType<typeof vi.fn>
+		getAllowList: ReturnType<typeof vi.fn>
+		dispose: ReturnType<typeof vi.fn>
+	}
+	let mockTelemetryService: {
+		hasInstance: ReturnType<typeof vi.fn>
+		instance: {
+			register: ReturnType<typeof vi.fn>
+		}
+	}
+
+	beforeEach(() => {
+		CloudService.resetInstance()
+
+		mockContext = {
+			secrets: {
+				get: vi.fn(),
+				store: vi.fn(),
+				delete: vi.fn(),
+			},
+			globalState: {
+				get: vi.fn(),
+				update: vi.fn(),
+			},
+			extension: {
+				packageJSON: {
+					version: "1.0.0",
+				},
+			},
+		} as unknown as vscode.ExtensionContext
+
+		mockAuthService = {
+			initialize: vi.fn(),
+			login: vi.fn(),
+			logout: vi.fn(),
+			isAuthenticated: vi.fn().mockReturnValue(false),
+			hasActiveSession: vi.fn().mockReturnValue(false),
+			getUserInfo: vi.fn(),
+			getState: vi.fn().mockReturnValue("logged-out"),
+			getSessionToken: vi.fn(),
+			handleCallback: vi.fn(),
+			on: vi.fn(),
+			off: vi.fn(),
+			once: vi.fn(),
+			emit: vi.fn(),
+		}
+
+		mockSettingsService = {
+			initialize: vi.fn(),
+			getSettings: vi.fn(),
+			getAllowList: vi.fn(),
+			dispose: vi.fn(),
+		}
+
+		mockTelemetryService = {
+			hasInstance: vi.fn().mockReturnValue(true),
+			instance: {
+				register: vi.fn(),
+			},
+		}
+
+		vi.mocked(AuthService.createInstance).mockResolvedValue(mockAuthService as unknown as AuthService)
+		Object.defineProperty(AuthService, "instance", { get: () => mockAuthService, configurable: true })
+
+		vi.mocked(SettingsService.createInstance).mockResolvedValue(mockSettingsService as unknown as SettingsService)
+		Object.defineProperty(SettingsService, "instance", { get: () => mockSettingsService, configurable: true })
+
+		vi.mocked(TelemetryService.hasInstance).mockReturnValue(true)
+		Object.defineProperty(TelemetryService, "instance", {
+			get: () => mockTelemetryService.instance,
+			configurable: true,
+		})
+	})
+
+	afterEach(() => {
+		vi.clearAllMocks()
+		CloudService.resetInstance()
+	})
+
+	describe("createInstance", () => {
+		it("should create and initialize CloudService instance", async () => {
+			const callbacks = {
+				stateChanged: vi.fn(),
+			}
+
+			const cloudService = await CloudService.createInstance(mockContext, callbacks)
+
+			expect(cloudService).toBeInstanceOf(CloudService)
+			expect(AuthService.createInstance).toHaveBeenCalledWith(mockContext)
+			expect(SettingsService.createInstance).toHaveBeenCalledWith(mockContext, expect.any(Function))
+		})
+
+		it("should throw error if instance already exists", async () => {
+			await CloudService.createInstance(mockContext)
+
+			await expect(CloudService.createInstance(mockContext)).rejects.toThrow(
+				"CloudService instance already created",
+			)
+		})
+	})
+
+	describe("authentication methods", () => {
+		let cloudService: CloudService
+		let callbacks: CloudServiceCallbacks
+
+		beforeEach(async () => {
+			callbacks = { stateChanged: vi.fn() }
+			cloudService = await CloudService.createInstance(mockContext, callbacks)
+		})
+
+		it("should delegate login to AuthService", async () => {
+			await cloudService.login()
+			expect(mockAuthService.login).toHaveBeenCalled()
+		})
+
+		it("should delegate logout to AuthService", async () => {
+			await cloudService.logout()
+			expect(mockAuthService.logout).toHaveBeenCalled()
+		})
+
+		it("should delegate isAuthenticated to AuthService", () => {
+			const result = cloudService.isAuthenticated()
+			expect(mockAuthService.isAuthenticated).toHaveBeenCalled()
+			expect(result).toBe(false)
+		})
+
+		it("should delegate hasActiveSession to AuthService", () => {
+			const result = cloudService.hasActiveSession()
+			expect(mockAuthService.hasActiveSession).toHaveBeenCalled()
+			expect(result).toBe(false)
+		})
+
+		it("should delegate getUserInfo to AuthService", async () => {
+			await cloudService.getUserInfo()
+			expect(mockAuthService.getUserInfo).toHaveBeenCalled()
+		})
+
+		it("should delegate getAuthState to AuthService", () => {
+			const result = cloudService.getAuthState()
+			expect(mockAuthService.getState).toHaveBeenCalled()
+			expect(result).toBe("logged-out")
+		})
+
+		it("should delegate handleAuthCallback to AuthService", async () => {
+			await cloudService.handleAuthCallback("code", "state")
+			expect(mockAuthService.handleCallback).toHaveBeenCalledWith("code", "state")
+		})
+	})
+
+	describe("organization settings methods", () => {
+		let cloudService: CloudService
+
+		beforeEach(async () => {
+			cloudService = await CloudService.createInstance(mockContext)
+		})
+
+		it("should delegate getAllowList to SettingsService", () => {
+			cloudService.getAllowList()
+			expect(mockSettingsService.getAllowList).toHaveBeenCalled()
+		})
+	})
+
+	describe("error handling", () => {
+		it("should throw error when accessing methods before initialization", () => {
+			expect(() => CloudService.instance.login()).toThrow("CloudService not initialized")
+		})
+
+		it("should throw error when accessing instance before creation", () => {
+			expect(() => CloudService.instance).toThrow("CloudService not initialized")
+		})
+	})
+
+	describe("hasInstance", () => {
+		it("should return false when no instance exists", () => {
+			expect(CloudService.hasInstance()).toBe(false)
+		})
+
+		it("should return true when instance exists and is initialized", async () => {
+			await CloudService.createInstance(mockContext)
+			expect(CloudService.hasInstance()).toBe(true)
+		})
+	})
+
+	describe("dispose", () => {
+		it("should dispose of all services and clean up", async () => {
+			const cloudService = await CloudService.createInstance(mockContext)
+			cloudService.dispose()
+
+			expect(mockSettingsService.dispose).toHaveBeenCalled()
+		})
+	})
+})

+ 22 - 22
src/utils/__tests__/refresh-timer.test.ts → packages/cloud/src/__tests__/RefreshTimer.test.ts

@@ -1,27 +1,27 @@
-import { RefreshTimer } from "../refresh-timer"
+// npx vitest run src/__tests__/RefreshTimer.test.ts
 
-// Mock timers
-jest.useFakeTimers()
+import { Mock } from "vitest"
+
+import { RefreshTimer } from "../RefreshTimer"
+
+vi.useFakeTimers()
 
 describe("RefreshTimer", () => {
-	let mockCallback: jest.Mock
+	let mockCallback: Mock
 	let refreshTimer: RefreshTimer
 
 	beforeEach(() => {
-		// Reset mocks before each test
-		mockCallback = jest.fn()
-
-		// Default mock implementation returns success
+		mockCallback = vi.fn()
 		mockCallback.mockResolvedValue(true)
 	})
 
 	afterEach(() => {
-		// Clean up after each test
 		if (refreshTimer) {
 			refreshTimer.stop()
 		}
-		jest.clearAllTimers()
-		jest.clearAllMocks()
+
+		vi.clearAllTimers()
+		vi.clearAllMocks()
 	})
 
 	it("should execute callback immediately when started", () => {
@@ -50,7 +50,7 @@ describe("RefreshTimer", () => {
 		expect(mockCallback).toHaveBeenCalledTimes(1)
 
 		// Fast-forward 50 seconds
-		jest.advanceTimersByTime(50000)
+		vi.advanceTimersByTime(50000)
 
 		// Callback should be called again
 		expect(mockCallback).toHaveBeenCalledTimes(2)
@@ -72,7 +72,7 @@ describe("RefreshTimer", () => {
 		expect(mockCallback).toHaveBeenCalledTimes(1)
 
 		// Fast-forward 1 second
-		jest.advanceTimersByTime(1000)
+		vi.advanceTimersByTime(1000)
 
 		// Callback should be called again
 		expect(mockCallback).toHaveBeenCalledTimes(2)
@@ -81,7 +81,7 @@ describe("RefreshTimer", () => {
 		await Promise.resolve()
 
 		// Fast-forward 2 seconds
-		jest.advanceTimersByTime(2000)
+		vi.advanceTimersByTime(2000)
 
 		// Callback should be called again
 		expect(mockCallback).toHaveBeenCalledTimes(3)
@@ -103,13 +103,13 @@ describe("RefreshTimer", () => {
 
 		// Fast-forward through multiple failures to reach max backoff
 		await Promise.resolve() // First attempt
-		jest.advanceTimersByTime(1000)
+		vi.advanceTimersByTime(1000)
 
 		await Promise.resolve() // Second attempt (backoff = 2000ms)
-		jest.advanceTimersByTime(2000)
+		vi.advanceTimersByTime(2000)
 
 		await Promise.resolve() // Third attempt (backoff = 4000ms)
-		jest.advanceTimersByTime(4000)
+		vi.advanceTimersByTime(4000)
 
 		await Promise.resolve() // Fourth attempt (backoff would be 8000ms but max is 5000ms)
 
@@ -132,13 +132,13 @@ describe("RefreshTimer", () => {
 		await Promise.resolve()
 
 		// Fast-forward 1 second
-		jest.advanceTimersByTime(1000)
+		vi.advanceTimersByTime(1000)
 
 		// Second attempt (succeeds)
 		await Promise.resolve()
 
 		// Fast-forward 5 seconds
-		jest.advanceTimersByTime(5000)
+		vi.advanceTimersByTime(5000)
 
 		// Third attempt (fails)
 		await Promise.resolve()
@@ -173,7 +173,7 @@ describe("RefreshTimer", () => {
 		refreshTimer.stop()
 
 		// Fast-forward a long time
-		jest.advanceTimersByTime(1000000)
+		vi.advanceTimersByTime(1000000)
 
 		// Callback should only have been called once (the initial call)
 		expect(mockCallback).toHaveBeenCalledTimes(1)
@@ -191,10 +191,10 @@ describe("RefreshTimer", () => {
 
 		// Fast-forward through a few failures
 		await Promise.resolve()
-		jest.advanceTimersByTime(1000)
+		vi.advanceTimersByTime(1000)
 
 		await Promise.resolve()
-		jest.advanceTimersByTime(2000)
+		vi.advanceTimersByTime(2000)
 
 		// Reset the timer
 		refreshTimer.reset()

+ 429 - 0
packages/cloud/src/__tests__/TelemetryClient.test.ts

@@ -0,0 +1,429 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
+// npx vitest run src/__tests__/TelemetryClient.test.ts
+
+import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"
+
+import { type TelemetryPropertiesProvider, TelemetryEventName } from "@roo-code/types"
+
+import { TelemetryClient } from "../TelemetryClient"
+
+const mockFetch = vi.fn()
+global.fetch = mockFetch as any
+
+describe("TelemetryClient", () => {
+	const getPrivateProperty = <T>(instance: any, propertyName: string): T => {
+		return instance[propertyName]
+	}
+
+	let mockAuthService: any
+	let mockSettingsService: any
+
+	beforeEach(() => {
+		vi.clearAllMocks()
+
+		// Create a mock AuthService instead of using the singleton
+		mockAuthService = {
+			getSessionToken: vi.fn().mockReturnValue("mock-token"),
+			getState: vi.fn().mockReturnValue("active-session"),
+			isAuthenticated: vi.fn().mockReturnValue(true),
+			hasActiveSession: vi.fn().mockReturnValue(true),
+		}
+
+		// Create a mock SettingsService
+		mockSettingsService = {
+			getSettings: vi.fn().mockReturnValue({
+				cloudSettings: {
+					recordTaskMessages: true,
+				},
+			}),
+		}
+
+		mockFetch.mockResolvedValue({
+			ok: true,
+			json: vi.fn().mockResolvedValue({}),
+		})
+
+		vi.spyOn(console, "info").mockImplementation(() => {})
+		vi.spyOn(console, "error").mockImplementation(() => {})
+	})
+
+	afterEach(() => {
+		vi.restoreAllMocks()
+	})
+
+	describe("isEventCapturable", () => {
+		it("should return true for events not in exclude list", () => {
+			const client = new TelemetryClient(mockAuthService, mockSettingsService)
+
+			const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>(
+				client,
+				"isEventCapturable",
+			).bind(client)
+
+			expect(isEventCapturable(TelemetryEventName.TASK_CREATED)).toBe(true)
+			expect(isEventCapturable(TelemetryEventName.LLM_COMPLETION)).toBe(true)
+			expect(isEventCapturable(TelemetryEventName.MODE_SWITCH)).toBe(true)
+			expect(isEventCapturable(TelemetryEventName.TOOL_USED)).toBe(true)
+		})
+
+		it("should return false for events in exclude list", () => {
+			const client = new TelemetryClient(mockAuthService, mockSettingsService)
+
+			const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>(
+				client,
+				"isEventCapturable",
+			).bind(client)
+
+			expect(isEventCapturable(TelemetryEventName.TASK_CONVERSATION_MESSAGE)).toBe(false)
+		})
+
+		it("should return true for TASK_MESSAGE events when recordTaskMessages is true", () => {
+			mockSettingsService.getSettings.mockReturnValue({
+				cloudSettings: {
+					recordTaskMessages: true,
+				},
+			})
+
+			const client = new TelemetryClient(mockAuthService, mockSettingsService)
+
+			const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>(
+				client,
+				"isEventCapturable",
+			).bind(client)
+
+			expect(isEventCapturable(TelemetryEventName.TASK_MESSAGE)).toBe(true)
+		})
+
+		it("should return false for TASK_MESSAGE events when recordTaskMessages is false", () => {
+			mockSettingsService.getSettings.mockReturnValue({
+				cloudSettings: {
+					recordTaskMessages: false,
+				},
+			})
+
+			const client = new TelemetryClient(mockAuthService, mockSettingsService)
+
+			const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>(
+				client,
+				"isEventCapturable",
+			).bind(client)
+
+			expect(isEventCapturable(TelemetryEventName.TASK_MESSAGE)).toBe(false)
+		})
+
+		it("should return false for TASK_MESSAGE events when recordTaskMessages is undefined", () => {
+			mockSettingsService.getSettings.mockReturnValue({
+				cloudSettings: {},
+			})
+
+			const client = new TelemetryClient(mockAuthService, mockSettingsService)
+
+			const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>(
+				client,
+				"isEventCapturable",
+			).bind(client)
+
+			expect(isEventCapturable(TelemetryEventName.TASK_MESSAGE)).toBe(false)
+		})
+
+		it("should return false for TASK_MESSAGE events when cloudSettings is undefined", () => {
+			mockSettingsService.getSettings.mockReturnValue({})
+
+			const client = new TelemetryClient(mockAuthService, mockSettingsService)
+
+			const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>(
+				client,
+				"isEventCapturable",
+			).bind(client)
+
+			expect(isEventCapturable(TelemetryEventName.TASK_MESSAGE)).toBe(false)
+		})
+
+		it("should return false for TASK_MESSAGE events when getSettings returns undefined", () => {
+			mockSettingsService.getSettings.mockReturnValue(undefined)
+
+			const client = new TelemetryClient(mockAuthService, mockSettingsService)
+
+			const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>(
+				client,
+				"isEventCapturable",
+			).bind(client)
+
+			expect(isEventCapturable(TelemetryEventName.TASK_MESSAGE)).toBe(false)
+		})
+	})
+
+	describe("getEventProperties", () => {
+		it("should merge provider properties with event properties", async () => {
+			const client = new TelemetryClient(mockAuthService, mockSettingsService)
+
+			const mockProvider: TelemetryPropertiesProvider = {
+				getTelemetryProperties: vi.fn().mockResolvedValue({
+					appVersion: "1.0.0",
+					vscodeVersion: "1.60.0",
+					platform: "darwin",
+					editorName: "vscode",
+					language: "en",
+					mode: "code",
+				}),
+			}
+
+			client.setProvider(mockProvider)
+
+			const getEventProperties = getPrivateProperty<
+				(event: { event: TelemetryEventName; properties?: Record<string, any> }) => Promise<Record<string, any>>
+			>(client, "getEventProperties").bind(client)
+
+			const result = await getEventProperties({
+				event: TelemetryEventName.TASK_CREATED,
+				properties: {
+					customProp: "value",
+					mode: "override", // This should override the provider's mode.
+				},
+			})
+
+			expect(result).toEqual({
+				appVersion: "1.0.0",
+				vscodeVersion: "1.60.0",
+				platform: "darwin",
+				editorName: "vscode",
+				language: "en",
+				mode: "override", // Event property takes precedence.
+				customProp: "value",
+			})
+
+			expect(mockProvider.getTelemetryProperties).toHaveBeenCalledTimes(1)
+		})
+
+		it("should handle errors from provider gracefully", async () => {
+			const client = new TelemetryClient(mockAuthService, mockSettingsService)
+
+			const mockProvider: TelemetryPropertiesProvider = {
+				getTelemetryProperties: vi.fn().mockRejectedValue(new Error("Provider error")),
+			}
+
+			const consoleErrorSpy = vi.spyOn(console, "error")
+
+			client.setProvider(mockProvider)
+
+			const getEventProperties = getPrivateProperty<
+				(event: { event: TelemetryEventName; properties?: Record<string, any> }) => Promise<Record<string, any>>
+			>(client, "getEventProperties").bind(client)
+
+			const result = await getEventProperties({
+				event: TelemetryEventName.TASK_CREATED,
+				properties: { customProp: "value" },
+			})
+
+			expect(result).toEqual({ customProp: "value" })
+			expect(consoleErrorSpy).toHaveBeenCalledWith(
+				expect.stringContaining("Error getting telemetry properties: Provider error"),
+			)
+		})
+
+		it("should return event properties when no provider is set", async () => {
+			const client = new TelemetryClient(mockAuthService, mockSettingsService)
+
+			const getEventProperties = getPrivateProperty<
+				(event: { event: TelemetryEventName; properties?: Record<string, any> }) => Promise<Record<string, any>>
+			>(client, "getEventProperties").bind(client)
+
+			const result = await getEventProperties({
+				event: TelemetryEventName.TASK_CREATED,
+				properties: { customProp: "value" },
+			})
+
+			expect(result).toEqual({ customProp: "value" })
+		})
+	})
+
+	describe("capture", () => {
+		it("should not capture events that are not capturable", async () => {
+			const client = new TelemetryClient(mockAuthService, mockSettingsService)
+
+			await client.capture({
+				event: TelemetryEventName.TASK_CONVERSATION_MESSAGE, // In exclude list.
+				properties: { test: "value" },
+			})
+
+			expect(mockFetch).not.toHaveBeenCalled()
+		})
+
+		it("should not capture TASK_MESSAGE events when recordTaskMessages is false", async () => {
+			mockSettingsService.getSettings.mockReturnValue({
+				cloudSettings: {
+					recordTaskMessages: false,
+				},
+			})
+
+			const client = new TelemetryClient(mockAuthService, mockSettingsService)
+
+			await client.capture({
+				event: TelemetryEventName.TASK_MESSAGE,
+				properties: {
+					taskId: "test-task-id",
+					message: {
+						ts: 1,
+						type: "say",
+						say: "text",
+						text: "test message",
+					},
+				},
+			})
+
+			expect(mockFetch).not.toHaveBeenCalled()
+		})
+
+		it("should not capture TASK_MESSAGE events when recordTaskMessages is undefined", async () => {
+			mockSettingsService.getSettings.mockReturnValue({
+				cloudSettings: {},
+			})
+
+			const client = new TelemetryClient(mockAuthService, mockSettingsService)
+
+			await client.capture({
+				event: TelemetryEventName.TASK_MESSAGE,
+				properties: {
+					taskId: "test-task-id",
+					message: {
+						ts: 1,
+						type: "say",
+						say: "text",
+						text: "test message",
+					},
+				},
+			})
+
+			expect(mockFetch).not.toHaveBeenCalled()
+		})
+
+		it("should not send request when schema validation fails", async () => {
+			const client = new TelemetryClient(mockAuthService, mockSettingsService)
+
+			await client.capture({
+				event: TelemetryEventName.TASK_CREATED,
+				properties: { test: "value" },
+			})
+
+			expect(mockFetch).not.toHaveBeenCalled()
+			expect(console.error).toHaveBeenCalledWith(expect.stringContaining("Invalid telemetry event"))
+		})
+
+		it("should send request when event is capturable and validation passes", async () => {
+			const client = new TelemetryClient(mockAuthService, mockSettingsService)
+
+			const providerProperties = {
+				appName: "roo-code",
+				appVersion: "1.0.0",
+				vscodeVersion: "1.60.0",
+				platform: "darwin",
+				editorName: "vscode",
+				language: "en",
+				mode: "code",
+			}
+
+			const eventProperties = {
+				taskId: "test-task-id",
+			}
+
+			const mockValidatedData = {
+				type: TelemetryEventName.TASK_CREATED,
+				properties: {
+					...providerProperties,
+					taskId: "test-task-id",
+				},
+			}
+
+			const mockProvider: TelemetryPropertiesProvider = {
+				getTelemetryProperties: vi.fn().mockResolvedValue(providerProperties),
+			}
+
+			client.setProvider(mockProvider)
+
+			await client.capture({
+				event: TelemetryEventName.TASK_CREATED,
+				properties: eventProperties,
+			})
+
+			expect(mockFetch).toHaveBeenCalledWith(
+				"https://app.roocode.com/api/events",
+				expect.objectContaining({
+					method: "POST",
+					body: JSON.stringify(mockValidatedData),
+				}),
+			)
+		})
+
+		it("should attempt to capture TASK_MESSAGE events when recordTaskMessages is true", async () => {
+			mockSettingsService.getSettings.mockReturnValue({
+				cloudSettings: {
+					recordTaskMessages: true,
+				},
+			})
+
+			const eventProperties = {
+				appName: "roo-code",
+				appVersion: "1.0.0",
+				vscodeVersion: "1.60.0",
+				platform: "darwin",
+				editorName: "vscode",
+				language: "en",
+				mode: "code",
+				taskId: "test-task-id",
+				message: {
+					ts: 1,
+					type: "say",
+					say: "text",
+					text: "test message",
+				},
+			}
+
+			const mockValidatedData = {
+				type: TelemetryEventName.TASK_MESSAGE,
+				properties: eventProperties,
+			}
+
+			const client = new TelemetryClient(mockAuthService, mockSettingsService)
+
+			await client.capture({
+				event: TelemetryEventName.TASK_MESSAGE,
+				properties: eventProperties,
+			})
+
+			expect(mockFetch).toHaveBeenCalledWith(
+				"https://app.roocode.com/api/events",
+				expect.objectContaining({
+					method: "POST",
+					body: JSON.stringify(mockValidatedData),
+				}),
+			)
+		})
+
+		it("should handle fetch errors gracefully", async () => {
+			const client = new TelemetryClient(mockAuthService, mockSettingsService)
+
+			mockFetch.mockRejectedValue(new Error("Network error"))
+
+			await expect(
+				client.capture({
+					event: TelemetryEventName.TASK_CREATED,
+					properties: { test: "value" },
+				}),
+			).resolves.not.toThrow()
+		})
+	})
+
+	describe("telemetry state methods", () => {
+		it("should always return true for isTelemetryEnabled", () => {
+			const client = new TelemetryClient(mockAuthService, mockSettingsService)
+			expect(client.isTelemetryEnabled()).toBe(true)
+		})
+
+		it("should have empty implementations for updateTelemetryState and shutdown", async () => {
+			const client = new TelemetryClient(mockAuthService, mockSettingsService)
+			client.updateTelemetryState(true)
+			await client.shutdown()
+		})
+	})
+})

+ 1 - 0
packages/cloud/src/index.ts

@@ -0,0 +1 @@
+export * from "./CloudService"

+ 3 - 0
packages/cloud/src/types.ts

@@ -0,0 +1,3 @@
+export interface CloudServiceCallbacks {
+	stateChanged?: () => void
+}

+ 5 - 0
packages/cloud/tsconfig.json

@@ -0,0 +1,5 @@
+{
+	"extends": "@roo-code/config-typescript/vscode-library.json",
+	"include": ["src"],
+	"exclude": ["node_modules"]
+}

+ 13 - 0
packages/cloud/vitest.config.ts

@@ -0,0 +1,13 @@
+import { defineConfig } from "vitest/config"
+
+export default defineConfig({
+	test: {
+		globals: true,
+		environment: "node",
+	},
+	resolve: {
+		alias: {
+			vscode: new URL("./src/__mocks__/vscode.ts", import.meta.url).pathname,
+		},
+	},
+})

+ 12 - 0
packages/config-typescript/vscode-library.json

@@ -0,0 +1,12 @@
+{
+	"$schema": "https://json.schemastore.org/tsconfig",
+	"extends": "./base.json",
+	"compilerOptions": {
+		"types": ["vitest/globals"],
+		"outDir": "dist",
+		"module": "esnext",
+		"moduleResolution": "Bundler",
+		"noUncheckedIndexedAccess": false,
+		"useUnknownInCatchVariables": false
+	}
+}

+ 4 - 0
packages/telemetry/eslint.config.mjs

@@ -0,0 +1,4 @@
+import { config } from "@roo-code/config-eslint/base"
+
+/** @type {import("eslint").Linter.Config} */
+export default [...config]

+ 25 - 0
packages/telemetry/package.json

@@ -0,0 +1,25 @@
+{
+	"name": "@roo-code/telemetry",
+	"description": "Roo Code telemetry service and clients.",
+	"version": "0.0.0",
+	"type": "module",
+	"exports": "./src/index.ts",
+	"scripts": {
+		"lint": "eslint src --ext=ts --max-warnings=0",
+		"check-types": "tsc --noEmit",
+		"test": "vitest run",
+		"clean": "rimraf dist .turbo"
+	},
+	"dependencies": {
+		"@roo-code/types": "workspace:^",
+		"posthog-node": "^4.7.0",
+		"zod": "^3.24.2"
+	},
+	"devDependencies": {
+		"@roo-code/config-eslint": "workspace:^",
+		"@roo-code/config-typescript": "workspace:^",
+		"@types/node": "^22.15.20",
+		"@types/vscode": "^1.84.0",
+		"vitest": "^3.1.3"
+	}
+}

+ 7 - 3
src/services/telemetry/clients/BaseTelemetryClient.ts → packages/telemetry/src/BaseTelemetryClient.ts

@@ -1,6 +1,10 @@
-import { TelemetryEvent, TelemetryEventName } from "@roo-code/types"
-
-import { TelemetryClient, TelemetryPropertiesProvider, TelemetryEventSubscription } from "../types"
+import {
+	TelemetryEvent,
+	TelemetryEventName,
+	TelemetryClient,
+	TelemetryPropertiesProvider,
+	TelemetryEventSubscription,
+} from "@roo-code/types"
 
 export abstract class BaseTelemetryClient implements TelemetryClient {
 	protected providerRef: WeakRef<TelemetryPropertiesProvider> | null = null

+ 2 - 12
src/services/telemetry/clients/PostHogTelemetryClient.ts → packages/telemetry/src/PostHogTelemetryClient.ts

@@ -14,11 +14,11 @@ export class PostHogTelemetryClient extends BaseTelemetryClient {
 	private client: PostHog
 	private distinctId: string = vscode.env.machineId
 
-	private constructor(debug = false) {
+	constructor(debug = false) {
 		super(
 			{
 				type: "exclude",
-				events: [TelemetryEventName.LLM_COMPLETION],
+				events: [TelemetryEventName.TASK_MESSAGE, TelemetryEventName.LLM_COMPLETION],
 			},
 			debug,
 		)
@@ -75,14 +75,4 @@ export class PostHogTelemetryClient extends BaseTelemetryClient {
 	public override async shutdown(): Promise<void> {
 		await this.client.shutdown()
 	}
-
-	private static _instance: PostHogTelemetryClient | null = null
-
-	public static getInstance(): PostHogTelemetryClient {
-		if (!PostHogTelemetryClient._instance) {
-			PostHogTelemetryClient._instance = new PostHogTelemetryClient()
-		}
-
-		return PostHogTelemetryClient._instance
-	}
 }

+ 31 - 32
src/services/telemetry/TelemetryService.ts → packages/telemetry/src/TelemetryService.ts

@@ -1,38 +1,17 @@
-import * as vscode from "vscode"
 import { ZodError } from "zod"
 
-import { TelemetryEventName } from "@roo-code/types"
-
-import { logger } from "../../utils/logging"
-
-import { PostHogTelemetryClient } from "./clients/PostHogTelemetryClient"
-import { type TelemetryClient, type TelemetryPropertiesProvider } from "./types"
+import { type TelemetryClient, type TelemetryPropertiesProvider, TelemetryEventName } from "@roo-code/types"
 
 /**
  * TelemetryService wrapper class that defers initialization.
  * This ensures that we only create the various clients after environment
  * variables are loaded.
  */
-class TelemetryService {
-	private clients: TelemetryClient[] = []
-	private initialized = false
+export class TelemetryService {
+	constructor(private clients: TelemetryClient[]) {}
 
-	/**
-	 * Initialize the telemetry client. This should be called after environment
-	 * variables are loaded.
-	 */
-	public async initialize(context: vscode.ExtensionContext): Promise<void> {
-		if (this.initialized) {
-			return
-		}
-
-		this.initialized = true
-
-		try {
-			this.clients.push(PostHogTelemetryClient.getInstance())
-		} catch (error) {
-			console.warn("Failed to initialize telemetry service:", error)
-		}
+	public register(client: TelemetryClient): void {
+		this.clients.push(client)
 	}
 
 	/**
@@ -44,8 +23,6 @@ class TelemetryService {
 		if (this.isReady) {
 			this.clients.forEach((client) => client.setProvider(provider))
 		}
-
-		logger.debug("TelemetryService: ClineProvider reference set")
 	}
 
 	/**
@@ -54,7 +31,7 @@ class TelemetryService {
 	 * @returns Whether the service is ready to use
 	 */
 	private get isReady(): boolean {
-		return this.initialized && this.clients.length > 0
+		return this.clients.length > 0
 	}
 
 	/**
@@ -74,7 +51,8 @@ class TelemetryService {
 	 * @param eventName The event name to capture
 	 * @param properties The event properties
 	 */
-	public captureEvent(eventName: TelemetryEventName, properties?: any): void {
+	// eslint-disable-next-line @typescript-eslint/no-explicit-any
+	public captureEvent(eventName: TelemetryEventName, properties?: Record<string, any>): void {
 		if (!this.isReady) {
 			return
 		}
@@ -197,6 +175,27 @@ class TelemetryService {
 
 		this.clients.forEach((client) => client.shutdown())
 	}
-}
 
-export const telemetryService = new TelemetryService()
+	private static _instance: TelemetryService | null = null
+
+	static createInstance(clients: TelemetryClient[] = []) {
+		if (this._instance) {
+			throw new Error("TelemetryService instance already created")
+		}
+
+		this._instance = new TelemetryService(clients)
+		return this._instance
+	}
+
+	static get instance() {
+		if (!this._instance) {
+			throw new Error("TelemetryService not initialized")
+		}
+
+		return this._instance
+	}
+
+	static hasInstance(): boolean {
+		return this._instance !== null
+	}
+}

+ 42 - 48
src/services/telemetry/clients/__tests__/PostHogTelemetryClient.test.ts → packages/telemetry/src/__tests__/PostHogTelemetryClient.test.ts

@@ -1,21 +1,23 @@
-// npx jest src/services/telemetry/clients/__tests__/PostHogTelemetryClient.test.ts
+/* eslint-disable @typescript-eslint/no-explicit-any */
 
+// npx vitest run src/__tests__/PostHogTelemetryClient.test.ts
+
+import { describe, it, expect, beforeEach, vi } from "vitest"
 import * as vscode from "vscode"
 import { PostHog } from "posthog-node"
 
-import { TelemetryEventName } from "@roo-code/types"
+import { type TelemetryPropertiesProvider, TelemetryEventName } from "@roo-code/types"
 
-import { TelemetryPropertiesProvider } from "../../types"
 import { PostHogTelemetryClient } from "../PostHogTelemetryClient"
 
-jest.mock("posthog-node")
+vi.mock("posthog-node")
 
-jest.mock("vscode", () => ({
+vi.mock("vscode", () => ({
 	env: {
 		machineId: "test-machine-id",
 	},
 	workspace: {
-		getConfiguration: jest.fn(),
+		getConfiguration: vi.fn(),
 	},
 }))
 
@@ -24,37 +26,29 @@ describe("PostHogTelemetryClient", () => {
 		return instance[propertyName]
 	}
 
-	let mockPostHogClient: jest.Mocked<PostHog>
+	let mockPostHogClient: any
 
 	beforeEach(() => {
-		jest.clearAllMocks()
+		vi.clearAllMocks()
 
 		mockPostHogClient = {
-			capture: jest.fn(),
-			optIn: jest.fn(),
-			optOut: jest.fn(),
-			shutdown: jest.fn().mockResolvedValue(undefined),
-		} as unknown as jest.Mocked<PostHog>
-		;(PostHog as unknown as jest.Mock).mockImplementation(() => mockPostHogClient)
-
-		// @ts-ignore - Accessing private static property for testing
+			capture: vi.fn(),
+			optIn: vi.fn(),
+			optOut: vi.fn(),
+			shutdown: vi.fn().mockResolvedValue(undefined),
+		}
+		;(PostHog as any).mockImplementation(() => mockPostHogClient)
+
+		// @ts-expect-error - Accessing private static property for testing
 		PostHogTelemetryClient._instance = undefined
-		;(vscode.workspace.getConfiguration as jest.Mock).mockReturnValue({
-			get: jest.fn().mockReturnValue("all"),
-		})
-	})
-
-	describe("getInstance", () => {
-		it("should return the same instance when called multiple times", () => {
-			const instance1 = PostHogTelemetryClient.getInstance()
-			const instance2 = PostHogTelemetryClient.getInstance()
-			expect(instance1).toBe(instance2)
+		;(vscode.workspace.getConfiguration as any).mockReturnValue({
+			get: vi.fn().mockReturnValue("all"),
 		})
 	})
 
 	describe("isEventCapturable", () => {
 		it("should return true for events not in exclude list", () => {
-			const client = PostHogTelemetryClient.getInstance()
+			const client = new PostHogTelemetryClient()
 
 			const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>(
 				client,
@@ -66,7 +60,7 @@ describe("PostHogTelemetryClient", () => {
 		})
 
 		it("should return false for events in exclude list", () => {
-			const client = PostHogTelemetryClient.getInstance()
+			const client = new PostHogTelemetryClient()
 
 			const isEventCapturable = getPrivateProperty<(eventName: TelemetryEventName) => boolean>(
 				client,
@@ -79,10 +73,10 @@ describe("PostHogTelemetryClient", () => {
 
 	describe("getEventProperties", () => {
 		it("should merge provider properties with event properties", async () => {
-			const client = PostHogTelemetryClient.getInstance()
+			const client = new PostHogTelemetryClient()
 
 			const mockProvider: TelemetryPropertiesProvider = {
-				getTelemetryProperties: jest.fn().mockResolvedValue({
+				getTelemetryProperties: vi.fn().mockResolvedValue({
 					appVersion: "1.0.0",
 					vscodeVersion: "1.60.0",
 					platform: "darwin",
@@ -120,13 +114,13 @@ describe("PostHogTelemetryClient", () => {
 		})
 
 		it("should handle errors from provider gracefully", async () => {
-			const client = PostHogTelemetryClient.getInstance()
+			const client = new PostHogTelemetryClient()
 
 			const mockProvider: TelemetryPropertiesProvider = {
-				getTelemetryProperties: jest.fn().mockRejectedValue(new Error("Provider error")),
+				getTelemetryProperties: vi.fn().mockRejectedValue(new Error("Provider error")),
 			}
 
-			const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation()
+			const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {})
 			client.setProvider(mockProvider)
 
 			const getEventProperties = getPrivateProperty<
@@ -147,7 +141,7 @@ describe("PostHogTelemetryClient", () => {
 		})
 
 		it("should return event properties when no provider is set", async () => {
-			const client = PostHogTelemetryClient.getInstance()
+			const client = new PostHogTelemetryClient()
 
 			const getEventProperties = getPrivateProperty<
 				(event: { event: TelemetryEventName; properties?: Record<string, any> }) => Promise<Record<string, any>>
@@ -164,7 +158,7 @@ describe("PostHogTelemetryClient", () => {
 
 	describe("capture", () => {
 		it("should not capture events when telemetry is disabled", async () => {
-			const client = PostHogTelemetryClient.getInstance()
+			const client = new PostHogTelemetryClient()
 			client.updateTelemetryState(false)
 
 			await client.capture({
@@ -176,7 +170,7 @@ describe("PostHogTelemetryClient", () => {
 		})
 
 		it("should not capture events that are not capturable", async () => {
-			const client = PostHogTelemetryClient.getInstance()
+			const client = new PostHogTelemetryClient()
 			client.updateTelemetryState(true)
 
 			await client.capture({
@@ -188,11 +182,11 @@ describe("PostHogTelemetryClient", () => {
 		})
 
 		it("should capture events when telemetry is enabled and event is capturable", async () => {
-			const client = PostHogTelemetryClient.getInstance()
+			const client = new PostHogTelemetryClient()
 			client.updateTelemetryState(true)
 
 			const mockProvider: TelemetryPropertiesProvider = {
-				getTelemetryProperties: jest.fn().mockResolvedValue({
+				getTelemetryProperties: vi.fn().mockResolvedValue({
 					appVersion: "1.0.0",
 					vscodeVersion: "1.60.0",
 					platform: "darwin",
@@ -222,10 +216,10 @@ describe("PostHogTelemetryClient", () => {
 
 	describe("updateTelemetryState", () => {
 		it("should enable telemetry when user opts in and global telemetry is enabled", () => {
-			const client = PostHogTelemetryClient.getInstance()
+			const client = new PostHogTelemetryClient()
 
-			;(vscode.workspace.getConfiguration as jest.Mock).mockReturnValue({
-				get: jest.fn().mockReturnValue("all"),
+			;(vscode.workspace.getConfiguration as any).mockReturnValue({
+				get: vi.fn().mockReturnValue("all"),
 			})
 
 			client.updateTelemetryState(true)
@@ -235,10 +229,10 @@ describe("PostHogTelemetryClient", () => {
 		})
 
 		it("should disable telemetry when user opts out", () => {
-			const client = PostHogTelemetryClient.getInstance()
+			const client = new PostHogTelemetryClient()
 
-			;(vscode.workspace.getConfiguration as jest.Mock).mockReturnValue({
-				get: jest.fn().mockReturnValue("all"),
+			;(vscode.workspace.getConfiguration as any).mockReturnValue({
+				get: vi.fn().mockReturnValue("all"),
 			})
 
 			client.updateTelemetryState(false)
@@ -248,10 +242,10 @@ describe("PostHogTelemetryClient", () => {
 		})
 
 		it("should disable telemetry when global telemetry is disabled, regardless of user opt-in", () => {
-			const client = PostHogTelemetryClient.getInstance()
+			const client = new PostHogTelemetryClient()
 
-			;(vscode.workspace.getConfiguration as jest.Mock).mockReturnValue({
-				get: jest.fn().mockReturnValue("off"),
+			;(vscode.workspace.getConfiguration as any).mockReturnValue({
+				get: vi.fn().mockReturnValue("off"),
 			})
 
 			client.updateTelemetryState(true)
@@ -262,7 +256,7 @@ describe("PostHogTelemetryClient", () => {
 
 	describe("shutdown", () => {
 		it("should call shutdown on the PostHog client", async () => {
-			const client = PostHogTelemetryClient.getInstance()
+			const client = new PostHogTelemetryClient()
 			await client.shutdown()
 			expect(mockPostHogClient.shutdown).toHaveBeenCalled()
 		})

+ 3 - 0
packages/telemetry/src/index.ts

@@ -0,0 +1,3 @@
+export * from "./BaseTelemetryClient"
+export * from "./PostHogTelemetryClient"
+export * from "./TelemetryService"

+ 5 - 0
packages/telemetry/tsconfig.json

@@ -0,0 +1,5 @@
+{
+	"extends": "@roo-code/config-typescript/vscode-library.json",
+	"include": ["src"],
+	"exclude": ["node_modules"]
+}

+ 8 - 0
packages/telemetry/vitest.config.ts

@@ -0,0 +1,8 @@
+import { defineConfig } from "vitest/config"
+
+export default defineConfig({
+	test: {
+		globals: true,
+		environment: "node",
+	},
+})

+ 40 - 0
packages/types/npm/package.json

@@ -0,0 +1,40 @@
+{
+	"name": "@roo-code/types",
+	"version": "1.24.0",
+	"description": "TypeScript type definitions for Roo Code.",
+	"publishConfig": {
+		"access": "public",
+		"name": "@roo-code/types"
+	},
+	"author": "Roo Code Team",
+	"license": "MIT",
+	"repository": {
+		"type": "git",
+		"url": "git+https://github.com/RooCodeInc/Roo-Code.git"
+	},
+	"bugs": {
+		"url": "https://github.com/RooCodeInc/Roo-Code/issues"
+	},
+	"homepage": "https://github.com/RooCodeInc/Roo-Code/tree/main/packages/types",
+	"keywords": [
+		"roo",
+		"roo-code",
+		"ai"
+	],
+	"main": "./dist/index.cjs",
+	"module": "./dist/index.js",
+	"types": "./dist/index.d.ts",
+	"exports": {
+		".": {
+			"types": "./dist/index.d.ts",
+			"import": "./dist/index.js",
+			"require": {
+				"types": "./dist/index.d.cts",
+				"default": "./dist/index.cjs"
+			}
+		}
+	},
+	"files": [
+		"dist"
+	]
+}

+ 4 - 6
packages/types/package.json

@@ -1,6 +1,5 @@
 {
 	"name": "@roo-code/types",
-	"description": "Roo Code foundational types and schemas.",
 	"version": "0.0.0",
 	"type": "module",
 	"main": "./dist/index.cjs",
@@ -14,15 +13,14 @@
 			}
 		}
 	},
-	"files": [
-		"dist"
-	],
 	"scripts": {
 		"lint": "eslint src --ext=ts --max-warnings=0",
 		"check-types": "tsc --noEmit",
-		"test": "vitest --globals --run",
+		"test": "vitest run",
 		"build": "tsup",
-		"clean": "rimraf dist .turbo"
+		"npm:publish:test": "tsup --outDir npm/dist && cd npm && npm publish --dry-run",
+		"npm:publish": "tsup --outDir npm/dist && cd npm && npm publish",
+		"clean": "rimraf dist npm/dist .turbo"
 	},
 	"dependencies": {
 		"zod": "^3.24.2"

+ 1 - 1
packages/types/src/__tests__/index.test.ts

@@ -1,4 +1,4 @@
-// npx vitest run --globals src/__tests__/index.test.ts
+// npx vitest run src/__tests__/index.test.ts
 
 import { GLOBAL_STATE_KEYS } from "../index.js"
 

+ 54 - 0
packages/types/src/cloud.ts

@@ -0,0 +1,54 @@
+import { z } from "zod"
+
+export interface CloudUserInfo {
+	name?: string
+	email?: string
+	picture?: string
+}
+
+/**
+ * Organization Allow List
+ */
+
+export const organizationAllowListSchema = z.object({
+	allowAll: z.boolean(),
+	providers: z.record(
+		z.object({
+			allowAll: z.boolean(),
+			models: z.array(z.string()).optional(),
+		}),
+	),
+})
+
+export type OrganizationAllowList = z.infer<typeof organizationAllowListSchema>
+
+export const ORGANIZATION_ALLOW_ALL: OrganizationAllowList = {
+	allowAll: true,
+	providers: {},
+} as const
+
+/**
+ * Organization Settings
+ */
+
+export const organizationSettingsSchema = z.object({
+	version: z.number(),
+	defaultSettings: z
+		.object({
+			enableCheckpoints: z.boolean().optional(),
+			maxOpenTabsContext: z.number().optional(),
+			maxWorkspaceFiles: z.number().optional(),
+			showRooIgnoredFiles: z.boolean().optional(),
+			maxReadFileLine: z.number().optional(),
+			fuzzyMatchThreshold: z.number().optional(),
+		})
+		.optional(),
+	cloudSettings: z
+		.object({
+			recordTaskMessages: z.boolean().optional(),
+		})
+		.optional(),
+	allowList: organizationAllowListSchema,
+})
+
+export type OrganizationSettings = z.infer<typeof organizationSettingsSchema>

+ 2 - 2
packages/types/src/experiment.ts

@@ -6,7 +6,7 @@ import type { Keys, Equals, AssertEqual } from "./type-fu.js"
  * ExperimentId
  */
 
-export const experimentIds = ["autocomplete", "autoCondenseContext", "powerSteering"] as const //kilocode_change:autocomplete
+export const experimentIds = ["autocomplete", "powerSteering", "concurrentFileReads"] as const //kilocode_change:autocomplete
 
 export const experimentIdsSchema = z.enum(experimentIds)
 
@@ -17,8 +17,8 @@ export type ExperimentId = z.infer<typeof experimentIdsSchema>
  */
 
 export const experimentsSchema = z.object({
-	autoCondenseContext: z.boolean(),
 	powerSteering: z.boolean(),
+	concurrentFileReads: z.boolean(),
 	autocomplete: z.boolean(), // kilocode_change
 })
 

+ 4 - 0
packages/types/src/global-settings.ts

@@ -46,7 +46,9 @@ export const globalSettingsSchema = z.object({
 	alwaysAllowExecute: z.boolean().optional(),
 	allowedCommands: z.array(z.string()).optional(),
 	allowedMaxRequests: z.number().nullish(),
+	autoCondenseContext: z.boolean().optional(),
 	autoCondenseContextPercent: z.number().optional(),
+ 	maxConcurrentFileReads: z.number().optional(),
 
 	browserToolEnabled: z.boolean().optional(),
 	browserViewportSize: z.string().optional(),
@@ -134,7 +136,9 @@ export const GLOBAL_SETTINGS_KEYS = keysOf<GlobalSettings>()([
 	"alwaysAllowExecute",
 	"allowedCommands",
 	"allowedMaxRequests",
+	"autoCondenseContext",
 	"autoCondenseContextPercent",
+	"maxConcurrentFileReads",
 
 	"browserToolEnabled",
 	"browserViewportSize",

+ 3 - 0
packages/types/src/index.ts

@@ -1,5 +1,8 @@
+export * from "./providers/index.js"
+
 export * from "./api.js"
 export * from "./codebase-index.js"
+export * from "./cloud.js"
 export * from "./experiment.js"
 export * from "./global-settings.js"
 export * from "./history.js"

+ 1 - 0
packages/types/src/message.ts

@@ -53,6 +53,7 @@ export const clineSays = [
 	"rooignore_error",
 	"diff_error",
 	"condense_context",
+	"condense_context_error",
 	"codebase_search_result",
 ] as const
 

+ 4 - 0
packages/types/src/provider-settings.ts

@@ -109,6 +109,8 @@ const bedrockSchema = apiModelIdProviderModelSchema.extend({
 	awsProfile: z.string().optional(),
 	awsUseProfile: z.boolean().optional(),
 	awsCustomArn: z.string().optional(),
+	awsBedrockEndpointEnabled: z.boolean().optional(),
+	awsBedrockEndpoint: z.string().optional(),
 })
 
 const vertexSchema = apiModelIdProviderModelSchema.extend({
@@ -308,6 +310,8 @@ export const PROVIDER_SETTINGS_KEYS = keysOf<ProviderSettings>()([
 	"awsProfile",
 	"awsUseProfile",
 	"awsCustomArn",
+	"awsBedrockEndpointEnabled",
+	"awsBedrockEndpoint",
 	// Google Vertex
 	"vertexKeyFile",
 	"vertexJsonCredentials",

+ 100 - 0
packages/types/src/providers/anthropic.ts

@@ -0,0 +1,100 @@
+import type { ModelInfo } from "../model.js"
+
+// https://docs.anthropic.com/en/docs/about-claude/models
+
+export type AnthropicModelId = keyof typeof anthropicModels
+export const anthropicDefaultModelId: AnthropicModelId = "claude-sonnet-4-20250514"
+
+export const anthropicModels = {
+	"claude-sonnet-4-20250514": {
+		maxTokens: 64_000, // Overridden to 8k if `enableReasoningEffort` is false.
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsComputerUse: true,
+		supportsPromptCache: true,
+		inputPrice: 3.0, // $3 per million input tokens
+		outputPrice: 15.0, // $15 per million output tokens
+		cacheWritesPrice: 3.75, // $3.75 per million tokens
+		cacheReadsPrice: 0.3, // $0.30 per million tokens
+		supportsReasoningBudget: true,
+	},
+	"claude-opus-4-20250514": {
+		maxTokens: 32_000, // Overridden to 8k if `enableReasoningEffort` is false.
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsComputerUse: true,
+		supportsPromptCache: true,
+		inputPrice: 15.0, // $15 per million input tokens
+		outputPrice: 75.0, // $75 per million output tokens
+		cacheWritesPrice: 18.75, // $18.75 per million tokens
+		cacheReadsPrice: 1.5, // $1.50 per million tokens
+		supportsReasoningBudget: true,
+	},
+	"claude-3-7-sonnet-20250219:thinking": {
+		maxTokens: 128_000, // Unlocked by passing `beta` flag to the model. Otherwise, it's 64k.
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsComputerUse: true,
+		supportsPromptCache: true,
+		inputPrice: 3.0, // $3 per million input tokens
+		outputPrice: 15.0, // $15 per million output tokens
+		cacheWritesPrice: 3.75, // $3.75 per million tokens
+		cacheReadsPrice: 0.3, // $0.30 per million tokens
+		supportsReasoningBudget: true,
+		requiredReasoningBudget: true,
+	},
+	"claude-3-7-sonnet-20250219": {
+		maxTokens: 8192, // Since we already have a `:thinking` virtual model we aren't setting `supportsReasoningBudget: true` here.
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsComputerUse: true,
+		supportsPromptCache: true,
+		inputPrice: 3.0, // $3 per million input tokens
+		outputPrice: 15.0, // $15 per million output tokens
+		cacheWritesPrice: 3.75, // $3.75 per million tokens
+		cacheReadsPrice: 0.3, // $0.30 per million tokens
+	},
+	"claude-3-5-sonnet-20241022": {
+		maxTokens: 8192,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsComputerUse: true,
+		supportsPromptCache: true,
+		inputPrice: 3.0, // $3 per million input tokens
+		outputPrice: 15.0, // $15 per million output tokens
+		cacheWritesPrice: 3.75, // $3.75 per million tokens
+		cacheReadsPrice: 0.3, // $0.30 per million tokens
+	},
+	"claude-3-5-haiku-20241022": {
+		maxTokens: 8192,
+		contextWindow: 200_000,
+		supportsImages: false,
+		supportsPromptCache: true,
+		inputPrice: 1.0,
+		outputPrice: 5.0,
+		cacheWritesPrice: 1.25,
+		cacheReadsPrice: 0.1,
+	},
+	"claude-3-opus-20240229": {
+		maxTokens: 4096,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 15.0,
+		outputPrice: 75.0,
+		cacheWritesPrice: 18.75,
+		cacheReadsPrice: 1.5,
+	},
+	"claude-3-haiku-20240307": {
+		maxTokens: 4096,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 0.25,
+		outputPrice: 1.25,
+		cacheWritesPrice: 0.3,
+		cacheReadsPrice: 0.03,
+	},
+} as const satisfies Record<string, ModelInfo>
+
+export const ANTHROPIC_DEFAULT_MAX_TOKENS = 8192

+ 432 - 0
packages/types/src/providers/bedrock.ts

@@ -0,0 +1,432 @@
+import type { ModelInfo } from "../model.js"
+
+// https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference.html
+
+export type BedrockModelId = keyof typeof bedrockModels
+
+export const bedrockDefaultModelId: BedrockModelId = "anthropic.claude-sonnet-4-20250514-v1:0"
+
+export const bedrockDefaultPromptRouterModelId: BedrockModelId = "anthropic.claude-3-sonnet-20240229-v1:0"
+
+// March, 12 2025 - updated prices to match US-West-2 list price shown at
+// https://aws.amazon.com/bedrock/pricing, including older models that are part
+// of the default prompt routers AWS enabled for GA of the promot router
+// feature.
+export const bedrockModels = {
+	"amazon.nova-pro-v1:0": {
+		maxTokens: 5000,
+		contextWindow: 300_000,
+		supportsImages: true,
+		supportsComputerUse: false,
+		supportsPromptCache: true,
+		inputPrice: 0.8,
+		outputPrice: 3.2,
+		cacheWritesPrice: 0.8, // per million tokens
+		cacheReadsPrice: 0.2, // per million tokens
+		minTokensPerCachePoint: 1,
+		maxCachePoints: 1,
+		cachableFields: ["system"],
+	},
+	"amazon.nova-pro-latency-optimized-v1:0": {
+		maxTokens: 5000,
+		contextWindow: 300_000,
+		supportsImages: true,
+		supportsComputerUse: false,
+		supportsPromptCache: false,
+		inputPrice: 1.0,
+		outputPrice: 4.0,
+		cacheWritesPrice: 1.0, // per million tokens
+		cacheReadsPrice: 0.25, // per million tokens
+		description: "Amazon Nova Pro with latency optimized inference",
+	},
+	"amazon.nova-lite-v1:0": {
+		maxTokens: 5000,
+		contextWindow: 300_000,
+		supportsImages: true,
+		supportsComputerUse: false,
+		supportsPromptCache: true,
+		inputPrice: 0.06,
+		outputPrice: 0.24,
+		cacheWritesPrice: 0.06, // per million tokens
+		cacheReadsPrice: 0.015, // per million tokens
+		minTokensPerCachePoint: 1,
+		maxCachePoints: 1,
+		cachableFields: ["system"],
+	},
+	"amazon.nova-micro-v1:0": {
+		maxTokens: 5000,
+		contextWindow: 128_000,
+		supportsImages: false,
+		supportsComputerUse: false,
+		supportsPromptCache: true,
+		inputPrice: 0.035,
+		outputPrice: 0.14,
+		cacheWritesPrice: 0.035, // per million tokens
+		cacheReadsPrice: 0.00875, // per million tokens
+		minTokensPerCachePoint: 1,
+		maxCachePoints: 1,
+		cachableFields: ["system"],
+	},
+	"anthropic.claude-sonnet-4-20250514-v1:0": {
+		maxTokens: 8192,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsComputerUse: true,
+		supportsPromptCache: true,
+		inputPrice: 3.0,
+		outputPrice: 15.0,
+		cacheWritesPrice: 3.75,
+		cacheReadsPrice: 0.3,
+		minTokensPerCachePoint: 1024,
+		maxCachePoints: 4,
+		cachableFields: ["system", "messages", "tools"],
+	},
+	"anthropic.claude-opus-4-20250514-v1:0": {
+		maxTokens: 8192,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsComputerUse: true,
+		supportsPromptCache: true,
+		inputPrice: 15.0,
+		outputPrice: 75.0,
+		cacheWritesPrice: 18.75,
+		cacheReadsPrice: 1.5,
+		minTokensPerCachePoint: 1024,
+		maxCachePoints: 4,
+		cachableFields: ["system", "messages", "tools"],
+	},
+	"anthropic.claude-3-7-sonnet-20250219-v1:0": {
+		maxTokens: 8192,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsComputerUse: true,
+		supportsPromptCache: true,
+		inputPrice: 3.0,
+		outputPrice: 15.0,
+		cacheWritesPrice: 3.75,
+		cacheReadsPrice: 0.3,
+		minTokensPerCachePoint: 1024,
+		maxCachePoints: 4,
+		cachableFields: ["system", "messages", "tools"],
+	},
+	"anthropic.claude-3-5-sonnet-20241022-v2:0": {
+		maxTokens: 8192,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsComputerUse: true,
+		supportsPromptCache: true,
+		inputPrice: 3.0,
+		outputPrice: 15.0,
+		cacheWritesPrice: 3.75,
+		cacheReadsPrice: 0.3,
+		minTokensPerCachePoint: 1024,
+		maxCachePoints: 4,
+		cachableFields: ["system", "messages", "tools"],
+	},
+	"anthropic.claude-3-5-haiku-20241022-v1:0": {
+		maxTokens: 8192,
+		contextWindow: 200_000,
+		supportsImages: false,
+		supportsPromptCache: true,
+		inputPrice: 0.8,
+		outputPrice: 4.0,
+		cacheWritesPrice: 1.0,
+		cacheReadsPrice: 0.08,
+		minTokensPerCachePoint: 2048,
+		maxCachePoints: 4,
+		cachableFields: ["system", "messages", "tools"],
+	},
+	"anthropic.claude-3-5-sonnet-20240620-v1:0": {
+		maxTokens: 8192,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 3.0,
+		outputPrice: 15.0,
+	},
+	"anthropic.claude-3-opus-20240229-v1:0": {
+		maxTokens: 4096,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 15.0,
+		outputPrice: 75.0,
+	},
+	"anthropic.claude-3-sonnet-20240229-v1:0": {
+		maxTokens: 4096,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 3.0,
+		outputPrice: 15.0,
+	},
+	"anthropic.claude-3-haiku-20240307-v1:0": {
+		maxTokens: 4096,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0.25,
+		outputPrice: 1.25,
+	},
+	"anthropic.claude-2-1-v1:0": {
+		maxTokens: 4096,
+		contextWindow: 100_000,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 8.0,
+		outputPrice: 24.0,
+		description: "Claude 2.1",
+	},
+	"anthropic.claude-2-0-v1:0": {
+		maxTokens: 4096,
+		contextWindow: 100_000,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 8.0,
+		outputPrice: 24.0,
+		description: "Claude 2.0",
+	},
+	"anthropic.claude-instant-v1:0": {
+		maxTokens: 4096,
+		contextWindow: 100_000,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0.8,
+		outputPrice: 2.4,
+		description: "Claude Instant",
+	},
+	"deepseek.r1-v1:0": {
+		maxTokens: 32_768,
+		contextWindow: 128_000,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 1.35,
+		outputPrice: 5.4,
+	},
+	"meta.llama3-3-70b-instruct-v1:0": {
+		maxTokens: 8192,
+		contextWindow: 128_000,
+		supportsImages: false,
+		supportsComputerUse: false,
+		supportsPromptCache: false,
+		inputPrice: 0.72,
+		outputPrice: 0.72,
+		description: "Llama 3.3 Instruct (70B)",
+	},
+	"meta.llama3-2-90b-instruct-v1:0": {
+		maxTokens: 8192,
+		contextWindow: 128_000,
+		supportsImages: true,
+		supportsComputerUse: false,
+		supportsPromptCache: false,
+		inputPrice: 0.72,
+		outputPrice: 0.72,
+		description: "Llama 3.2 Instruct (90B)",
+	},
+	"meta.llama3-2-11b-instruct-v1:0": {
+		maxTokens: 8192,
+		contextWindow: 128_000,
+		supportsImages: true,
+		supportsComputerUse: false,
+		supportsPromptCache: false,
+		inputPrice: 0.16,
+		outputPrice: 0.16,
+		description: "Llama 3.2 Instruct (11B)",
+	},
+	"meta.llama3-2-3b-instruct-v1:0": {
+		maxTokens: 8192,
+		contextWindow: 128_000,
+		supportsImages: false,
+		supportsComputerUse: false,
+		supportsPromptCache: false,
+		inputPrice: 0.15,
+		outputPrice: 0.15,
+		description: "Llama 3.2 Instruct (3B)",
+	},
+	"meta.llama3-2-1b-instruct-v1:0": {
+		maxTokens: 8192,
+		contextWindow: 128_000,
+		supportsImages: false,
+		supportsComputerUse: false,
+		supportsPromptCache: false,
+		inputPrice: 0.1,
+		outputPrice: 0.1,
+		description: "Llama 3.2 Instruct (1B)",
+	},
+	"meta.llama3-1-405b-instruct-v1:0": {
+		maxTokens: 8192,
+		contextWindow: 128_000,
+		supportsImages: false,
+		supportsComputerUse: false,
+		supportsPromptCache: false,
+		inputPrice: 2.4,
+		outputPrice: 2.4,
+		description: "Llama 3.1 Instruct (405B)",
+	},
+	"meta.llama3-1-70b-instruct-v1:0": {
+		maxTokens: 8192,
+		contextWindow: 128_000,
+		supportsImages: false,
+		supportsComputerUse: false,
+		supportsPromptCache: false,
+		inputPrice: 0.72,
+		outputPrice: 0.72,
+		description: "Llama 3.1 Instruct (70B)",
+	},
+	"meta.llama3-1-70b-instruct-latency-optimized-v1:0": {
+		maxTokens: 8192,
+		contextWindow: 128_000,
+		supportsImages: false,
+		supportsComputerUse: false,
+		supportsPromptCache: false,
+		inputPrice: 0.9,
+		outputPrice: 0.9,
+		description: "Llama 3.1 Instruct (70B) (w/ latency optimized inference)",
+	},
+	"meta.llama3-1-8b-instruct-v1:0": {
+		maxTokens: 8192,
+		contextWindow: 8_000,
+		supportsImages: false,
+		supportsComputerUse: false,
+		supportsPromptCache: false,
+		inputPrice: 0.22,
+		outputPrice: 0.22,
+		description: "Llama 3.1 Instruct (8B)",
+	},
+	"meta.llama3-70b-instruct-v1:0": {
+		maxTokens: 2048,
+		contextWindow: 8_000,
+		supportsImages: false,
+		supportsComputerUse: false,
+		supportsPromptCache: false,
+		inputPrice: 2.65,
+		outputPrice: 3.5,
+	},
+	"meta.llama3-8b-instruct-v1:0": {
+		maxTokens: 2048,
+		contextWindow: 4_000,
+		supportsImages: false,
+		supportsComputerUse: false,
+		supportsPromptCache: false,
+		inputPrice: 0.3,
+		outputPrice: 0.6,
+	},
+	"amazon.titan-text-lite-v1:0": {
+		maxTokens: 4096,
+		contextWindow: 8_000,
+		supportsImages: false,
+		supportsComputerUse: false,
+		supportsPromptCache: false,
+		inputPrice: 0.15,
+		outputPrice: 0.2,
+		description: "Amazon Titan Text Lite",
+	},
+	"amazon.titan-text-express-v1:0": {
+		maxTokens: 4096,
+		contextWindow: 8_000,
+		supportsImages: false,
+		supportsComputerUse: false,
+		supportsPromptCache: false,
+		inputPrice: 0.2,
+		outputPrice: 0.6,
+		description: "Amazon Titan Text Express",
+	},
+	"amazon.titan-text-embeddings-v1:0": {
+		maxTokens: 8192,
+		contextWindow: 8_000,
+		supportsImages: false,
+		supportsComputerUse: false,
+		supportsPromptCache: false,
+		inputPrice: 0.1,
+		description: "Amazon Titan Text Embeddings",
+	},
+	"amazon.titan-text-embeddings-v2:0": {
+		maxTokens: 8192,
+		contextWindow: 8_000,
+		supportsImages: false,
+		supportsComputerUse: false,
+		supportsPromptCache: false,
+		inputPrice: 0.02,
+		description: "Amazon Titan Text Embeddings V2",
+	},
+} as const satisfies Record<string, ModelInfo>
+
+export const BEDROCK_DEFAULT_TEMPERATURE = 0.3
+
+export const BEDROCK_MAX_TOKENS = 4096
+
+export const BEDROCK_REGION_INFO: Record<
+	string,
+	{
+		regionId: string
+		description: string
+		pattern?: string
+		multiRegion?: boolean
+	}
+> = {
+	/*
+	 * This JSON generated by AWS's AI assistant - Amazon Q on March 29, 2025
+	 *
+	 *  - Africa (Cape Town) region does not appear to support Amazon Bedrock at this time.
+	 *  - Some Asia Pacific regions, such as Asia Pacific (Hong Kong) and Asia Pacific (Jakarta), are not listed among the supported regions for Bedrock services.
+	 *  - Middle East regions, including Middle East (Bahrain) and Middle East (UAE), are not mentioned in the list of supported regions for Bedrock. [3]
+	 *  - China regions (Beijing and Ningxia) are not listed as supported for Amazon Bedrock.
+	 *  - Some newer or specialized AWS regions may not have Bedrock support yet.
+	 */
+	"us.": { regionId: "us-east-1", description: "US East (N. Virginia)", pattern: "us-", multiRegion: true },
+	"use.": { regionId: "us-east-1", description: "US East (N. Virginia)" },
+	"use1.": { regionId: "us-east-1", description: "US East (N. Virginia)" },
+	"use2.": { regionId: "us-east-2", description: "US East (Ohio)" },
+	"usw.": { regionId: "us-west-2", description: "US West (Oregon)" },
+	"usw2.": { regionId: "us-west-2", description: "US West (Oregon)" },
+	"ug.": {
+		regionId: "us-gov-west-1",
+		description: "AWS GovCloud (US-West)",
+		pattern: "us-gov-",
+		multiRegion: true,
+	},
+	"uge1.": { regionId: "us-gov-east-1", description: "AWS GovCloud (US-East)" },
+	"ugw1.": { regionId: "us-gov-west-1", description: "AWS GovCloud (US-West)" },
+	"eu.": { regionId: "eu-west-1", description: "Europe (Ireland)", pattern: "eu-", multiRegion: true },
+	"euw1.": { regionId: "eu-west-1", description: "Europe (Ireland)" },
+	"euw2.": { regionId: "eu-west-2", description: "Europe (London)" },
+	"euw3.": { regionId: "eu-west-3", description: "Europe (Paris)" },
+	"euc1.": { regionId: "eu-central-1", description: "Europe (Frankfurt)" },
+	"euc2.": { regionId: "eu-central-2", description: "Europe (Zurich)" },
+	"eun1.": { regionId: "eu-north-1", description: "Europe (Stockholm)" },
+	"eus1.": { regionId: "eu-south-1", description: "Europe (Milan)" },
+	"eus2.": { regionId: "eu-south-2", description: "Europe (Spain)" },
+	"ap.": {
+		regionId: "ap-southeast-1",
+		description: "Asia Pacific (Singapore)",
+		pattern: "ap-",
+		multiRegion: true,
+	},
+	"ape1.": { regionId: "ap-east-1", description: "Asia Pacific (Hong Kong)" },
+	"apne1.": { regionId: "ap-northeast-1", description: "Asia Pacific (Tokyo)" },
+	"apne2.": { regionId: "ap-northeast-2", description: "Asia Pacific (Seoul)" },
+	"apne3.": { regionId: "ap-northeast-3", description: "Asia Pacific (Osaka)" },
+	"aps1.": { regionId: "ap-south-1", description: "Asia Pacific (Mumbai)" },
+	"aps2.": { regionId: "ap-south-2", description: "Asia Pacific (Hyderabad)" },
+	"apse1.": { regionId: "ap-southeast-1", description: "Asia Pacific (Singapore)" },
+	"apse2.": { regionId: "ap-southeast-2", description: "Asia Pacific (Sydney)" },
+	"ca.": { regionId: "ca-central-1", description: "Canada (Central)", pattern: "ca-", multiRegion: true },
+	"cac1.": { regionId: "ca-central-1", description: "Canada (Central)" },
+	"sa.": { regionId: "sa-east-1", description: "South America (São Paulo)", pattern: "sa-", multiRegion: true },
+	"sae1.": { regionId: "sa-east-1", description: "South America (São Paulo)" },
+
+	// These are not official - they weren't generated by Amazon Q nor were
+	// found in the AWS documentation but another Roo contributor found apac.
+	// Was needed so I've added the pattern of the other geo zones.
+	"apac.": { regionId: "ap-southeast-1", description: "Default APAC region", pattern: "ap-", multiRegion: true },
+	"emea.": { regionId: "eu-west-1", description: "Default EMEA region", pattern: "eu-", multiRegion: true },
+	"amer.": { regionId: "us-east-1", description: "Default Americas region", pattern: "us-", multiRegion: true },
+}
+
+export const BEDROCK_REGIONS = Object.values(BEDROCK_REGION_INFO)
+	// Extract all region IDs
+	.map((info) => ({ value: info.regionId, label: info.regionId }))
+	// Filter to unique region IDs (remove duplicates)
+	.filter((region, index, self) => index === self.findIndex((r) => r.value === region.value))
+	// Sort alphabetically by region ID
+	.sort((a, b) => a.value.localeCompare(b.value))

+ 229 - 0
packages/types/src/providers/chutes.ts

@@ -0,0 +1,229 @@
+import type { ModelInfo } from "../model.js"
+
+// https://llm.chutes.ai/v1 (OpenAI compatible)
+export type ChutesModelId =
+	| "deepseek-ai/DeepSeek-R1-0528"
+	| "deepseek-ai/DeepSeek-R1"
+	| "deepseek-ai/DeepSeek-V3"
+	| "unsloth/Llama-3.3-70B-Instruct"
+	| "chutesai/Llama-4-Scout-17B-16E-Instruct"
+	| "unsloth/Mistral-Nemo-Instruct-2407"
+	| "unsloth/gemma-3-12b-it"
+	| "NousResearch/DeepHermes-3-Llama-3-8B-Preview"
+	| "unsloth/gemma-3-4b-it"
+	| "nvidia/Llama-3_3-Nemotron-Super-49B-v1"
+	| "nvidia/Llama-3_1-Nemotron-Ultra-253B-v1"
+	| "chutesai/Llama-4-Maverick-17B-128E-Instruct-FP8"
+	| "deepseek-ai/DeepSeek-V3-Base"
+	| "deepseek-ai/DeepSeek-R1-Zero"
+	| "deepseek-ai/DeepSeek-V3-0324"
+	| "Qwen/Qwen3-235B-A22B"
+	| "Qwen/Qwen3-32B"
+	| "Qwen/Qwen3-30B-A3B"
+	| "Qwen/Qwen3-14B"
+	| "Qwen/Qwen3-8B"
+	| "microsoft/MAI-DS-R1-FP8"
+	| "tngtech/DeepSeek-R1T-Chimera"
+
+export const chutesDefaultModelId: ChutesModelId = "deepseek-ai/DeepSeek-R1-0528"
+
+export const chutesModels = {
+	"deepseek-ai/DeepSeek-R1-0528": {
+		maxTokens: 32768,
+		contextWindow: 163840,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "DeepSeek R1 0528 model.",
+	},
+	"deepseek-ai/DeepSeek-R1": {
+		maxTokens: 32768,
+		contextWindow: 163840,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "DeepSeek R1 model.",
+	},
+	"deepseek-ai/DeepSeek-V3": {
+		maxTokens: 32768,
+		contextWindow: 163840,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "DeepSeek V3 model.",
+	},
+	"unsloth/Llama-3.3-70B-Instruct": {
+		maxTokens: 32768, // From Groq
+		contextWindow: 131072, // From Groq
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "Unsloth Llama 3.3 70B Instruct model.",
+	},
+	"chutesai/Llama-4-Scout-17B-16E-Instruct": {
+		maxTokens: 32768,
+		contextWindow: 512000,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "ChutesAI Llama 4 Scout 17B Instruct model, 512K context.",
+	},
+	"unsloth/Mistral-Nemo-Instruct-2407": {
+		maxTokens: 32768,
+		contextWindow: 128000,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "Unsloth Mistral Nemo Instruct model.",
+	},
+	"unsloth/gemma-3-12b-it": {
+		maxTokens: 32768,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "Unsloth Gemma 3 12B IT model.",
+	},
+	"NousResearch/DeepHermes-3-Llama-3-8B-Preview": {
+		maxTokens: 32768,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "Nous DeepHermes 3 Llama 3 8B Preview model.",
+	},
+	"unsloth/gemma-3-4b-it": {
+		maxTokens: 32768,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "Unsloth Gemma 3 4B IT model.",
+	},
+	"nvidia/Llama-3_3-Nemotron-Super-49B-v1": {
+		maxTokens: 32768,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "Nvidia Llama 3.3 Nemotron Super 49B model.",
+	},
+	"nvidia/Llama-3_1-Nemotron-Ultra-253B-v1": {
+		maxTokens: 32768,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "Nvidia Llama 3.1 Nemotron Ultra 253B model.",
+	},
+	"chutesai/Llama-4-Maverick-17B-128E-Instruct-FP8": {
+		maxTokens: 32768,
+		contextWindow: 256000,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "ChutesAI Llama 4 Maverick 17B Instruct FP8 model.",
+	},
+	"deepseek-ai/DeepSeek-V3-Base": {
+		maxTokens: 32768,
+		contextWindow: 163840,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "DeepSeek V3 Base model.",
+	},
+	"deepseek-ai/DeepSeek-R1-Zero": {
+		maxTokens: 32768,
+		contextWindow: 163840,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "DeepSeek R1 Zero model.",
+	},
+	"deepseek-ai/DeepSeek-V3-0324": {
+		maxTokens: 32768,
+		contextWindow: 163840,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "DeepSeek V3 (0324) model.",
+	},
+	"Qwen/Qwen3-235B-A22B": {
+		maxTokens: 32768,
+		contextWindow: 40960,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "Qwen3 235B A22B model.",
+	},
+	"Qwen/Qwen3-32B": {
+		maxTokens: 32768,
+		contextWindow: 40960,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "Qwen3 32B model.",
+	},
+	"Qwen/Qwen3-30B-A3B": {
+		maxTokens: 32768,
+		contextWindow: 40960,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "Qwen3 30B A3B model.",
+	},
+	"Qwen/Qwen3-14B": {
+		maxTokens: 32768,
+		contextWindow: 40960,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "Qwen3 14B model.",
+	},
+	"Qwen/Qwen3-8B": {
+		maxTokens: 32768,
+		contextWindow: 40960,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "Qwen3 8B model.",
+	},
+	"microsoft/MAI-DS-R1-FP8": {
+		maxTokens: 32768,
+		contextWindow: 163840,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "Microsoft MAI-DS-R1 FP8 model.",
+	},
+	"tngtech/DeepSeek-R1T-Chimera": {
+		maxTokens: 32768,
+		contextWindow: 163840,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "TNGTech DeepSeek R1T Chimera model.",
+	},
+} as const satisfies Record<string, ModelInfo>

+ 33 - 0
packages/types/src/providers/deepseek.ts

@@ -0,0 +1,33 @@
+import type { ModelInfo } from "../model.js"
+
+// https://platform.deepseek.com/docs/api
+export type DeepSeekModelId = keyof typeof deepSeekModels
+
+export const deepSeekDefaultModelId: DeepSeekModelId = "deepseek-chat"
+
+export const deepSeekModels = {
+	"deepseek-chat": {
+		maxTokens: 8192,
+		contextWindow: 64_000,
+		supportsImages: false,
+		supportsPromptCache: true,
+		inputPrice: 0.27, // $0.27 per million tokens (cache miss)
+		outputPrice: 1.1, // $1.10 per million tokens
+		cacheWritesPrice: 0.27, // $0.27 per million tokens (cache miss)
+		cacheReadsPrice: 0.07, // $0.07 per million tokens (cache hit).
+		description: `DeepSeek-V3 achieves a significant breakthrough in inference speed over previous models. It tops the leaderboard among open-source models and rivals the most advanced closed-source models globally.`,
+	},
+	"deepseek-reasoner": {
+		maxTokens: 8192,
+		contextWindow: 64_000,
+		supportsImages: false,
+		supportsPromptCache: true,
+		inputPrice: 0.55, // $0.55 per million tokens (cache miss)
+		outputPrice: 2.19, // $2.19 per million tokens
+		cacheWritesPrice: 0.55, // $0.55 per million tokens (cache miss)
+		cacheReadsPrice: 0.14, // $0.14 per million tokens (cache hit)
+		description: `DeepSeek-R1 achieves performance comparable to OpenAI-o1 across math, code, and reasoning tasks. Supports Chain of Thought reasoning with up to 32K tokens.`,
+	},
+} as const satisfies Record<string, ModelInfo>
+
+export const DEEP_SEEK_DEFAULT_TEMPERATURE = 0.6

+ 221 - 0
packages/types/src/providers/gemini.ts

@@ -0,0 +1,221 @@
+import type { ModelInfo } from "../model.js"
+
+// https://ai.google.dev/gemini-api/docs/models/gemini
+export type GeminiModelId = keyof typeof geminiModels
+
+export const geminiDefaultModelId: GeminiModelId = "gemini-2.0-flash-001"
+
+export const geminiModels = {
+	"gemini-2.5-flash-preview-04-17:thinking": {
+		maxTokens: 65_535,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0.15,
+		outputPrice: 3.5,
+		maxThinkingTokens: 24_576,
+		supportsReasoningBudget: true,
+		requiredReasoningBudget: true,
+	},
+	"gemini-2.5-flash-preview-04-17": {
+		maxTokens: 65_535,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0.15,
+		outputPrice: 0.6,
+	},
+	"gemini-2.5-flash-preview-05-20:thinking": {
+		maxTokens: 65_535,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 0.15,
+		outputPrice: 3.5,
+		cacheReadsPrice: 0.0375,
+		cacheWritesPrice: 1.0,
+		maxThinkingTokens: 24_576,
+		supportsReasoningBudget: true,
+		requiredReasoningBudget: true,
+	},
+	"gemini-2.5-flash-preview-05-20": {
+		maxTokens: 65_535,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 0.15,
+		outputPrice: 0.6,
+		cacheReadsPrice: 0.0375,
+		cacheWritesPrice: 1.0,
+	},
+	"gemini-2.5-pro-exp-03-25": {
+		maxTokens: 65_535,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-2.5-pro-preview-03-25": {
+		maxTokens: 65_535,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 2.5, // This is the pricing for prompts above 200k tokens.
+		outputPrice: 15,
+		cacheReadsPrice: 0.625,
+		cacheWritesPrice: 4.5,
+		tiers: [
+			{
+				contextWindow: 200_000,
+				inputPrice: 1.25,
+				outputPrice: 10,
+				cacheReadsPrice: 0.31,
+			},
+			{
+				contextWindow: Infinity,
+				inputPrice: 2.5,
+				outputPrice: 15,
+				cacheReadsPrice: 0.625,
+			},
+		],
+	},
+	"gemini-2.5-pro-preview-05-06": {
+		maxTokens: 65_535,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 2.5, // This is the pricing for prompts above 200k tokens.
+		outputPrice: 15,
+		cacheReadsPrice: 0.625,
+		cacheWritesPrice: 4.5,
+		tiers: [
+			{
+				contextWindow: 200_000,
+				inputPrice: 1.25,
+				outputPrice: 10,
+				cacheReadsPrice: 0.31,
+			},
+			{
+				contextWindow: Infinity,
+				inputPrice: 2.5,
+				outputPrice: 15,
+				cacheReadsPrice: 0.625,
+			},
+		],
+	},
+	"gemini-2.0-flash-001": {
+		maxTokens: 8192,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 0.1,
+		outputPrice: 0.4,
+		cacheReadsPrice: 0.025,
+		cacheWritesPrice: 1.0,
+	},
+	"gemini-2.0-flash-lite-preview-02-05": {
+		maxTokens: 8192,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-2.0-pro-exp-02-05": {
+		maxTokens: 8192,
+		contextWindow: 2_097_152,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-2.0-flash-thinking-exp-01-21": {
+		maxTokens: 65_536,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-2.0-flash-thinking-exp-1219": {
+		maxTokens: 8192,
+		contextWindow: 32_767,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-2.0-flash-exp": {
+		maxTokens: 8192,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-1.5-flash-002": {
+		maxTokens: 8192,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 0.15, // This is the pricing for prompts above 128k tokens.
+		outputPrice: 0.6,
+		cacheReadsPrice: 0.0375,
+		cacheWritesPrice: 1.0,
+		tiers: [
+			{
+				contextWindow: 128_000,
+				inputPrice: 0.075,
+				outputPrice: 0.3,
+				cacheReadsPrice: 0.01875,
+			},
+			{
+				contextWindow: Infinity,
+				inputPrice: 0.15,
+				outputPrice: 0.6,
+				cacheReadsPrice: 0.0375,
+			},
+		],
+	},
+	"gemini-1.5-flash-exp-0827": {
+		maxTokens: 8192,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-1.5-flash-8b-exp-0827": {
+		maxTokens: 8192,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-1.5-pro-002": {
+		maxTokens: 8192,
+		contextWindow: 2_097_152,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-1.5-pro-exp-0827": {
+		maxTokens: 8192,
+		contextWindow: 2_097_152,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-exp-1206": {
+		maxTokens: 8192,
+		contextWindow: 2_097_152,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+} as const satisfies Record<string, ModelInfo>

+ 20 - 0
packages/types/src/providers/glama.ts

@@ -0,0 +1,20 @@
+import type { ModelInfo } from "../model.js"
+
+// https://glama.ai/models
+export const glamaDefaultModelId = "anthropic/claude-3-7-sonnet"
+
+export const glamaDefaultModelInfo: ModelInfo = {
+	maxTokens: 8192,
+	contextWindow: 200_000,
+	supportsImages: true,
+	supportsComputerUse: true,
+	supportsPromptCache: true,
+	inputPrice: 3.0,
+	outputPrice: 15.0,
+	cacheWritesPrice: 3.75,
+	cacheReadsPrice: 0.3,
+	description:
+		"Claude 3.7 Sonnet is an advanced large language model with improved reasoning, coding, and problem-solving capabilities. It introduces a hybrid reasoning approach, allowing users to choose between rapid responses and extended, step-by-step processing for complex tasks. The model demonstrates notable improvements in coding, particularly in front-end development and full-stack updates, and excels in agentic workflows, where it can autonomously navigate multi-step processes. Claude 3.7 Sonnet maintains performance parity with its predecessor in standard mode while offering an extended reasoning mode for enhanced accuracy in math, coding, and instruction-following tasks. Read more at the [blog post here](https://www.anthropic.com/news/claude-3-7-sonnet)",
+}
+
+export const GLAMA_DEFAULT_TEMPERATURE = 0

+ 80 - 0
packages/types/src/providers/groq.ts

@@ -0,0 +1,80 @@
+import type { ModelInfo } from "../model.js"
+
+// https://console.groq.com/docs/models
+export type GroqModelId =
+	| "llama-3.1-8b-instant"
+	| "llama-3.3-70b-versatile"
+	| "meta-llama/llama-4-scout-17b-16e-instruct"
+	| "meta-llama/llama-4-maverick-17b-128e-instruct"
+	| "mistral-saba-24b"
+	| "qwen-qwq-32b"
+	| "deepseek-r1-distill-llama-70b"
+
+export const groqDefaultModelId: GroqModelId = "llama-3.3-70b-versatile" // Defaulting to Llama3 70B Versatile
+
+export const groqModels = {
+	// Models based on API response: https://api.groq.com/openai/v1/models
+	"llama-3.1-8b-instant": {
+		maxTokens: 131072,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "Meta Llama 3.1 8B Instant model, 128K context.",
+	},
+	"llama-3.3-70b-versatile": {
+		maxTokens: 32768,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "Meta Llama 3.3 70B Versatile model, 128K context.",
+	},
+	"meta-llama/llama-4-scout-17b-16e-instruct": {
+		maxTokens: 8192,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "Meta Llama 4 Scout 17B Instruct model, 128K context.",
+	},
+	"meta-llama/llama-4-maverick-17b-128e-instruct": {
+		maxTokens: 8192,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "Meta Llama 4 Maverick 17B Instruct model, 128K context.",
+	},
+	"mistral-saba-24b": {
+		maxTokens: 32768,
+		contextWindow: 32768,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "Mistral Saba 24B model, 32K context.",
+	},
+	"qwen-qwq-32b": {
+		maxTokens: 131072,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "Alibaba Qwen QwQ 32B model, 128K context.",
+	},
+	"deepseek-r1-distill-llama-70b": {
+		maxTokens: 131072,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		description: "DeepSeek R1 Distill Llama 70B model, 128K context.",
+	},
+} as const satisfies Record<string, ModelInfo>

+ 17 - 0
packages/types/src/providers/index.ts

@@ -0,0 +1,17 @@
+export * from "./anthropic.js"
+export * from "./bedrock.js"
+export * from "./chutes.js"
+export * from "./deepseek.js"
+export * from "./gemini.js"
+export * from "./glama.js"
+export * from "./groq.js"
+export * from "./lite-llm.js"
+export * from "./lm-studio.js"
+export * from "./mistral.js"
+export * from "./openai.js"
+export * from "./openrouter.js"
+export * from "./requesty.js"
+export * from "./unbound.js"
+export * from "./vertex.js"
+export * from "./vscode-llm.js"
+export * from "./xai.js"

+ 48 - 0
packages/types/src/providers/lite-llm.ts

@@ -0,0 +1,48 @@
+import type { ModelInfo } from "../model.js"
+
+// https://docs.litellm.ai/
+export const litellmDefaultModelId = "claude-3-7-sonnet-20250219"
+
+export const litellmDefaultModelInfo: ModelInfo = {
+	maxTokens: 8192,
+	contextWindow: 200_000,
+	supportsImages: true,
+	supportsComputerUse: true,
+	supportsPromptCache: true,
+	inputPrice: 3.0,
+	outputPrice: 15.0,
+	cacheWritesPrice: 3.75,
+	cacheReadsPrice: 0.3,
+}
+
+export const LITELLM_COMPUTER_USE_MODELS = new Set([
+	"claude-3-5-sonnet-latest",
+	"claude-opus-4-20250514",
+	"claude-sonnet-4-20250514",
+	"claude-3-7-sonnet-latest",
+	"claude-3-7-sonnet-20250219",
+	"claude-3-5-sonnet-20241022",
+	"vertex_ai/claude-3-5-sonnet",
+	"vertex_ai/claude-3-5-sonnet-v2",
+	"vertex_ai/claude-3-5-sonnet-v2@20241022",
+	"vertex_ai/claude-3-7-sonnet@20250219",
+	"vertex_ai/claude-opus-4@20250514",
+	"vertex_ai/claude-sonnet-4@20250514",
+	"openrouter/anthropic/claude-3.5-sonnet",
+	"openrouter/anthropic/claude-3.5-sonnet:beta",
+	"openrouter/anthropic/claude-3.7-sonnet",
+	"openrouter/anthropic/claude-3.7-sonnet:beta",
+	"anthropic.claude-opus-4-20250514-v1:0",
+	"anthropic.claude-sonnet-4-20250514-v1:0",
+	"anthropic.claude-3-7-sonnet-20250219-v1:0",
+	"anthropic.claude-3-5-sonnet-20241022-v2:0",
+	"us.anthropic.claude-3-5-sonnet-20241022-v2:0",
+	"us.anthropic.claude-3-7-sonnet-20250219-v1:0",
+	"us.anthropic.claude-opus-4-20250514-v1:0",
+	"us.anthropic.claude-sonnet-4-20250514-v1:0",
+	"eu.anthropic.claude-3-5-sonnet-20241022-v2:0",
+	"eu.anthropic.claude-3-7-sonnet-20250219-v1:0",
+	"eu.anthropic.claude-opus-4-20250514-v1:0",
+	"eu.anthropic.claude-sonnet-4-20250514-v1:0",
+	"snowflake/claude-3-5-sonnet",
+])

+ 1 - 0
packages/types/src/providers/lm-studio.ts

@@ -0,0 +1 @@
+export const LMSTUDIO_DEFAULT_TEMPERATURE = 0

+ 59 - 0
packages/types/src/providers/mistral.ts

@@ -0,0 +1,59 @@
+import type { ModelInfo } from "../model.js"
+
+// 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: 256_000,
+		contextWindow: 256_000,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0.3,
+		outputPrice: 0.9,
+	},
+	"mistral-large-latest": {
+		maxTokens: 131_000,
+		contextWindow: 131_000,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 2.0,
+		outputPrice: 6.0,
+	},
+	"ministral-8b-latest": {
+		maxTokens: 131_000,
+		contextWindow: 131_000,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0.1,
+		outputPrice: 0.1,
+	},
+	"ministral-3b-latest": {
+		maxTokens: 131_000,
+		contextWindow: 131_000,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0.04,
+		outputPrice: 0.04,
+	},
+	"mistral-small-latest": {
+		maxTokens: 32_000,
+		contextWindow: 32_000,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0.2,
+		outputPrice: 0.6,
+	},
+	"pixtral-large-latest": {
+		maxTokens: 131_000,
+		contextWindow: 131_000,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 2.0,
+		outputPrice: 6.0,
+	},
+} as const satisfies Record<string, ModelInfo>
+
+export const MISTRAL_DEFAULT_TEMPERATURE = 0

+ 200 - 0
packages/types/src/providers/openai.ts

@@ -0,0 +1,200 @@
+import type { ModelInfo } from "../model.js"
+
+// https://openai.com/api/pricing/
+export type OpenAiNativeModelId = keyof typeof openAiNativeModels
+
+export const openAiNativeDefaultModelId: OpenAiNativeModelId = "gpt-4.1"
+
+export const openAiNativeModels = {
+	"gpt-4.1": {
+		maxTokens: 32_768,
+		contextWindow: 1_047_576,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 2,
+		outputPrice: 8,
+		cacheReadsPrice: 0.5,
+	},
+	"gpt-4.1-mini": {
+		maxTokens: 32_768,
+		contextWindow: 1_047_576,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 0.4,
+		outputPrice: 1.6,
+		cacheReadsPrice: 0.1,
+	},
+	"gpt-4.1-nano": {
+		maxTokens: 32_768,
+		contextWindow: 1_047_576,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 0.1,
+		outputPrice: 0.4,
+		cacheReadsPrice: 0.025,
+	},
+	o3: {
+		maxTokens: 100_000,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 10.0,
+		outputPrice: 40.0,
+		cacheReadsPrice: 2.5,
+		supportsReasoningEffort: true,
+		reasoningEffort: "medium",
+	},
+	"o3-high": {
+		maxTokens: 100_000,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 10.0,
+		outputPrice: 40.0,
+		cacheReadsPrice: 2.5,
+		reasoningEffort: "high",
+	},
+	"o3-low": {
+		maxTokens: 100_000,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 10.0,
+		outputPrice: 40.0,
+		cacheReadsPrice: 2.5,
+		reasoningEffort: "low",
+	},
+	"o4-mini": {
+		maxTokens: 100_000,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 1.1,
+		outputPrice: 4.4,
+		cacheReadsPrice: 0.275,
+		supportsReasoningEffort: true,
+		reasoningEffort: "medium",
+	},
+	"o4-mini-high": {
+		maxTokens: 100_000,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 1.1,
+		outputPrice: 4.4,
+		cacheReadsPrice: 0.275,
+		reasoningEffort: "high",
+	},
+	"o4-mini-low": {
+		maxTokens: 100_000,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 1.1,
+		outputPrice: 4.4,
+		cacheReadsPrice: 0.275,
+		reasoningEffort: "low",
+	},
+	"o3-mini": {
+		maxTokens: 100_000,
+		contextWindow: 200_000,
+		supportsImages: false,
+		supportsPromptCache: true,
+		inputPrice: 1.1,
+		outputPrice: 4.4,
+		cacheReadsPrice: 0.55,
+		supportsReasoningEffort: true,
+		reasoningEffort: "medium",
+	},
+	"o3-mini-high": {
+		maxTokens: 100_000,
+		contextWindow: 200_000,
+		supportsImages: false,
+		supportsPromptCache: true,
+		inputPrice: 1.1,
+		outputPrice: 4.4,
+		cacheReadsPrice: 0.55,
+		reasoningEffort: "high",
+	},
+	"o3-mini-low": {
+		maxTokens: 100_000,
+		contextWindow: 200_000,
+		supportsImages: false,
+		supportsPromptCache: true,
+		inputPrice: 1.1,
+		outputPrice: 4.4,
+		cacheReadsPrice: 0.55,
+		reasoningEffort: "low",
+	},
+	o1: {
+		maxTokens: 100_000,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 15,
+		outputPrice: 60,
+		cacheReadsPrice: 7.5,
+	},
+	"o1-preview": {
+		maxTokens: 32_768,
+		contextWindow: 128_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 15,
+		outputPrice: 60,
+		cacheReadsPrice: 7.5,
+	},
+	"o1-mini": {
+		maxTokens: 65_536,
+		contextWindow: 128_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 1.1,
+		outputPrice: 4.4,
+		cacheReadsPrice: 0.55,
+	},
+	"gpt-4.5-preview": {
+		maxTokens: 16_384,
+		contextWindow: 128_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 75,
+		outputPrice: 150,
+		cacheReadsPrice: 37.5,
+	},
+	"gpt-4o": {
+		maxTokens: 16_384,
+		contextWindow: 128_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 2.5,
+		outputPrice: 10,
+		cacheReadsPrice: 1.25,
+	},
+	"gpt-4o-mini": {
+		maxTokens: 16_384,
+		contextWindow: 128_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 0.15,
+		outputPrice: 0.6,
+		cacheReadsPrice: 0.075,
+	},
+} as const satisfies Record<string, ModelInfo>
+
+export const openAiModelInfoSaneDefaults: ModelInfo = {
+	maxTokens: -1,
+	contextWindow: 128_000,
+	supportsImages: true,
+	supportsPromptCache: false,
+	inputPrice: 0,
+	outputPrice: 0,
+}
+
+// https://learn.microsoft.com/en-us/azure/ai-services/openai/api-version-deprecation
+// https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#api-specs
+export const azureOpenAiDefaultApiVersion = "2024-08-01-preview"
+
+export const OPENAI_NATIVE_DEFAULT_TEMPERATURE = 0
+
+export const OPENAI_AZURE_AI_INFERENCE_PATH = "/models/chat/completions"

+ 75 - 0
packages/types/src/providers/openrouter.ts

@@ -0,0 +1,75 @@
+import type { ModelInfo } from "../model.js"
+
+// https://openrouter.ai/models?order=newest&supported_parameters=tools
+export const openRouterDefaultModelId = "anthropic/claude-sonnet-4"
+
+export const openRouterDefaultModelInfo: ModelInfo = {
+	maxTokens: 8192,
+	contextWindow: 200_000,
+	supportsImages: true,
+	supportsComputerUse: true,
+	supportsPromptCache: true,
+	inputPrice: 3.0,
+	outputPrice: 15.0,
+	cacheWritesPrice: 3.75,
+	cacheReadsPrice: 0.3,
+	description:
+		"Claude 3.7 Sonnet is an advanced large language model with improved reasoning, coding, and problem-solving capabilities. It introduces a hybrid reasoning approach, allowing users to choose between rapid responses and extended, step-by-step processing for complex tasks. The model demonstrates notable improvements in coding, particularly in front-end development and full-stack updates, and excels in agentic workflows, where it can autonomously navigate multi-step processes. Claude 3.7 Sonnet maintains performance parity with its predecessor in standard mode while offering an extended reasoning mode for enhanced accuracy in math, coding, and instruction-following tasks. Read more at the [blog post here](https://www.anthropic.com/news/claude-3-7-sonnet)",
+}
+
+export const OPENROUTER_DEFAULT_PROVIDER_NAME = "[default]"
+
+export const OPEN_ROUTER_PROMPT_CACHING_MODELS = new Set([
+	"anthropic/claude-3-haiku",
+	"anthropic/claude-3-haiku:beta",
+	"anthropic/claude-3-opus",
+	"anthropic/claude-3-opus:beta",
+	"anthropic/claude-3-sonnet",
+	"anthropic/claude-3-sonnet:beta",
+	"anthropic/claude-3.5-haiku",
+	"anthropic/claude-3.5-haiku-20241022",
+	"anthropic/claude-3.5-haiku-20241022:beta",
+	"anthropic/claude-3.5-haiku:beta",
+	"anthropic/claude-3.5-sonnet",
+	"anthropic/claude-3.5-sonnet-20240620",
+	"anthropic/claude-3.5-sonnet-20240620:beta",
+	"anthropic/claude-3.5-sonnet:beta",
+	"anthropic/claude-3.7-sonnet",
+	"anthropic/claude-3.7-sonnet:beta",
+	"anthropic/claude-3.7-sonnet:thinking",
+	"anthropic/claude-sonnet-4",
+	"anthropic/claude-opus-4",
+	"google/gemini-2.5-pro-preview",
+	"google/gemini-2.5-flash-preview",
+	"google/gemini-2.5-flash-preview:thinking",
+	"google/gemini-2.5-flash-preview-05-20",
+	"google/gemini-2.5-flash-preview-05-20:thinking",
+	"google/gemini-2.0-flash-001",
+	"google/gemini-flash-1.5",
+	"google/gemini-flash-1.5-8b",
+])
+
+// https://www.anthropic.com/news/3-5-models-and-computer-use
+export const OPEN_ROUTER_COMPUTER_USE_MODELS = new Set([
+	"anthropic/claude-3.5-sonnet",
+	"anthropic/claude-3.5-sonnet:beta",
+	"anthropic/claude-3.7-sonnet",
+	"anthropic/claude-3.7-sonnet:beta",
+	"anthropic/claude-3.7-sonnet:thinking",
+	"anthropic/claude-sonnet-4",
+	"anthropic/claude-opus-4",
+])
+
+export const OPEN_ROUTER_REASONING_BUDGET_MODELS = new Set([
+	"anthropic/claude-3.7-sonnet:beta",
+	"anthropic/claude-3.7-sonnet:thinking",
+	"anthropic/claude-opus-4",
+	"anthropic/claude-sonnet-4",
+	"google/gemini-2.5-flash-preview-05-20",
+	"google/gemini-2.5-flash-preview-05-20:thinking",
+])
+
+export const OPEN_ROUTER_REQUIRED_REASONING_BUDGET_MODELS = new Set([
+	"anthropic/claude-3.7-sonnet:thinking",
+	"google/gemini-2.5-flash-preview-05-20:thinking",
+])

+ 19 - 0
packages/types/src/providers/requesty.ts

@@ -0,0 +1,19 @@
+import type { ModelInfo } from "../model.js"
+
+// Requesty
+// https://requesty.ai/router-2
+export const requestyDefaultModelId = "coding/claude-4-sonnet"
+
+export const requestyDefaultModelInfo: ModelInfo = {
+	maxTokens: 8192,
+	contextWindow: 200_000,
+	supportsImages: true,
+	supportsComputerUse: true,
+	supportsPromptCache: true,
+	inputPrice: 3.0,
+	outputPrice: 15.0,
+	cacheWritesPrice: 3.75,
+	cacheReadsPrice: 0.3,
+	description:
+		"The best coding model, optimized by Requesty, and automatically routed to the fastest provider. Claude 4 Sonnet is an advanced large language model with improved reasoning, coding, and problem-solving capabilities.",
+}

+ 14 - 0
packages/types/src/providers/unbound.ts

@@ -0,0 +1,14 @@
+import type { ModelInfo } from "../model.js"
+
+export const unboundDefaultModelId = "anthropic/claude-3-7-sonnet-20250219"
+
+export const unboundDefaultModelInfo: ModelInfo = {
+	maxTokens: 8192,
+	contextWindow: 200_000,
+	supportsImages: true,
+	supportsPromptCache: true,
+	inputPrice: 3.0,
+	outputPrice: 15.0,
+	cacheWritesPrice: 3.75,
+	cacheReadsPrice: 0.3,
+}

+ 225 - 0
packages/types/src/providers/vertex.ts

@@ -0,0 +1,225 @@
+import type { ModelInfo } from "../model.js"
+
+// https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-claude
+export type VertexModelId = keyof typeof vertexModels
+
+export const vertexDefaultModelId: VertexModelId = "claude-sonnet-4@20250514"
+
+export const vertexModels = {
+	"gemini-2.5-flash-preview-05-20:thinking": {
+		maxTokens: 65_535,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 0.15,
+		outputPrice: 3.5,
+		maxThinkingTokens: 24_576,
+		supportsReasoningBudget: true,
+		requiredReasoningBudget: true,
+	},
+	"gemini-2.5-flash-preview-05-20": {
+		maxTokens: 65_535,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 0.15,
+		outputPrice: 0.6,
+	},
+	"gemini-2.5-flash-preview-04-17:thinking": {
+		maxTokens: 65_535,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0.15,
+		outputPrice: 3.5,
+		maxThinkingTokens: 24_576,
+		supportsReasoningBudget: true,
+		requiredReasoningBudget: true,
+	},
+	"gemini-2.5-flash-preview-04-17": {
+		maxTokens: 65_535,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0.15,
+		outputPrice: 0.6,
+	},
+	"gemini-2.5-pro-preview-03-25": {
+		maxTokens: 65_535,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 2.5,
+		outputPrice: 15,
+	},
+	"gemini-2.5-pro-preview-05-06": {
+		maxTokens: 65_535,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 2.5,
+		outputPrice: 15,
+	},
+	"gemini-2.5-pro-exp-03-25": {
+		maxTokens: 65_535,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-2.0-pro-exp-02-05": {
+		maxTokens: 8192,
+		contextWindow: 2_097_152,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-2.0-flash-001": {
+		maxTokens: 8192,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 0.15,
+		outputPrice: 0.6,
+	},
+	"gemini-2.0-flash-lite-001": {
+		maxTokens: 8192,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0.075,
+		outputPrice: 0.3,
+	},
+	"gemini-2.0-flash-thinking-exp-01-21": {
+		maxTokens: 8192,
+		contextWindow: 32_768,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+	},
+	"gemini-1.5-flash-002": {
+		maxTokens: 8192,
+		contextWindow: 1_048_576,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 0.075,
+		outputPrice: 0.3,
+	},
+	"gemini-1.5-pro-002": {
+		maxTokens: 8192,
+		contextWindow: 2_097_152,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 1.25,
+		outputPrice: 5,
+	},
+	"claude-sonnet-4@20250514": {
+		maxTokens: 8192,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsComputerUse: true,
+		supportsPromptCache: true,
+		inputPrice: 3.0,
+		outputPrice: 15.0,
+		cacheWritesPrice: 3.75,
+		cacheReadsPrice: 0.3,
+		supportsReasoningBudget: true,
+	},
+	"claude-opus-4@20250514": {
+		maxTokens: 8192,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsComputerUse: true,
+		supportsPromptCache: true,
+		inputPrice: 15.0,
+		outputPrice: 75.0,
+		cacheWritesPrice: 18.75,
+		cacheReadsPrice: 1.5,
+	},
+	"claude-3-7-sonnet@20250219:thinking": {
+		maxTokens: 64_000,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsComputerUse: true,
+		supportsPromptCache: true,
+		inputPrice: 3.0,
+		outputPrice: 15.0,
+		cacheWritesPrice: 3.75,
+		cacheReadsPrice: 0.3,
+		supportsReasoningBudget: true,
+		requiredReasoningBudget: true,
+	},
+	"claude-3-7-sonnet@20250219": {
+		maxTokens: 8192,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsComputerUse: true,
+		supportsPromptCache: true,
+		inputPrice: 3.0,
+		outputPrice: 15.0,
+		cacheWritesPrice: 3.75,
+		cacheReadsPrice: 0.3,
+	},
+	"claude-3-5-sonnet-v2@20241022": {
+		maxTokens: 8192,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsComputerUse: true,
+		supportsPromptCache: true,
+		inputPrice: 3.0,
+		outputPrice: 15.0,
+		cacheWritesPrice: 3.75,
+		cacheReadsPrice: 0.3,
+	},
+	"claude-3-5-sonnet@20240620": {
+		maxTokens: 8192,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 3.0,
+		outputPrice: 15.0,
+		cacheWritesPrice: 3.75,
+		cacheReadsPrice: 0.3,
+	},
+	"claude-3-5-haiku@20241022": {
+		maxTokens: 8192,
+		contextWindow: 200_000,
+		supportsImages: false,
+		supportsPromptCache: true,
+		inputPrice: 1.0,
+		outputPrice: 5.0,
+		cacheWritesPrice: 1.25,
+		cacheReadsPrice: 0.1,
+	},
+	"claude-3-opus@20240229": {
+		maxTokens: 4096,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 15.0,
+		outputPrice: 75.0,
+		cacheWritesPrice: 18.75,
+		cacheReadsPrice: 1.5,
+	},
+	"claude-3-haiku@20240307": {
+		maxTokens: 4096,
+		contextWindow: 200_000,
+		supportsImages: true,
+		supportsPromptCache: true,
+		inputPrice: 0.25,
+		outputPrice: 1.25,
+		cacheWritesPrice: 0.3,
+		cacheReadsPrice: 0.03,
+	},
+} as const satisfies Record<string, ModelInfo>
+
+export const VERTEX_REGIONS = [
+	{ value: "us-east5", label: "us-east5" },
+	{ value: "us-central1", label: "us-central1" },
+	{ value: "europe-west1", label: "europe-west1" },
+	{ value: "europe-west4", label: "europe-west4" },
+	{ value: "asia-southeast1", label: "asia-southeast1" },
+]

+ 161 - 0
packages/types/src/providers/vscode-llm.ts

@@ -0,0 +1,161 @@
+import type { ModelInfo } from "../model.js"
+
+export type VscodeLlmModelId = keyof typeof vscodeLlmModels
+
+export const vscodeLlmDefaultModelId: VscodeLlmModelId = "claude-3.5-sonnet"
+
+export const vscodeLlmModels = {
+	"gpt-3.5-turbo": {
+		contextWindow: 12114,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		family: "gpt-3.5-turbo",
+		version: "gpt-3.5-turbo-0613",
+		name: "GPT 3.5 Turbo",
+		supportsToolCalling: true,
+		maxInputTokens: 12114,
+	},
+	"gpt-4o-mini": {
+		contextWindow: 12115,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		family: "gpt-4o-mini",
+		version: "gpt-4o-mini-2024-07-18",
+		name: "GPT-4o mini",
+		supportsToolCalling: true,
+		maxInputTokens: 12115,
+	},
+	"gpt-4": {
+		contextWindow: 28501,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		family: "gpt-4",
+		version: "gpt-4-0613",
+		name: "GPT 4",
+		supportsToolCalling: true,
+		maxInputTokens: 28501,
+	},
+	"gpt-4-0125-preview": {
+		contextWindow: 63826,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		family: "gpt-4-turbo",
+		version: "gpt-4-0125-preview",
+		name: "GPT 4 Turbo",
+		supportsToolCalling: true,
+		maxInputTokens: 63826,
+	},
+	"gpt-4o": {
+		contextWindow: 63827,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		family: "gpt-4o",
+		version: "gpt-4o-2024-11-20",
+		name: "GPT-4o",
+		supportsToolCalling: true,
+		maxInputTokens: 63827,
+	},
+	o1: {
+		contextWindow: 19827,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		family: "o1-ga",
+		version: "o1-2024-12-17",
+		name: "o1 (Preview)",
+		supportsToolCalling: true,
+		maxInputTokens: 19827,
+	},
+	"o3-mini": {
+		contextWindow: 63827,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		family: "o3-mini",
+		version: "o3-mini-2025-01-31",
+		name: "o3-mini",
+		supportsToolCalling: true,
+		maxInputTokens: 63827,
+	},
+	"claude-3.5-sonnet": {
+		contextWindow: 81638,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		family: "claude-3.5-sonnet",
+		version: "claude-3.5-sonnet",
+		name: "Claude 3.5 Sonnet",
+		supportsToolCalling: true,
+		maxInputTokens: 81638,
+	},
+	"gemini-2.0-flash-001": {
+		contextWindow: 127827,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		family: "gemini-2.0-flash",
+		version: "gemini-2.0-flash-001",
+		name: "Gemini 2.0 Flash",
+		supportsToolCalling: false,
+		maxInputTokens: 127827,
+	},
+	"gemini-2.5-pro": {
+		contextWindow: 63830,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		family: "gemini-2.5-pro",
+		version: "gemini-2.5-pro-preview-03-25",
+		name: "Gemini 2.5 Pro (Preview)",
+		supportsToolCalling: true,
+		maxInputTokens: 63830,
+	},
+	"o4-mini": {
+		contextWindow: 111446,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		family: "o4-mini",
+		version: "o4-mini-2025-04-16",
+		name: "o4-mini (Preview)",
+		supportsToolCalling: true,
+		maxInputTokens: 111446,
+	},
+	"gpt-4.1": {
+		contextWindow: 111446,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 0,
+		outputPrice: 0,
+		family: "gpt-4.1",
+		version: "gpt-4.1-2025-04-14",
+		name: "GPT-4.1 (Preview)",
+		supportsToolCalling: true,
+		maxInputTokens: 111446,
+	},
+} as const satisfies Record<
+	string,
+	ModelInfo & {
+		family: string
+		version: string
+		name: string
+		supportsToolCalling: boolean
+		maxInputTokens: number
+	}
+>

+ 157 - 0
packages/types/src/providers/xai.ts

@@ -0,0 +1,157 @@
+import type { ModelInfo } from "../model.js"
+
+// https://docs.x.ai/docs/api-reference
+export type XAIModelId = keyof typeof xaiModels
+
+export const xaiDefaultModelId: XAIModelId = "grok-3"
+
+export const xaiModels = {
+	"grok-3-beta": {
+		maxTokens: 8192,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 3.0,
+		outputPrice: 15.0,
+		description: "xAI's Grok-3 beta model with 131K context window",
+	},
+	"grok-3-fast-beta": {
+		maxTokens: 8192,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 5.0,
+		outputPrice: 25.0,
+		description: "xAI's Grok-3 fast beta model with 131K context window",
+	},
+	"grok-3-mini-beta": {
+		maxTokens: 8192,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0.3,
+		outputPrice: 0.5,
+		description: "xAI's Grok-3 mini beta model with 131K context window",
+		supportsReasoningEffort: true,
+	},
+	"grok-3-mini-fast-beta": {
+		maxTokens: 8192,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0.6,
+		outputPrice: 4.0,
+		description: "xAI's Grok-3 mini fast beta model with 131K context window",
+		supportsReasoningEffort: true,
+	},
+	"grok-3": {
+		maxTokens: 8192,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 3.0,
+		outputPrice: 15.0,
+		description: "xAI's Grok-3 model with 131K context window",
+	},
+	"grok-3-fast": {
+		maxTokens: 8192,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 5.0,
+		outputPrice: 25.0,
+		description: "xAI's Grok-3 fast model with 131K context window",
+	},
+	"grok-3-mini": {
+		maxTokens: 8192,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0.3,
+		outputPrice: 0.5,
+		description: "xAI's Grok-3 mini model with 131K context window",
+		supportsReasoningEffort: true,
+	},
+	"grok-3-mini-fast": {
+		maxTokens: 8192,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 0.6,
+		outputPrice: 4.0,
+		description: "xAI's Grok-3 mini fast model with 131K context window",
+		supportsReasoningEffort: true,
+	},
+	"grok-2-latest": {
+		maxTokens: 8192,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 2.0,
+		outputPrice: 10.0,
+		description: "xAI's Grok-2 model - latest version with 131K context window",
+	},
+	"grok-2": {
+		maxTokens: 8192,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 2.0,
+		outputPrice: 10.0,
+		description: "xAI's Grok-2 model with 131K context window",
+	},
+	"grok-2-1212": {
+		maxTokens: 8192,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 2.0,
+		outputPrice: 10.0,
+		description: "xAI's Grok-2 model (version 1212) with 131K context window",
+	},
+	"grok-2-vision-latest": {
+		maxTokens: 8192,
+		contextWindow: 32768,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 2.0,
+		outputPrice: 10.0,
+		description: "xAI's Grok-2 Vision model - latest version with image support and 32K context window",
+	},
+	"grok-2-vision": {
+		maxTokens: 8192,
+		contextWindow: 32768,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 2.0,
+		outputPrice: 10.0,
+		description: "xAI's Grok-2 Vision model with image support and 32K context window",
+	},
+	"grok-2-vision-1212": {
+		maxTokens: 8192,
+		contextWindow: 32768,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 2.0,
+		outputPrice: 10.0,
+		description: "xAI's Grok-2 Vision model (version 1212) with image support and 32K context window",
+	},
+	"grok-vision-beta": {
+		maxTokens: 8192,
+		contextWindow: 8192,
+		supportsImages: true,
+		supportsPromptCache: false,
+		inputPrice: 5.0,
+		outputPrice: 15.0,
+		description: "xAI's Grok Vision Beta model with image support and 8K context window",
+	},
+	"grok-beta": {
+		maxTokens: 8192,
+		contextWindow: 131072,
+		supportsImages: false,
+		supportsPromptCache: false,
+		inputPrice: 5.0,
+		outputPrice: 15.0,
+		description: "xAI's Grok Beta model (legacy) with 131K context window",
+	},
+} as const satisfies Record<string, ModelInfo>

+ 48 - 13
packages/types/src/telemetry.ts

@@ -1,6 +1,7 @@
 import { z } from "zod"
 
 import { providerNames } from "./provider-settings.js"
+import { clineMessageSchema } from "./message.js"
 
 /**
  * TelemetrySetting
@@ -20,6 +21,7 @@ export enum TelemetryEventName {
 	TASK_CREATED = "Task Created",
 	TASK_RESTARTED = "Task Reopened",
 	TASK_COMPLETED = "Task Completed",
+	TASK_MESSAGE = "Task Message",
 	TASK_CONVERSATION_MESSAGE = "Conversation Message",
 	LLM_COMPLETION = "LLM Completion",
 	MODE_SWITCH = "Mode Switched",
@@ -50,6 +52,7 @@ export enum TelemetryEventName {
  */
 
 export const appPropertiesSchema = z.object({
+	appName: z.string(),
 	appVersion: z.string(),
 	vscodeVersion: z.string(),
 	platform: z.string(),
@@ -87,14 +90,6 @@ export type TelemetryEvent = {
  * RooCodeTelemetryEvent
  */
 
-const completionPropertiesSchema = z.object({
-	inputTokens: z.number(),
-	outputTokens: z.number(),
-	cacheReadTokens: z.number().optional(),
-	cacheWriteTokens: z.number().optional(),
-	cost: z.number().optional(),
-})
-
 export const rooCodeTelemetryEventSchema = z.discriminatedUnion("type", [
 	z.object({
 		type: z.enum([
@@ -115,20 +110,60 @@ export const rooCodeTelemetryEventSchema = z.discriminatedUnion("type", [
 			TelemetryEventName.DIFF_APPLICATION_ERROR,
 			TelemetryEventName.SHELL_INTEGRATION_ERROR,
 			TelemetryEventName.CONSECUTIVE_MISTAKE_ERROR,
+			TelemetryEventName.CONTEXT_CONDENSED,
+			TelemetryEventName.SLIDING_WINDOW_TRUNCATION,
 		]),
+		properties: telemetryPropertiesSchema,
+	}),
+	z.object({
+		type: z.literal(TelemetryEventName.TASK_MESSAGE),
 		properties: z.object({
-			...appPropertiesSchema.shape,
-			...taskPropertiesSchema.shape,
+			...telemetryPropertiesSchema.shape,
+			taskId: z.string(),
+			message: clineMessageSchema,
 		}),
 	}),
 	z.object({
 		type: z.literal(TelemetryEventName.LLM_COMPLETION),
 		properties: z.object({
-			...appPropertiesSchema.shape,
-			...taskPropertiesSchema.shape,
-			...completionPropertiesSchema.shape,
+			...telemetryPropertiesSchema.shape,
+			inputTokens: z.number(),
+			outputTokens: z.number(),
+			cacheReadTokens: z.number().optional(),
+			cacheWriteTokens: z.number().optional(),
+			cost: z.number().optional(),
 		}),
 	}),
 ])
 
 export type RooCodeTelemetryEvent = z.infer<typeof rooCodeTelemetryEventSchema>
+
+/**
+ * TelemetryEventSubscription
+ */
+
+export type TelemetryEventSubscription =
+	| { type: "include"; events: TelemetryEventName[] }
+	| { type: "exclude"; events: TelemetryEventName[] }
+
+/**
+ * TelemetryPropertiesProvider
+ */
+
+export interface TelemetryPropertiesProvider {
+	getTelemetryProperties(): Promise<TelemetryProperties>
+}
+
+/**
+ * TelemetryClient
+ */
+
+export interface TelemetryClient {
+	subscription?: TelemetryEventSubscription
+
+	setProvider(provider: TelemetryPropertiesProvider): void
+	capture(options: TelemetryEvent): Promise<void>
+	updateTelemetryState(didUserOptIn: boolean): void
+	isTelemetryEnabled(): boolean
+	shutdown(): Promise<void>
+}

+ 1 - 0
packages/types/src/vscode.ts

@@ -34,6 +34,7 @@ export const commandIds = [
 
 	"historyButtonClicked",
 	"popoutButtonClicked",
+	"accountButtonClicked",
 	"settingsButtonClicked",
 
 	"openInNewTab",

+ 7 - 0
packages/types/vitest.config.ts

@@ -0,0 +1,7 @@
+import { defineConfig } from "vitest/config"
+
+export default defineConfig({
+	test: {
+		globals: true,
+	},
+})

+ 79 - 66
pnpm-lock.yaml

@@ -78,8 +78,8 @@ importers:
         specifier: ^1.95.0
         version: 1.100.0
       '@vscode/test-cli':
-        specifier: ^0.0.10
-        version: 0.0.10
+        specifier: ^0.0.11
+        version: 0.0.11
       '@vscode/test-electron':
         specifier: ^2.4.0
         version: 2.5.2
@@ -121,6 +121,37 @@ importers:
         specifier: ^3.1.3
         version: 3.2.0(@types/[email protected])(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
 
+  packages/cloud:
+    dependencies:
+      '@roo-code/telemetry':
+        specifier: workspace:^
+        version: link:../telemetry
+      '@roo-code/types':
+        specifier: workspace:^
+        version: link:../types
+      axios:
+        specifier: ^1.7.4
+        version: 1.9.0
+      zod:
+        specifier: ^3.24.2
+        version: 3.25.48
+    devDependencies:
+      '@roo-code/config-eslint':
+        specifier: workspace:^
+        version: link:../config-eslint
+      '@roo-code/config-typescript':
+        specifier: workspace:^
+        version: link:../config-typescript
+      '@types/node':
+        specifier: ^22.15.20
+        version: 22.15.29
+      '@types/vscode':
+        specifier: ^1.84.0
+        version: 1.100.0
+      vitest:
+        specifier: ^3.1.3
+        version: 3.2.0(@types/[email protected])(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
+
   packages/config-eslint:
     devDependencies:
       '@eslint/js':
@@ -156,6 +187,34 @@ importers:
 
   packages/config-typescript: {}
 
+  packages/telemetry:
+    dependencies:
+      '@roo-code/types':
+        specifier: workspace:^
+        version: link:../types
+      posthog-node:
+        specifier: ^4.7.0
+        version: 4.18.0
+      zod:
+        specifier: ^3.24.2
+        version: 3.25.48
+    devDependencies:
+      '@roo-code/config-eslint':
+        specifier: workspace:^
+        version: link:../config-eslint
+      '@roo-code/config-typescript':
+        specifier: workspace:^
+        version: link:../config-typescript
+      '@types/node':
+        specifier: ^22.15.20
+        version: 22.15.29
+      '@types/vscode':
+        specifier: ^1.84.0
+        version: 1.100.0
+      vitest:
+        specifier: ^3.1.3
+        version: 3.2.0(@types/[email protected])(@types/[email protected])([email protected])([email protected])([email protected])([email protected])([email protected])
+
   packages/types:
     dependencies:
       zod:
@@ -207,15 +266,18 @@ importers:
       '@qdrant/js-client-rest':
         specifier: ^1.14.0
         version: 1.14.1([email protected])
+      '@roo-code/cloud':
+        specifier: workspace:^
+        version: link:../packages/cloud
+      '@roo-code/telemetry':
+        specifier: workspace:^
+        version: link:../packages/telemetry
       '@roo-code/types':
         specifier: workspace:^
         version: link:../packages/types
       '@types/lodash.debounce':
         specifier: ^4.0.9
         version: 4.0.9
-      '@types/lru-cache':
-        specifier: ^7.10.9
-        version: 7.10.10
       '@vscode/codicons':
         specifier: ^0.0.36
         version: 0.0.36
@@ -309,9 +371,6 @@ importers:
       pkce-challenge:
         specifier: ^4.1.0
         version: 4.1.0
-      posthog-node:
-        specifier: ^4.7.0
-        version: 4.18.0
       pretty-bytes:
         specifier: ^6.1.1
         version: 6.1.1
@@ -592,9 +651,12 @@ importers:
       knuth-shuffle-seeded:
         specifier: ^1.0.6
         version: 1.0.6
+      lru-cache:
+        specifier: ^11.1.0
+        version: 11.1.0
       lucide-react:
-        specifier: ^0.510.0
-        version: 0.510.0([email protected])
+        specifier: ^0.511.0
+        version: 0.511.0([email protected])
       mermaid:
         specifier: ^11.4.1
         version: 11.6.0
@@ -3140,10 +3202,6 @@ packages:
   '@types/[email protected]':
     resolution: {integrity: sha512-RRVJ+J3J+WmyOTqnz3PiBLA501eKwXl2noseKOrNo/6+XEHjTAxO4xHvxQB6QuNm+s4WRbn6rSiap8+EA+ykFQ==}
 
-  '@types/[email protected]':
-    resolution: {integrity: sha512-nEpVRPWW9EBmx2SCfNn3ClYxPL7IktPX12HhIoSc/H5mMjdeW3+YsXIpseLQ2xF35+OcpwKQbEUw5VtqE4PDNA==}
-    deprecated: This is a stub types definition. lru-cache provides its own type definitions, so you do not need this installed.
-
   '@types/[email protected]':
     resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==}
 
@@ -3354,8 +3412,8 @@ packages:
   '@vscode/[email protected]':
     resolution: {integrity: sha512-wsNOvNMMJ2BY8rC2N2MNBG7yOowV3ov8KlvUE/AiVUlHKTfWsw3OgAOQduX7h0Un6GssKD3aoTVH+TF3DSQwKQ==}
 
-  '@vscode/[email protected]0':
-    resolution: {integrity: sha512-B0mMH4ia+MOOtwNiLi79XhA+MLmUItIC8FckEuKrVAVriIuSWjt7vv4+bF8qVFiNFe4QRfzPaIZk39FZGWEwHA==}
+  '@vscode/[email protected]1':
+    resolution: {integrity: sha512-qO332yvzFqGhBMJrp6TdwbIydiHgCtxXc2Nl6M58mbH/Z+0CyLR76Jzv4YWPEthhrARprzCRJUqzFvTHFhTj7Q==}
     engines: {node: '>=18'}
     hasBin: true
 
@@ -5112,11 +5170,6 @@ packages:
     resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
     deprecated: Glob versions prior to v9 are no longer supported
 
-  [email protected]:
-    resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
-    engines: {node: '>=12'}
-    deprecated: Glob versions prior to v9 are no longer supported
-
   [email protected]:
     resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
     engines: {node: '>=4'}
@@ -6200,8 +6253,8 @@ packages:
     resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
     engines: {node: '>=12'}
 
-  [email protected]0.0:
-    resolution: {integrity: sha512-p8SQRAMVh7NhsAIETokSqDrc5CHnDLbV29mMnzaXx+Vc/hnqQzwI2r0FMWCcoTXnbw2KEjy48xwpGdEL+ck06Q==}
+  [email protected]1.0:
+    resolution: {integrity: sha512-VK5a2ydJ7xm8GvBeKLS9mu1pVK6ucef9780JVUjw6bAjJL/QXnd4Y0p7SPeOUMC27YhzNCZvm5d/QX0Tp3rc0w==}
     peerDependencies:
       react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
 
@@ -6525,11 +6578,6 @@ packages:
   [email protected]:
     resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==}
 
-  [email protected]:
-    resolution: {integrity: sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==}
-    engines: {node: '>= 14.0.0'}
-    hasBin: true
-
   [email protected]:
     resolution: {integrity: sha512-VKDjhy6LMTKm0WgNEdlY77YVsD49LZnPSXJAaPNL9NRYQADxvORsyG1DIQY6v53BKTnlNbEE2MbVCDbnxr4K3w==}
     engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -11984,10 +12032,6 @@ snapshots:
 
   '@types/[email protected]': {}
 
-  '@types/[email protected]':
-    dependencies:
-      lru-cache: 11.1.0
-
   '@types/[email protected]':
     dependencies:
       '@types/unist': 2.0.11
@@ -12253,7 +12297,7 @@ snapshots:
 
   '@vscode/[email protected]': {}
 
-  '@vscode/[email protected]0':
+  '@vscode/[email protected]1':
     dependencies:
       '@types/mocha': 10.0.10
       c8: 9.1.0
@@ -12261,7 +12305,7 @@ snapshots:
       enhanced-resolve: 5.18.1
       glob: 10.4.5
       minimatch: 9.0.5
-      mocha: 10.8.2
+      mocha: 11.5.0
       supports-color: 9.4.0
       yargs: 17.7.2
 
@@ -14301,14 +14345,6 @@ snapshots:
       once: 1.4.0
       path-is-absolute: 1.0.1
 
-  [email protected]:
-    dependencies:
-      fs.realpath: 1.0.0
-      inflight: 1.0.6
-      inherits: 2.0.4
-      minimatch: 5.1.6
-      once: 1.4.0
-
   [email protected]: {}
 
   [email protected]: {}
@@ -15707,7 +15743,7 @@ snapshots:
 
   [email protected]: {}
 
-  [email protected]0.0([email protected]):
+  [email protected]1.0([email protected]):
     dependencies:
       react: 18.3.1
 
@@ -16258,29 +16294,6 @@ snapshots:
       pkg-types: 1.3.1
       ufo: 1.6.1
 
-  [email protected]:
-    dependencies:
-      ansi-colors: 4.1.3
-      browser-stdout: 1.3.1
-      chokidar: 3.6.0
-      debug: 4.4.1([email protected])
-      diff: 5.2.0
-      escape-string-regexp: 4.0.0
-      find-up: 5.0.0
-      glob: 8.1.0
-      he: 1.2.0
-      js-yaml: 4.1.0
-      log-symbols: 4.1.0
-      minimatch: 5.1.6
-      ms: 2.1.3
-      serialize-javascript: 6.0.2
-      strip-json-comments: 3.1.1
-      supports-color: 8.1.1
-      workerpool: 6.5.1
-      yargs: 16.2.0
-      yargs-parser: 20.2.9
-      yargs-unparser: 2.0.0
-
   [email protected]:
     dependencies:
       browser-stdout: 1.3.1

+ 3 - 1
renovate.json

@@ -1,4 +1,6 @@
 {
 	"$schema": "https://docs.renovatebot.com/renovate-schema.json",
-	"extends": ["config:recommended"]
+	"extends": ["config:recommended"],
+	"forkProcessing": "enabled",
+	"ignoreDeps": ["@vscode/vsce"]
 }

+ 3 - 2
src/__mocks__/@modelcontextprotocol/sdk/client/streamableHttp.js

@@ -1,8 +1,9 @@
-class StreamableHttpClientTransport {
+class StreamableHTTPClientTransport {
 	constructor(url, options = {}) {
 		this.url = url
 		this.options = options
 		this.onerror = null
+		this.onclose = null
 		this.connect = jest.fn().mockResolvedValue()
 		this.close = jest.fn().mockResolvedValue()
 		this.start = jest.fn().mockResolvedValue()
@@ -10,5 +11,5 @@ class StreamableHttpClientTransport {
 }
 
 module.exports = {
-	StreamableHttpClientTransport,
+	StreamableHTTPClientTransport,
 }

+ 9 - 0
src/activate/handleUri.ts

@@ -1,11 +1,14 @@
 import * as vscode from "vscode"
 
+import { CloudService } from "@roo-code/cloud"
+
 import { ClineProvider } from "../core/webview/ClineProvider"
 
 export const handleUri = async (uri: vscode.Uri) => {
 	const path = uri.path
 	const query = new URLSearchParams(uri.query.replace(/\+/g, "%2B"))
 	const visibleProvider = ClineProvider.getVisibleInstance()
+
 	if (!visibleProvider) {
 		return
 	}
@@ -39,6 +42,12 @@ export const handleUri = async (uri: vscode.Uri) => {
 			}
 			break
 		}
+		case "/auth/clerk/callback": {
+			const code = query.get("code")
+			const state = query.get("state")
+			await CloudService.instance.handleAuthCallback(code, state)
+			break
+		}
 		default:
 			break
 	}

+ 22 - 0
src/activate/registerCommands.ts

@@ -70,6 +70,15 @@ export const registerCommands = (options: RegisterCommandOptions) => {
 
 const getCommandsMap = ({ context, outputChannel }: RegisterCommandOptions): Record<CommandId, any> => ({
 	activationCompleted: () => {},
+	accountButtonClicked: () => {
+		const visibleProvider = getVisibleProviderOrLog(outputChannel)
+
+		if (!visibleProvider) {
+			return
+		}
+
+		visibleProvider.postMessageToWebview({ type: "action", action: "accountButtonClicked" })
+	},
 	plusButtonClicked: async () => {
 		const visibleProvider = getVisibleProviderOrLog(outputChannel)
 
@@ -81,6 +90,19 @@ const getCommandsMap = ({ context, outputChannel }: RegisterCommandOptions): Rec
 		await visibleProvider.postStateToWebview()
 		await visibleProvider.postMessageToWebview({ type: "action", action: "chatButtonClicked" })
 	},
+
+	// kilocode_change: unused
+	// mcpButtonClicked: () => {
+	// 	const visibleProvider = getVisibleProviderOrLog(outputChannel)
+
+	// 	if (!visibleProvider) {
+	// 		return
+	// 	}
+
+	// 	TelemetryService.instance.captureTitleButtonClicked("mcp")
+
+	// 	visibleProvider.postMessageToWebview({ type: "action", action: "mcpButtonClicked" })
+	// },
 	promptsButtonClicked: () => {
 		const visibleProvider = getVisibleProviderOrLog(outputChannel)
 

+ 2 - 2
src/api/providers/__tests__/bedrock-custom-arn.test.ts

@@ -69,7 +69,7 @@ describe("Bedrock ARN Handling", () => {
 	describe("parseArn direct tests", () => {
 		it("should correctly extract modelType and modelId from foundation-model ARN", () => {
 			const handler = createHandler()
-			//note: properly formated foundation-model ARNs don't have an account id.
+			//note: properly formatted foundation-model ARNs don't have an account id.
 			const arn = "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-sonnet-20240229-v1:0"
 
 			// Access the private method using type casting
@@ -79,7 +79,7 @@ describe("Bedrock ARN Handling", () => {
 			expect(result.isValid).toBe(true)
 			expect(result.modelType).toBe("foundation-model")
 
-			//verify the id is not the ARN for foudation models, but the ID
+			//verify the id is not the ARN for foundation models, but the ID
 			expect(result.modelId).toBe("anthropic.claude-3-sonnet-20240229-v1:0")
 			expect(result.crossRegionInference).toBe(false)
 		})

+ 178 - 0
src/api/providers/__tests__/bedrock-vpc-endpoint.test.ts

@@ -0,0 +1,178 @@
+// Mock AWS SDK credential providers
+jest.mock("@aws-sdk/credential-providers", () => {
+	const mockFromIni = jest.fn().mockReturnValue({
+		accessKeyId: "profile-access-key",
+		secretAccessKey: "profile-secret-key",
+	})
+	return { fromIni: mockFromIni }
+})
+
+// Mock BedrockRuntimeClient and ConverseStreamCommand
+const mockBedrockRuntimeClient = jest.fn()
+const mockSend = jest.fn().mockResolvedValue({
+	stream: [],
+})
+
+jest.mock("@aws-sdk/client-bedrock-runtime", () => ({
+	BedrockRuntimeClient: mockBedrockRuntimeClient.mockImplementation(() => ({
+		send: mockSend,
+	})),
+	ConverseStreamCommand: jest.fn(),
+	ConverseCommand: jest.fn(),
+}))
+
+import { AwsBedrockHandler } from "../bedrock"
+
+describe("AWS Bedrock VPC Endpoint Functionality", () => {
+	beforeEach(() => {
+		// Clear all mocks before each test
+		jest.clearAllMocks()
+	})
+
+	// Test Scenario 1: Input Validation Test
+	describe("VPC Endpoint URL Validation", () => {
+		it("should configure client with endpoint URL when both URL and enabled flag are provided", () => {
+			// Create handler with endpoint URL and enabled flag
+			new AwsBedrockHandler({
+				apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
+				awsAccessKey: "test-access-key",
+				awsSecretKey: "test-secret-key",
+				awsRegion: "us-east-1",
+				awsBedrockEndpoint: "https://bedrock-vpc.example.com",
+				awsBedrockEndpointEnabled: true,
+			})
+
+			// Verify the client was created with the correct endpoint
+			expect(mockBedrockRuntimeClient).toHaveBeenCalledWith(
+				expect.objectContaining({
+					region: "us-east-1",
+					endpoint: "https://bedrock-vpc.example.com",
+				}),
+			)
+		})
+
+		it("should not configure client with endpoint URL when URL is provided but enabled flag is false", () => {
+			// Create handler with endpoint URL but disabled flag
+			new AwsBedrockHandler({
+				apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
+				awsAccessKey: "test-access-key",
+				awsSecretKey: "test-secret-key",
+				awsRegion: "us-east-1",
+				awsBedrockEndpoint: "https://bedrock-vpc.example.com",
+				awsBedrockEndpointEnabled: false,
+			})
+
+			// Verify the client was created without the endpoint
+			expect(mockBedrockRuntimeClient).toHaveBeenCalledWith(
+				expect.objectContaining({
+					region: "us-east-1",
+				}),
+			)
+
+			// Verify the endpoint property is not present
+			const clientConfig = mockBedrockRuntimeClient.mock.calls[0][0]
+			expect(clientConfig).not.toHaveProperty("endpoint")
+		})
+	})
+
+	// Test Scenario 2: Edge Case Tests
+	describe("Edge Cases", () => {
+		it("should handle empty endpoint URL gracefully", () => {
+			// Create handler with empty endpoint URL but enabled flag
+			new AwsBedrockHandler({
+				apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
+				awsAccessKey: "test-access-key",
+				awsSecretKey: "test-secret-key",
+				awsRegion: "us-east-1",
+				awsBedrockEndpoint: "",
+				awsBedrockEndpointEnabled: true,
+			})
+
+			// Verify the client was created without the endpoint (since it's empty)
+			expect(mockBedrockRuntimeClient).toHaveBeenCalledWith(
+				expect.objectContaining({
+					region: "us-east-1",
+				}),
+			)
+
+			// Verify the endpoint property is not present
+			const clientConfig = mockBedrockRuntimeClient.mock.calls[0][0]
+			expect(clientConfig).not.toHaveProperty("endpoint")
+		})
+
+		it("should handle undefined endpoint URL gracefully", () => {
+			// Create handler with undefined endpoint URL but enabled flag
+			new AwsBedrockHandler({
+				apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
+				awsAccessKey: "test-access-key",
+				awsSecretKey: "test-secret-key",
+				awsRegion: "us-east-1",
+				awsBedrockEndpoint: undefined,
+				awsBedrockEndpointEnabled: true,
+			})
+
+			// Verify the client was created without the endpoint
+			expect(mockBedrockRuntimeClient).toHaveBeenCalledWith(
+				expect.objectContaining({
+					region: "us-east-1",
+				}),
+			)
+
+			// Verify the endpoint property is not present
+			const clientConfig = mockBedrockRuntimeClient.mock.calls[0][0]
+			expect(clientConfig).not.toHaveProperty("endpoint")
+		})
+	})
+
+	// Test Scenario 4: Error Handling Tests
+	describe("Error Handling", () => {
+		it("should handle invalid endpoint URLs by passing them directly to AWS SDK", () => {
+			// Create handler with an invalid URL format
+			new AwsBedrockHandler({
+				apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
+				awsAccessKey: "test-access-key",
+				awsSecretKey: "test-secret-key",
+				awsRegion: "us-east-1",
+				awsBedrockEndpoint: "invalid-url-format",
+				awsBedrockEndpointEnabled: true,
+			})
+
+			// Verify the client was created with the invalid endpoint
+			// (AWS SDK will handle the validation/errors)
+			expect(mockBedrockRuntimeClient).toHaveBeenCalledWith(
+				expect.objectContaining({
+					region: "us-east-1",
+					endpoint: "invalid-url-format",
+				}),
+			)
+		})
+	})
+
+	// Test Scenario 5: Persistence Tests
+	describe("Persistence", () => {
+		it("should maintain consistent behavior across multiple requests", async () => {
+			// Create handler with endpoint URL and enabled flag
+			const handler = new AwsBedrockHandler({
+				apiModelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
+				awsAccessKey: "test-access-key",
+				awsSecretKey: "test-secret-key",
+				awsRegion: "us-east-1",
+				awsBedrockEndpoint: "https://bedrock-vpc.example.com",
+				awsBedrockEndpointEnabled: true,
+			})
+
+			// Reset mock to clear the constructor call
+			mockBedrockRuntimeClient.mockClear()
+
+			// Make a request
+			try {
+				await handler.completePrompt("Test prompt")
+			} catch (error) {
+				// Ignore errors, we're just testing the client configuration
+			}
+
+			// Verify the client was configured with the endpoint
+			expect(mockSend).toHaveBeenCalled()
+		})
+	})
+})

+ 1 - 1
src/api/providers/__tests__/chutes.test.ts

@@ -3,7 +3,7 @@
 import OpenAI from "openai"
 import { Anthropic } from "@anthropic-ai/sdk"
 
-import { ChutesModelId, chutesDefaultModelId, chutesModels } from "../../../shared/api"
+import { type ChutesModelId, chutesDefaultModelId, chutesModels } from "@roo-code/types"
 
 import { ChutesHandler } from "../chutes"
 

+ 6 - 3
src/api/providers/__tests__/deepseek.test.ts

@@ -1,9 +1,12 @@
-import { DeepSeekHandler } from "../deepseek"
-import { ApiHandlerOptions, deepSeekDefaultModelId } from "../../../shared/api"
 import OpenAI from "openai"
 import { Anthropic } from "@anthropic-ai/sdk"
 
-// Mock OpenAI client
+import { deepSeekDefaultModelId } from "@roo-code/types"
+
+import type { ApiHandlerOptions } from "../../../shared/api"
+
+import { DeepSeekHandler } from "../deepseek"
+
 const mockCreate = jest.fn()
 jest.mock("openai", () => {
 	return {

+ 1 - 2
src/api/providers/__tests__/gemini.test.ts

@@ -2,9 +2,8 @@
 
 import { Anthropic } from "@anthropic-ai/sdk"
 
-import type { ModelInfo } from "@roo-code/types"
+import { type ModelInfo, geminiDefaultModelId } from "@roo-code/types"
 
-import { geminiDefaultModelId } from "../../../shared/api"
 import { GeminiHandler } from "../gemini"
 
 const GEMINI_20_FLASH_THINKING_NAME = "gemini-2.0-flash-thinking-exp-1219"

+ 1 - 1
src/api/providers/__tests__/groq.test.ts

@@ -3,7 +3,7 @@
 import OpenAI from "openai"
 import { Anthropic } from "@anthropic-ai/sdk"
 
-import { GroqModelId, groqDefaultModelId, groqModels } from "../../../shared/api"
+import { type GroqModelId, groqDefaultModelId, groqModels } from "@roo-code/types"
 
 import { GroqHandler } from "../groq"
 

+ 1 - 1
src/api/providers/__tests__/lmstudio.test.ts

@@ -1,6 +1,6 @@
 import { Anthropic } from "@anthropic-ai/sdk"
 
-import { LmStudioHandler } from "../lmstudio"
+import { LmStudioHandler } from "../lm-studio"
 import { ApiHandlerOptions } from "../../../shared/api"
 
 // Mock OpenAI client

+ 4 - 3
src/api/providers/__tests__/xai.test.ts

@@ -1,9 +1,10 @@
-import { XAIHandler } from "../xai"
-import { xaiDefaultModelId, xaiModels } from "../../../shared/api"
 import OpenAI from "openai"
 import { Anthropic } from "@anthropic-ai/sdk"
 
-// Mock OpenAI client
+import { xaiDefaultModelId, xaiModels } from "@roo-code/types"
+
+import { XAIHandler } from "../xai"
+
 jest.mock("openai", () => {
 	const createMock = jest.fn()
 	return jest.fn(() => ({

+ 9 - 4
src/api/providers/anthropic-vertex.ts

@@ -2,16 +2,21 @@ import { Anthropic } from "@anthropic-ai/sdk"
 import { AnthropicVertex } from "@anthropic-ai/vertex-sdk"
 import { GoogleAuth, JWTInput } from "google-auth-library"
 
-import type { ModelInfo } from "@roo-code/types"
-
-import { ApiHandlerOptions, vertexDefaultModelId, VertexModelId, vertexModels } from "../../shared/api"
+import {
+	type ModelInfo,
+	type VertexModelId,
+	vertexDefaultModelId,
+	vertexModels,
+	ANTHROPIC_DEFAULT_MAX_TOKENS,
+} from "@roo-code/types"
+
+import { ApiHandlerOptions } from "../../shared/api"
 import { safeJsonParse } from "../../shared/safeJsonParse"
 
 import { ApiStream } from "../transform/stream"
 import { addCacheBreakpoints } from "../transform/caching/vertex"
 import { getModelParams } from "../transform/model-params"
 
-import { ANTHROPIC_DEFAULT_MAX_TOKENS } from "./constants"
 import { BaseProvider } from "./base-provider"
 import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index"
 

+ 8 - 3
src/api/providers/anthropic.ts

@@ -2,14 +2,19 @@ import { Anthropic } from "@anthropic-ai/sdk"
 import { Stream as AnthropicStream } from "@anthropic-ai/sdk/streaming"
 import { CacheControlEphemeral } from "@anthropic-ai/sdk/resources"
 
-import type { ModelInfo } from "@roo-code/types"
+import {
+	type ModelInfo,
+	type AnthropicModelId,
+	anthropicDefaultModelId,
+	anthropicModels,
+	ANTHROPIC_DEFAULT_MAX_TOKENS,
+} from "@roo-code/types"
 
-import { anthropicDefaultModelId, AnthropicModelId, anthropicModels, ApiHandlerOptions } from "../../shared/api"
+import type { ApiHandlerOptions } from "../../shared/api"
 
 import { ApiStream } from "../transform/stream"
 import { getModelParams } from "../transform/model-params"
 
-import { ANTHROPIC_DEFAULT_MAX_TOKENS } from "./constants"
 import { BaseProvider } from "./base-provider"
 import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index"
 

+ 1 - 0
src/api/providers/base-provider.ts

@@ -15,6 +15,7 @@ export abstract class BaseProvider implements ApiHandler {
 		messages: Anthropic.Messages.MessageParam[],
 		metadata?: ApiHandlerCreateMessageMetadata,
 	): ApiStream
+
 	abstract getModel(): { id: string; info: ModelInfo }
 
 	/**

+ 15 - 12
src/api/providers/bedrock.ts

@@ -10,26 +10,26 @@ import {
 import { fromIni } from "@aws-sdk/credential-providers"
 import { Anthropic } from "@anthropic-ai/sdk"
 
-import type { ModelInfo, ProviderSettings } from "@roo-code/types"
-
 import {
-	BedrockModelId,
+	type ModelInfo,
+	type ProviderSettings,
+	type BedrockModelId,
 	bedrockDefaultModelId,
 	bedrockModels,
 	bedrockDefaultPromptRouterModelId,
-} from "../../shared/api"
+	BEDROCK_DEFAULT_TEMPERATURE,
+	BEDROCK_MAX_TOKENS,
+	BEDROCK_REGION_INFO,
+} from "@roo-code/types"
+
 import { ApiStream } from "../transform/stream"
 import { BaseProvider } from "./base-provider"
 import { logger } from "../../utils/logging"
 import { MultiPointStrategy } from "../transform/cache-strategy/multi-point-strategy"
 import { ModelInfo as CacheModelInfo } from "../transform/cache-strategy/types"
-import { AMAZON_BEDROCK_REGION_INFO } from "../../shared/aws_regions"
 import { convertToBedrockConverseMessages as sharedConverter } from "../transform/bedrock-converse-format"
 import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index"
 
-const BEDROCK_DEFAULT_TEMPERATURE = 0.3
-const BEDROCK_MAX_TOKENS = 4096
-
 /************************************************************************************
  *
  *     TYPES
@@ -124,7 +124,7 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
 		let region = this.options.awsRegion
 
 		// process the various user input options, be opinionated about the intent of the options
-		// and determine the model to use during inference and for cost caclulations
+		// and determine the model to use during inference and for cost calculations
 		// There are variations on ARN strings that can be entered making the conditional logic
 		// more involved than the non-ARN branch of logic
 		if (this.options.awsCustomArn) {
@@ -169,6 +169,9 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
 
 		const clientConfig: BedrockRuntimeClientConfig = {
 			region: this.options.awsRegion,
+			// Add the endpoint configuration when specified and enabled
+			...(this.options.awsBedrockEndpoint &&
+				this.options.awsBedrockEndpointEnabled && { endpoint: this.options.awsBedrockEndpoint }),
 		}
 
 		if (this.options.awsUseProfile && this.options.awsProfile) {
@@ -729,11 +732,11 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
 	 *************************************************************************************/
 
 	private static getPrefixList(): string[] {
-		return Object.keys(AMAZON_BEDROCK_REGION_INFO)
+		return Object.keys(BEDROCK_REGION_INFO)
 	}
 
 	private static getPrefixForRegion(region: string): string | undefined {
-		for (const [prefix, info] of Object.entries(AMAZON_BEDROCK_REGION_INFO)) {
+		for (const [prefix, info] of Object.entries(BEDROCK_REGION_INFO)) {
 			if (info.pattern && region.startsWith(info.pattern)) {
 				return prefix
 			}
@@ -742,7 +745,7 @@ export class AwsBedrockHandler extends BaseProvider implements SingleCompletionH
 	}
 
 	private static prefixIsMultiRegion(arnPrefix: string): boolean {
-		for (const [prefix, info] of Object.entries(AMAZON_BEDROCK_REGION_INFO)) {
+		for (const [prefix, info] of Object.entries(BEDROCK_REGION_INFO)) {
 			if (arnPrefix === prefix) {
 				if (info?.multiRegion) return info.multiRegion
 				else return false

+ 3 - 1
src/api/providers/chutes.ts

@@ -1,4 +1,6 @@
-import { ApiHandlerOptions, ChutesModelId, chutesDefaultModelId, chutesModels } from "../../shared/api"
+import { type ChutesModelId, chutesDefaultModelId, chutesModels } from "@roo-code/types"
+
+import type { ApiHandlerOptions } from "../../shared/api"
 
 import { BaseOpenAiCompatibleProvider } from "./base-openai-compatible-provider"
 

+ 0 - 4
src/api/providers/constants.ts

@@ -2,7 +2,3 @@ export const DEFAULT_HEADERS = {
 	"HTTP-Referer": "https://kilocode.ai",
 	"X-Title": "Kilo Code",
 }
-
-export const ANTHROPIC_DEFAULT_MAX_TOKENS = 8192
-
-export const DEEP_SEEK_DEFAULT_TEMPERATURE = 0.6

+ 2 - 1
src/api/providers/deepseek.ts

@@ -1,4 +1,5 @@
-import { deepSeekModels, deepSeekDefaultModelId } from "../../shared/api"
+import { deepSeekModels, deepSeekDefaultModelId } from "@roo-code/types"
+
 import type { ApiHandlerOptions } from "../../shared/api"
 
 import type { ApiStreamUsageChunk } from "../transform/stream"

+ 2 - 2
src/api/providers/fetchers/__tests__/openrouter.spec.ts

@@ -1,4 +1,4 @@
-// npx vitest run --globals api/providers/fetchers/__tests__/openrouter.spec.ts
+// npx vitest run api/providers/fetchers/__tests__/openrouter.spec.ts
 
 import * as path from "path"
 
@@ -9,7 +9,7 @@ import {
 	OPEN_ROUTER_COMPUTER_USE_MODELS,
 	OPEN_ROUTER_REASONING_BUDGET_MODELS,
 	OPEN_ROUTER_REQUIRED_REASONING_BUDGET_MODELS,
-} from "../../../../shared/api"
+} from "@roo-code/types"
 
 import { getOpenRouterModelEndpoints, getOpenRouterModels } from "../openrouter"
 

+ 9 - 1
src/api/providers/fetchers/litellm.ts

@@ -1,6 +1,8 @@
 import axios from "axios"
 
-import { LITELLM_COMPUTER_USE_MODELS, ModelRecord } from "../../../shared/api"
+import { LITELLM_COMPUTER_USE_MODELS } from "@roo-code/types"
+
+import type { ModelRecord } from "../../../shared/api"
 
 /**
  * Fetches available models from a LiteLLM server
@@ -56,6 +58,12 @@ export async function getLiteLLMModels(apiKey: string, baseUrl: string): Promise
 					outputPrice: modelInfo.output_cost_per_token
 						? modelInfo.output_cost_per_token * 1000000
 						: undefined,
+					cacheWritesPrice: modelInfo.cache_creation_input_token_cost
+						? modelInfo.cache_creation_input_token_cost * 1000000
+						: undefined,
+					cacheReadsPrice: modelInfo.cache_read_input_token_cost
+						? modelInfo.cache_read_input_token_cost * 1000000
+						: undefined,
 					description: `${modelName} via LiteLLM proxy`,
 				}
 			}

+ 5 - 4
src/api/providers/fetchers/openrouter.ts

@@ -1,15 +1,16 @@
 import axios, { type RawAxiosRequestHeaders /*kilocode_change*/ } from "axios"
 import { z } from "zod"
 
-import { type ModelInfo, isModelParameter } from "@roo-code/types"
-
 import {
-	ApiHandlerOptions,
+	type ModelInfo,
+	isModelParameter,
 	OPEN_ROUTER_COMPUTER_USE_MODELS,
 	OPEN_ROUTER_REASONING_BUDGET_MODELS,
 	OPEN_ROUTER_REQUIRED_REASONING_BUDGET_MODELS,
 	anthropicModels,
-} from "../../../shared/api"
+} from "@roo-code/types"
+
+import type { ApiHandlerOptions } from "../../../shared/api"
 import { parseApiPrice } from "../../../shared/cost"
 
 /**

+ 2 - 2
src/api/providers/gemini.ts

@@ -7,9 +7,9 @@ import {
 } from "@google/genai"
 import type { JWTInput } from "google-auth-library"
 
-import type { ModelInfo } from "@roo-code/types"
+import { type ModelInfo, type GeminiModelId, geminiDefaultModelId, geminiModels } from "@roo-code/types"
 
-import { ApiHandlerOptions, GeminiModelId, geminiDefaultModelId, geminiModels } from "../../shared/api"
+import type { ApiHandlerOptions } from "../../shared/api"
 import { safeJsonParse } from "../../shared/safeJsonParse"
 
 import { convertAnthropicContentToGemini, convertAnthropicMessageToGemini } from "../transform/gemini-format"

+ 3 - 3
src/api/providers/glama.ts

@@ -2,8 +2,10 @@ import { Anthropic } from "@anthropic-ai/sdk"
 import axios from "axios"
 import OpenAI from "openai"
 
+import { glamaDefaultModelId, glamaDefaultModelInfo, GLAMA_DEFAULT_TEMPERATURE } from "@roo-code/types"
+
 import { Package } from "../../shared/package"
-import { ApiHandlerOptions, glamaDefaultModelId, glamaDefaultModelInfo } from "../../shared/api"
+import { ApiHandlerOptions } from "../../shared/api"
 
 import { ApiStream } from "../transform/stream"
 import { convertToOpenAiMessages } from "../transform/openai-format"
@@ -12,8 +14,6 @@ import { addCacheBreakpoints } from "../transform/caching/anthropic"
 import type { SingleCompletionHandler, ApiHandlerCreateMessageMetadata } from "../index"
 import { RouterProvider } from "./router-provider"
 
-const GLAMA_DEFAULT_TEMPERATURE = 0
-
 const DEFAULT_HEADERS = {
 	"X-Glama-Metadata": JSON.stringify({
 		labels: [{ key: "app", value: `vscode.${Package.publisher}.${Package.name}` }],

Некоторые файлы не были показаны из-за большого количества измененных файлов