Skip to content

Commit 0978ba3

Browse files
feat: Cloud agents in extension (#8470)
Co-authored-by: Matt Rubens <[email protected]>
1 parent 6d6b836 commit 0978ba3

Some content is hidden

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

43 files changed

+1181
-479
lines changed

packages/cloud/src/CloudAPI.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import { z } from "zod"
22

3-
import { type AuthService, type ShareVisibility, type ShareResponse, shareResponseSchema } from "@roo-code/types"
3+
import {
4+
type AuthService,
5+
type ShareVisibility,
6+
type ShareResponse,
7+
shareResponseSchema,
8+
type CloudAgent,
9+
cloudAgentsResponseSchema,
10+
} from "@roo-code/types"
411

512
import { getRooCodeApiUrl } from "./config.js"
613
import { getUserAgent } from "./utils.js"
@@ -134,4 +141,16 @@ export class CloudAPI {
134141
.parse(data),
135142
})
136143
}
144+
145+
async getCloudAgents(): Promise<CloudAgent[]> {
146+
this.log("[CloudAPI] Fetching cloud agents")
147+
148+
const agents = await this.request<CloudAgent[]>("/api/cloud-agents", {
149+
method: "GET",
150+
parseResponse: (data) => cloudAgentsResponseSchema.parse(data).data,
151+
})
152+
153+
this.log("[CloudAPI] Cloud agents response:", agents)
154+
return agents
155+
}
137156
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { describe, it, expect, vi, beforeEach } from "vitest"
2+
import { CloudAPI } from "../CloudAPI.js"
3+
import { AuthenticationError } from "../errors.js"
4+
import type { AuthService } from "@roo-code/types"
5+
6+
// Mock fetch globally
7+
global.fetch = vi.fn()
8+
9+
describe("CloudAPI", () => {
10+
let mockAuthService: Partial<AuthService>
11+
let cloudAPI: CloudAPI
12+
13+
beforeEach(() => {
14+
// Mock only the methods we need for testing
15+
mockAuthService = {
16+
getSessionToken: vi.fn().mockReturnValue("test-token"),
17+
}
18+
19+
cloudAPI = new CloudAPI(mockAuthService as AuthService)
20+
vi.clearAllMocks()
21+
})
22+
23+
describe("getCloudAgents", () => {
24+
it("should return cloud agents on success", async () => {
25+
const mockAgents = [
26+
{ id: "1", name: "Agent 1", type: "code", icon: "code" },
27+
{ id: "2", name: "Agent 2", type: "chat", icon: "chat" },
28+
]
29+
30+
// Mock successful response with schema-compliant format
31+
;(global.fetch as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
32+
ok: true,
33+
json: async () => ({ success: true, data: mockAgents }),
34+
})
35+
36+
const agents = await cloudAPI.getCloudAgents()
37+
38+
expect(agents).toEqual(mockAgents)
39+
expect(global.fetch).toHaveBeenCalledWith(
40+
expect.stringContaining("/api/cloud-agents"),
41+
expect.objectContaining({
42+
method: "GET",
43+
headers: expect.objectContaining({
44+
Authorization: "Bearer test-token",
45+
}),
46+
}),
47+
)
48+
})
49+
50+
it("should throw AuthenticationError on 401 response", async () => {
51+
// Mock 401 response
52+
;(global.fetch as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
53+
ok: false,
54+
status: 401,
55+
statusText: "Unauthorized",
56+
json: async () => ({ error: "Authentication required" }),
57+
})
58+
59+
await expect(cloudAPI.getCloudAgents()).rejects.toThrow(AuthenticationError)
60+
})
61+
62+
it("should throw AuthenticationError when no session token", async () => {
63+
// Mock no session token
64+
mockAuthService.getSessionToken = vi.fn().mockReturnValue(null)
65+
66+
await expect(cloudAPI.getCloudAgents()).rejects.toThrow(AuthenticationError)
67+
})
68+
69+
it("should return empty array when agents array is empty", async () => {
70+
// Mock response with empty agents array
71+
;(global.fetch as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
72+
ok: true,
73+
json: async () => ({ success: true, data: [] }),
74+
})
75+
76+
const agents = await cloudAPI.getCloudAgents()
77+
78+
expect(agents).toEqual([])
79+
})
80+
})
81+
})

packages/types/src/cloud.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,35 @@ export type LeaveResponse = {
723723
timestamp?: string
724724
}
725725

726+
/**
727+
* CloudAgent
728+
*/
729+
730+
export interface CloudAgent {
731+
id: string
732+
name: string
733+
type: string // e.g., "PR Reviewer", "Documentation Writer"
734+
icon?: string // e.g., "pr-reviewer", "documentation-writer"
735+
}
736+
737+
/**
738+
* CloudAgents API Response
739+
*/
740+
741+
export const cloudAgentsResponseSchema = z.object({
742+
success: z.boolean(),
743+
data: z.array(
744+
z.object({
745+
id: z.string(),
746+
name: z.string(),
747+
type: z.string(),
748+
icon: z.string().optional(),
749+
}),
750+
),
751+
})
752+
753+
export type CloudAgentsResponse = z.infer<typeof cloudAgentsResponseSchema>
754+
726755
/**
727756
* UsageStats
728757
*/

packages/types/src/providers/chutes.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ export const chutesModels = {
8787
supportsPromptCache: false,
8888
inputPrice: 0.23,
8989
outputPrice: 0.9,
90-
description: "DeepSeek‑V3.1‑Terminus is an update to V3.1 that improves language consistency by reducing CN/EN mix‑ups and eliminating random characters, while strengthening agent capabilities with notably better Code Agent and Search Agent performance.",
90+
description:
91+
"DeepSeek‑V3.1‑Terminus is an update to V3.1 that improves language consistency by reducing CN/EN mix‑ups and eliminating random characters, while strengthening agent capabilities with notably better Code Agent and Search Agent performance.",
9192
},
9293
"deepseek-ai/DeepSeek-V3.1-turbo": {
9394
maxTokens: 32768,
@@ -96,7 +97,8 @@ export const chutesModels = {
9697
supportsPromptCache: false,
9798
inputPrice: 1.0,
9899
outputPrice: 3.0,
99-
description: "DeepSeek-V3.1-turbo is an FP8, speculative-decoding turbo variant optimized for ultra-fast single-shot queries (~200 TPS), with outputs close to the originals and solid function calling/reasoning/structured output, priced at $1/M input and $3/M output tokens, using 2× quota per request and not intended for bulk workloads.",
100+
description:
101+
"DeepSeek-V3.1-turbo is an FP8, speculative-decoding turbo variant optimized for ultra-fast single-shot queries (~200 TPS), with outputs close to the originals and solid function calling/reasoning/structured output, priced at $1/M input and $3/M output tokens, using 2× quota per request and not intended for bulk workloads.",
100102
},
101103
"deepseek-ai/DeepSeek-V3.2-Exp": {
102104
maxTokens: 163840,
@@ -105,7 +107,8 @@ export const chutesModels = {
105107
supportsPromptCache: false,
106108
inputPrice: 0.25,
107109
outputPrice: 0.35,
108-
description: "DeepSeek-V3.2-Exp is an experimental LLM that introduces DeepSeek Sparse Attention to improve long‑context training and inference efficiency while maintaining performance comparable to V3.1‑Terminus.",
110+
description:
111+
"DeepSeek-V3.2-Exp is an experimental LLM that introduces DeepSeek Sparse Attention to improve long‑context training and inference efficiency while maintaining performance comparable to V3.1‑Terminus.",
109112
},
110113
"unsloth/Llama-3.3-70B-Instruct": {
111114
maxTokens: 32768, // From Groq
@@ -387,8 +390,9 @@ export const chutesModels = {
387390
contextWindow: 262144,
388391
supportsImages: true,
389392
supportsPromptCache: false,
390-
inputPrice: 0.1600,
391-
outputPrice: 0.6500,
392-
description: "Qwen3‑VL‑235B‑A22B‑Thinking is an open‑weight MoE vision‑language model (235B total, ~22B activated) optimized for deliberate multi‑step reasoning with strong text‑image‑video understanding and long‑context capabilities.",
393+
inputPrice: 0.16,
394+
outputPrice: 0.65,
395+
description:
396+
"Qwen3‑VL‑235B‑A22B‑Thinking is an open‑weight MoE vision‑language model (235B total, ~22B activated) optimized for deliberate multi‑step reasoning with strong text‑image‑video understanding and long‑context capabilities.",
393397
},
394398
} as const satisfies Record<string, ModelInfo>

src/core/webview/ClineProvider.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,12 +1034,11 @@ export class ClineProvider
10341034
window.__vite_plugin_react_preamble_installed__ = true
10351035
</script>
10361036
`
1037-
10381037
const csp = [
10391038
"default-src 'none'",
10401039
`font-src ${webview.cspSource} data:`,
10411040
`style-src ${webview.cspSource} 'unsafe-inline' https://* http://${localServerUrl} http://0.0.0.0:${localPort}`,
1042-
`img-src ${webview.cspSource} https://storage.googleapis.com https://img.clerk.com data:`,
1041+
`img-src ${webview.cspSource} https://storage.googleapis.com https://img.clerk.com ${getRooCodeApiUrl()} data:`,
10431042
`media-src ${webview.cspSource}`,
10441043
`script-src 'unsafe-eval' ${webview.cspSource} https://* https://*.posthog.com http://${localServerUrl} http://0.0.0.0:${localPort} 'nonce-${nonce}'`,
10451044
`connect-src ${webview.cspSource} https://* https://*.posthog.com ws://${localServerUrl} ws://0.0.0.0:${localPort} http://${localServerUrl} http://0.0.0.0:${localPort}`,
@@ -1124,7 +1123,7 @@ export class ClineProvider
11241123
<meta charset="utf-8">
11251124
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
11261125
<meta name="theme-color" content="#000000">
1127-
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; font-src ${webview.cspSource} data:; style-src ${webview.cspSource} 'unsafe-inline'; img-src ${webview.cspSource} https://storage.googleapis.com https://img.clerk.com data:; media-src ${webview.cspSource}; script-src ${webview.cspSource} 'wasm-unsafe-eval' 'nonce-${nonce}' https://us-assets.i.posthog.com 'strict-dynamic'; connect-src ${webview.cspSource} https://openrouter.ai https://api.requesty.ai https://us.i.posthog.com https://us-assets.i.posthog.com;">
1126+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; font-src ${webview.cspSource} data:; style-src ${webview.cspSource} 'unsafe-inline'; img-src ${webview.cspSource} https://storage.googleapis.com https://img.clerk.com ${getRooCodeApiUrl()} data:; media-src ${webview.cspSource}; script-src ${webview.cspSource} 'wasm-unsafe-eval' 'nonce-${nonce}' https://us-assets.i.posthog.com 'strict-dynamic'; connect-src ${webview.cspSource} https://openrouter.ai https://api.requesty.ai https://us.i.posthog.com https://us-assets.i.posthog.com;">
11281127
<link rel="stylesheet" type="text/css" href="${stylesUri}">
11291128
<link href="${codiconsUri}" rel="stylesheet" />
11301129
<script nonce="${nonce}">

0 commit comments

Comments
 (0)