Add a mode switched event, better mode switch e2e test (#2237)

This commit is contained in:
Chris Estreich 2025-04-02 14:25:11 -07:00 • committed by GitHub
parent c4af8cadbd
commit 42f7465666
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 30 additions and 32 deletions

View file

@ -13,11 +13,17 @@ suite("Roo Code Modes", () => {
*/
const switchModesPrompt =
"For each mode (Code, Architect, Ask) respond with the mode name and what it specializes in after switching to that mode. " +
"Do not start with the current mode."
"For each mode (Architect, Ask, Debug) respond with the mode name and what it specializes in after switching to that mode."
let messages: ClineMessage[] = []
const modeSwitches: string[] = []
api.on("taskModeSwitched", (_taskId, mode) => {
console.log("taskModeSwitched", mode)
modeSwitches.push(mode)
})
api.on("message", ({ message }) => {
if (message.type === "say" && message.partial === false) {
messages.push(message)
@ -25,36 +31,15 @@ suite("Roo Code Modes", () => {
})
const switchModesTaskId = await api.startNewTask({
configuration: { mode: "Code", alwaysAllowModeSwitch: true, autoApprovalEnabled: true },
configuration: { mode: "code", alwaysAllowModeSwitch: true, autoApprovalEnabled: true },
text: switchModesPrompt,
})
await waitUntilCompleted({ api, taskId: switchModesTaskId, timeout: 60_000 })
/**
* Grade the response.
*/
const response = messages
.filter(({ type, say, partial }) => say === "text")
.map(({ text }) => text ?? "")
.join("\n")
const gradePrompt = `Given this prompt: ${switchModesPrompt} grade the response from 1 to 10 in the format of "Grade: (1-10)". For example: Grade 7\n\nResponse: ${response}`
messages = []
const gradeTaskId = await api.startNewTask({ configuration: { mode: "Ask" }, text: gradePrompt })
await waitUntilCompleted({ api, taskId: gradeTaskId })
const completion = messages.find(({ type, say, partial }) => say === "completion_result")
const match = completion?.text?.match(/Grade: (\d+)/)
const score = parseInt(match?.[1] ?? "0")
assert.ok(
score >= 7 && score <= 10,
`Grade must be between 7 and 10. DEBUG: score = ${score}, completion = ${completion?.text}`,
)
await waitUntilCompleted({ api, taskId: switchModesTaskId })
await api.cancelCurrentTask()
assert.ok(modeSwitches.includes("architect"))
assert.ok(modeSwitches.includes("ask"))
assert.ok(modeSwitches.includes("debug"))
})
})

View file

@ -87,6 +87,7 @@ type UserContent = Array<Anthropic.Messages.ContentBlockParam>
export type ClineEvents = {
message: [{ action: "created" | "updated"; message: ClineMessage }]
taskStarted: []
taskModeSwitched: [taskId: string, mode: string]
taskPaused: []
taskUnpaused: []
taskAskResponded: []

View file

@ -740,10 +740,11 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
*/
public async handleModeSwitch(newMode: Mode) {
// Capture mode switch telemetry event
const currentTaskId = this.getCurrentCline()?.taskId
const cline = this.getCurrentCline()
if (currentTaskId) {
telemetryService.captureModeSwitch(currentTaskId, newMode)
if (cline) {
telemetryService.captureModeSwitch(cline.taskId, newMode)
cline.emit("taskModeSwitched", cline.taskId, newMode)
}
await this.updateGlobalState("mode", newMode)

View file

@ -176,6 +176,8 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
cline.on("message", (message) => this.emit(RooCodeEventName.Message, { taskId: cline.taskId, ...message }))
cline.on("taskModeSwitched", (taskId, mode) => this.emit(RooCodeEventName.TaskModeSwitched, taskId, mode))
cline.on("taskTokenUsageUpdated", (_, usage) =>
this.emit(RooCodeEventName.TaskTokenUsageUpdated, cline.taskId, usage),
)

View file

@ -470,6 +470,7 @@ type RooCodeEvents = {
]
taskCreated: [string]
taskStarted: [string]
taskModeSwitched: [string, string]
taskPaused: [string]
taskUnpaused: [string]
taskAskResponded: [string]
@ -506,6 +507,7 @@ declare enum RooCodeEventName {
Message = "message",
TaskCreated = "taskCreated",
TaskStarted = "taskStarted",
TaskModeSwitched = "taskModeSwitched",
TaskPaused = "taskPaused",
TaskUnpaused = "taskUnpaused",
TaskAskResponded = "taskAskResponded",

View file

@ -479,6 +479,7 @@ type RooCodeEvents = {
]
taskCreated: [string]
taskStarted: [string]
taskModeSwitched: [string, string]
taskPaused: [string]
taskUnpaused: [string]
taskAskResponded: [string]

View file

@ -797,6 +797,7 @@ export enum RooCodeEventName {
Message = "message",
TaskCreated = "taskCreated",
TaskStarted = "taskStarted",
TaskModeSwitched = "taskModeSwitched",
TaskPaused = "taskPaused",
TaskUnpaused = "taskUnpaused",
TaskAskResponded = "taskAskResponded",
@ -816,6 +817,7 @@ export const rooCodeEventsSchema = z.object({
]),
[RooCodeEventName.TaskCreated]: z.tuple([z.string()]),
[RooCodeEventName.TaskStarted]: z.tuple([z.string()]),
[RooCodeEventName.TaskModeSwitched]: z.tuple([z.string(), z.string()]),
[RooCodeEventName.TaskPaused]: z.tuple([z.string()]),
[RooCodeEventName.TaskUnpaused]: z.tuple([z.string()]),
[RooCodeEventName.TaskAskResponded]: z.tuple([z.string()]),

View file

@ -63,6 +63,10 @@ export const taskEventSchema = z.discriminatedUnion("eventName", [
eventName: z.literal(RooCodeEventName.TaskStarted),
payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskStarted],
}),
z.object({
eventName: z.literal(RooCodeEventName.TaskModeSwitched),
payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskModeSwitched],
}),
z.object({
eventName: z.literal(RooCodeEventName.TaskPaused),
payload: rooCodeEventsSchema.shape[RooCodeEventName.TaskPaused],