Skip to content

Commit e45f515

Browse files
author
aheizi
committed
Merge branch 'main' into feature/add_sse_mcp
2 parents f9cc13c + 0fafe06 commit e45f515

37 files changed

+2275
-1506
lines changed

e2e/src/suite/task.test.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as assert from "assert"
2+
import * as vscode from "vscode"
23

34
suite("Roo Code Task", () => {
45
test("Should handle prompt and response correctly", async function () {
@@ -48,4 +49,113 @@ suite("Roo Code Task", () => {
4849
"Did not receive expected response containing 'My name is Roo'",
4950
)
5051
})
52+
53+
test("Should handle subtask cancellation and resumption correctly", async function () {
54+
this.timeout(60000) // Increase timeout for this test
55+
const interval = 1000
56+
57+
if (!globalThis.extension) {
58+
assert.fail("Extension not found")
59+
}
60+
61+
// Ensure the webview is launched
62+
await ensureWebviewLaunched(30000, interval)
63+
64+
// Set up required global state
65+
await globalThis.provider.updateGlobalState("mode", "Code")
66+
await globalThis.provider.updateGlobalState("alwaysAllowModeSwitch", true)
67+
await globalThis.provider.updateGlobalState("alwaysAllowSubtasks", true)
68+
await globalThis.provider.updateGlobalState("autoApprovalEnabled", true)
69+
70+
// 1. Start a parent task that will create a subtask
71+
await globalThis.api.startNewTask(
72+
"You are the parent task. Create a subtask by using the new_task tool with the message 'You are the subtask'. " +
73+
"After creating the subtask, wait for it to complete and then respond with 'Parent task resumed'.",
74+
)
75+
76+
// Wait for the parent task to use the new_task tool
77+
await waitForToolUse("new_task", 30000, interval)
78+
79+
// Wait for the subtask to be created and start responding
80+
await waitForMessage("You are the subtask", 10000, interval)
81+
82+
// 3. Cancel the current task (which should be the subtask)
83+
await globalThis.provider.cancelTask()
84+
85+
// 4. Check if the parent task is still waiting (not resumed)
86+
// We need to wait a bit to ensure any task resumption would have happened
87+
await new Promise((resolve) => setTimeout(resolve, 5000))
88+
89+
// The parent task should not have resumed yet, so we shouldn't see "Parent task resumed"
90+
assert.ok(
91+
!globalThis.provider.messages.some(
92+
({ type, text }) => type === "say" && text?.includes("Parent task resumed"),
93+
),
94+
"Parent task should not have resumed after subtask cancellation",
95+
)
96+
97+
// 5. Start a new task with the same message as the subtask
98+
await globalThis.api.startNewTask("You are the subtask")
99+
100+
// Wait for the subtask to complete
101+
await waitForMessage("Task complete", 20000, interval)
102+
103+
// 6. Verify that the parent task is still not resumed
104+
// We need to wait a bit to ensure any task resumption would have happened
105+
await new Promise((resolve) => setTimeout(resolve, 5000))
106+
107+
// The parent task should still not have resumed
108+
assert.ok(
109+
!globalThis.provider.messages.some(
110+
({ type, text }) => type === "say" && text?.includes("Parent task resumed"),
111+
),
112+
"Parent task should not have resumed after subtask completion",
113+
)
114+
115+
// Clean up - cancel all tasks
116+
await globalThis.provider.cancelTask()
117+
})
51118
})
119+
120+
// Helper functions
121+
async function ensureWebviewLaunched(timeout: number, interval: number): Promise<void> {
122+
const startTime = Date.now()
123+
while (Date.now() - startTime < timeout) {
124+
if (globalThis.provider.viewLaunched) {
125+
return
126+
}
127+
await new Promise((resolve) => setTimeout(resolve, interval))
128+
}
129+
throw new Error("Webview failed to launch within timeout")
130+
}
131+
132+
async function waitForToolUse(toolName: string, timeout: number, interval: number): Promise<void> {
133+
const startTime = Date.now()
134+
while (Date.now() - startTime < timeout) {
135+
const messages = globalThis.provider.messages
136+
if (
137+
messages.some(
138+
(message) =>
139+
message.type === "say" && message.say === "tool" && message.text && message.text.includes(toolName),
140+
)
141+
) {
142+
return
143+
}
144+
await new Promise((resolve) => setTimeout(resolve, interval))
145+
}
146+
throw new Error(`Tool ${toolName} was not used within timeout`)
147+
}
148+
149+
async function waitForMessage(messageContent: string, timeout: number, interval: number): Promise<void> {
150+
const startTime = Date.now()
151+
while (Date.now() - startTime < timeout) {
152+
const messages = globalThis.provider.messages
153+
if (
154+
messages.some((message) => message.type === "say" && message.text && message.text.includes(messageContent))
155+
) {
156+
return
157+
}
158+
await new Promise((resolve) => setTimeout(resolve, interval))
159+
}
160+
throw new Error(`Message containing "${messageContent}" not found within timeout`)
161+
}

package.json

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,31 @@
128128
"command": "roo-cline.addToContext",
129129
"title": "Roo Code: Add To Context",
130130
"category": "Roo Code"
131+
},
132+
{
133+
"command": "roo-cline.terminalAddToContext",
134+
"title": "Roo Code: Add Terminal Content to Context",
135+
"category": "Terminal"
136+
},
137+
{
138+
"command": "roo-cline.terminalFixCommand",
139+
"title": "Roo Code: Fix This Command",
140+
"category": "Terminal"
141+
},
142+
{
143+
"command": "roo-cline.terminalExplainCommand",
144+
"title": "Roo Code: Explain This Command",
145+
"category": "Terminal"
146+
},
147+
{
148+
"command": "roo-cline.terminalFixCommandInCurrentTask",
149+
"title": "Roo Code: Fix This Command (Current Task)",
150+
"category": "Terminal"
151+
},
152+
{
153+
"command": "roo-cline.terminalExplainCommandInCurrentTask",
154+
"title": "Roo Code: Explain This Command (Current Task)",
155+
"category": "Terminal"
131156
}
132157
],
133158
"menus": {
@@ -153,6 +178,28 @@
153178
"group": "Roo Code@4"
154179
}
155180
],
181+
"terminal/context": [
182+
{
183+
"command": "roo-cline.terminalAddToContext",
184+
"group": "Roo Code@1"
185+
},
186+
{
187+
"command": "roo-cline.terminalFixCommand",
188+
"group": "Roo Code@2"
189+
},
190+
{
191+
"command": "roo-cline.terminalExplainCommand",
192+
"group": "Roo Code@3"
193+
},
194+
{
195+
"command": "roo-cline.terminalFixCommandInCurrentTask",
196+
"group": "Roo Code@5"
197+
},
198+
{
199+
"command": "roo-cline.terminalExplainCommandInCurrentTask",
200+
"group": "Roo Code@6"
201+
}
202+
],
156203
"view/title": [
157204
{
158205
"command": "roo-cline.plusButtonClicked",

src/activate/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export { handleUri } from "./handleUri"
22
export { registerCommands } from "./registerCommands"
33
export { registerCodeActions } from "./registerCodeActions"
44
export { createRooCodeAPI } from "./createRooCodeAPI"
5+
export { registerTerminalActions } from "./registerTerminalActions"
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import * as vscode from "vscode"
2+
import { ClineProvider } from "../core/webview/ClineProvider"
3+
import { Terminal } from "../integrations/terminal/Terminal"
4+
5+
const TERMINAL_COMMAND_IDS = {
6+
ADD_TO_CONTEXT: "roo-cline.terminalAddToContext",
7+
FIX: "roo-cline.terminalFixCommand",
8+
FIX_IN_CURRENT_TASK: "roo-cline.terminalFixCommandInCurrentTask",
9+
EXPLAIN: "roo-cline.terminalExplainCommand",
10+
EXPLAIN_IN_CURRENT_TASK: "roo-cline.terminalExplainCommandInCurrentTask",
11+
} as const
12+
13+
export const registerTerminalActions = (context: vscode.ExtensionContext) => {
14+
registerTerminalAction(context, TERMINAL_COMMAND_IDS.ADD_TO_CONTEXT, "TERMINAL_ADD_TO_CONTEXT")
15+
16+
registerTerminalActionPair(context, TERMINAL_COMMAND_IDS.FIX, "TERMINAL_FIX", "What would you like Roo to fix?")
17+
18+
registerTerminalActionPair(
19+
context,
20+
TERMINAL_COMMAND_IDS.EXPLAIN,
21+
"TERMINAL_EXPLAIN",
22+
"What would you like Roo to explain?",
23+
)
24+
}
25+
26+
const registerTerminalAction = (
27+
context: vscode.ExtensionContext,
28+
command: string,
29+
promptType: "TERMINAL_ADD_TO_CONTEXT" | "TERMINAL_FIX" | "TERMINAL_EXPLAIN",
30+
inputPrompt?: string,
31+
) => {
32+
context.subscriptions.push(
33+
vscode.commands.registerCommand(command, async (args: any) => {
34+
let content = args.selection
35+
if (!content || content === "") {
36+
content = await Terminal.getTerminalContents(promptType === "TERMINAL_ADD_TO_CONTEXT" ? -1 : 1)
37+
}
38+
39+
if (!content) {
40+
vscode.window.showWarningMessage("No terminal content selected")
41+
return
42+
}
43+
44+
const params: Record<string, any> = {
45+
terminalContent: content,
46+
}
47+
48+
if (inputPrompt) {
49+
params.userInput =
50+
(await vscode.window.showInputBox({
51+
prompt: inputPrompt,
52+
})) ?? ""
53+
}
54+
55+
await ClineProvider.handleTerminalAction(command, promptType, params)
56+
}),
57+
)
58+
}
59+
60+
const registerTerminalActionPair = (
61+
context: vscode.ExtensionContext,
62+
baseCommand: string,
63+
promptType: "TERMINAL_ADD_TO_CONTEXT" | "TERMINAL_FIX" | "TERMINAL_EXPLAIN",
64+
inputPrompt?: string,
65+
) => {
66+
// Register new task version
67+
registerTerminalAction(context, baseCommand, promptType, inputPrompt)
68+
// Register current task version
69+
registerTerminalAction(context, `${baseCommand}InCurrentTask`, promptType, inputPrompt)
70+
}

src/api/providers/openrouter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ export class OpenRouterHandler extends BaseProvider implements SingleCompletionH
162162
await delay(200) // FIXME: necessary delay to ensure generation endpoint is ready
163163

164164
try {
165-
const response = await axios.get(`https://openrouter.ai/api/v1/generation?id=${genId}`, {
165+
const response = await axios.get(`${this.client.baseURL}/generation?id=${genId}`, {
166166
headers: {
167167
Authorization: `Bearer ${this.options.openRouterApiKey}`,
168168
},

0 commit comments

Comments
 (0)