Skip to content

Commit 2140059

Browse files
cteroomote[bot]
authored andcommitted
Move message queue to the extension host (RooCodeInc#7604)
Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com>
1 parent f1e09d7 commit 2140059

File tree

18 files changed

+380
-216
lines changed

18 files changed

+380
-216
lines changed

packages/cloud/src/bridge/ExtensionChannel.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ export class ExtensionChannel extends BaseChannel<
175175
{ from: RooCodeEventName.TaskInteractive, to: ExtensionBridgeEventName.TaskInteractive },
176176
{ from: RooCodeEventName.TaskResumable, to: ExtensionBridgeEventName.TaskResumable },
177177
{ from: RooCodeEventName.TaskIdle, to: ExtensionBridgeEventName.TaskIdle },
178+
{ from: RooCodeEventName.TaskUserMessage, to: ExtensionBridgeEventName.TaskUserMessage },
178179
] as const
179180

180181
eventMapping.forEach(({ from, to }) => {
@@ -220,6 +221,8 @@ export class ExtensionChannel extends BaseChannel<
220221
? {
221222
taskId: task.taskId,
222223
taskStatus: task.taskStatus,
224+
taskAsk: task?.taskAsk,
225+
queuedMessages: task.queuedMessages,
223226
...task.metadata,
224227
}
225228
: { taskId: "", taskStatus: TaskStatus.None },

packages/cloud/src/bridge/__tests__/ExtensionChannel.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ describe("ExtensionChannel", () => {
101101
RooCodeEventName.TaskInteractive,
102102
RooCodeEventName.TaskResumable,
103103
RooCodeEventName.TaskIdle,
104+
RooCodeEventName.TaskUserMessage,
104105
]
105106

106107
// Check that on() was called for each event
@@ -230,7 +231,7 @@ describe("ExtensionChannel", () => {
230231
}
231232

232233
// Listeners should still be the same count (not accumulated)
233-
const expectedEventCount = 10 // Number of events we listen to
234+
const expectedEventCount = 11 // Number of events we listen to (including TaskUserMessage)
234235
expect(eventListeners.size).toBe(expectedEventCount)
235236

236237
// Each event should have exactly 1 listener

packages/types/npm/package.metadata.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@roo-code/types",
3-
"version": "1.66.0",
3+
"version": "1.67.0",
44
"description": "TypeScript type definitions for Roo Code.",
55
"publishConfig": {
66
"access": "public",

packages/types/src/cloud.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { TaskStatus, taskMetadataSchema } from "./task.js"
77
import { globalSettingsSchema } from "./global-settings.js"
88
import { providerSettingsWithIdSchema } from "./provider-settings.js"
99
import { mcpMarketplaceItemSchema } from "./marketplace.js"
10-
import { clineMessageSchema } from "./message.js"
10+
import { clineMessageSchema, queuedMessageSchema } from "./message.js"
1111
import { staticAppPropertiesSchema, gitPropertiesSchema } from "./telemetry.js"
1212

1313
/**
@@ -359,6 +359,8 @@ export const INSTANCE_TTL_SECONDS = 60
359359
const extensionTaskSchema = z.object({
360360
taskId: z.string(),
361361
taskStatus: z.nativeEnum(TaskStatus),
362+
taskAsk: clineMessageSchema.optional(),
363+
queuedMessages: z.array(queuedMessageSchema).optional(),
362364
...taskMetadataSchema.shape,
363365
})
364366

@@ -402,6 +404,8 @@ export enum ExtensionBridgeEventName {
402404
TaskResumable = RooCodeEventName.TaskResumable,
403405
TaskIdle = RooCodeEventName.TaskIdle,
404406

407+
TaskUserMessage = RooCodeEventName.TaskUserMessage,
408+
405409
ModeChanged = RooCodeEventName.ModeChanged,
406410
ProviderProfileChanged = RooCodeEventName.ProviderProfileChanged,
407411

@@ -461,31 +465,39 @@ export const extensionBridgeEventSchema = z.discriminatedUnion("type", [
461465
instance: extensionInstanceSchema,
462466
timestamp: z.number(),
463467
}),
468+
464469
z.object({
465-
type: z.literal(ExtensionBridgeEventName.InstanceRegistered),
470+
type: z.literal(ExtensionBridgeEventName.TaskUserMessage),
466471
instance: extensionInstanceSchema,
467472
timestamp: z.number(),
468473
}),
474+
469475
z.object({
470-
type: z.literal(ExtensionBridgeEventName.InstanceUnregistered),
476+
type: z.literal(ExtensionBridgeEventName.ModeChanged),
471477
instance: extensionInstanceSchema,
478+
mode: z.string(),
472479
timestamp: z.number(),
473480
}),
474481
z.object({
475-
type: z.literal(ExtensionBridgeEventName.HeartbeatUpdated),
482+
type: z.literal(ExtensionBridgeEventName.ProviderProfileChanged),
476483
instance: extensionInstanceSchema,
484+
providerProfile: z.object({ name: z.string(), provider: z.string().optional() }),
477485
timestamp: z.number(),
478486
}),
487+
479488
z.object({
480-
type: z.literal(ExtensionBridgeEventName.ModeChanged),
489+
type: z.literal(ExtensionBridgeEventName.InstanceRegistered),
481490
instance: extensionInstanceSchema,
482-
mode: z.string(),
483491
timestamp: z.number(),
484492
}),
485493
z.object({
486-
type: z.literal(ExtensionBridgeEventName.ProviderProfileChanged),
494+
type: z.literal(ExtensionBridgeEventName.InstanceUnregistered),
495+
instance: extensionInstanceSchema,
496+
timestamp: z.number(),
497+
}),
498+
z.object({
499+
type: z.literal(ExtensionBridgeEventName.HeartbeatUpdated),
487500
instance: extensionInstanceSchema,
488-
providerProfile: z.object({ name: z.string(), provider: z.string().optional() }),
489501
timestamp: z.number(),
490502
}),
491503
])

packages/types/src/events.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export enum RooCodeEventName {
3131
Message = "message",
3232
TaskModeSwitched = "taskModeSwitched",
3333
TaskAskResponded = "taskAskResponded",
34+
TaskUserMessage = "taskUserMessage",
3435

3536
// Task Analytics
3637
TaskTokenUsageUpdated = "taskTokenUsageUpdated",
@@ -82,6 +83,7 @@ export const rooCodeEventsSchema = z.object({
8283
]),
8384
[RooCodeEventName.TaskModeSwitched]: z.tuple([z.string(), z.string()]),
8485
[RooCodeEventName.TaskAskResponded]: z.tuple([z.string()]),
86+
[RooCodeEventName.TaskUserMessage]: z.tuple([z.string()]),
8587

8688
[RooCodeEventName.TaskToolFailed]: z.tuple([z.string(), toolNamesSchema, z.string()]),
8789
[RooCodeEventName.TaskTokenUsageUpdated]: z.tuple([z.string(), tokenUsageSchema]),

packages/types/src/message.ts

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -248,14 +248,11 @@ export type TokenUsage = z.infer<typeof tokenUsageSchema>
248248
* QueuedMessage
249249
*/
250250

251-
/**
252-
* Represents a message that is queued to be sent when sending is enabled
253-
*/
254-
export interface QueuedMessage {
255-
/** Unique identifier for the queued message */
256-
id: string
257-
/** The text content of the message */
258-
text: string
259-
/** Array of image data URLs attached to the message */
260-
images: string[]
261-
}
251+
export const queuedMessageSchema = z.object({
252+
timestamp: z.number(),
253+
id: z.string(),
254+
text: z.string(),
255+
images: z.array(z.string()).optional(),
256+
})
257+
258+
export type QueuedMessage = z.infer<typeof queuedMessageSchema>

packages/types/src/task.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { z } from "zod"
22

33
import { RooCodeEventName } from "./events.js"
44
import type { RooCodeSettings } from "./global-settings.js"
5-
import type { ClineMessage, TokenUsage } from "./message.js"
5+
import type { ClineMessage, QueuedMessage, TokenUsage } from "./message.js"
66
import type { ToolUsage, ToolName } from "./tool.js"
77
import type { StaticAppProperties, GitProperties, TelemetryProperties } from "./telemetry.js"
88
import type { TodoItem } from "./todo.js"
@@ -59,8 +59,6 @@ export interface TaskProviderLike {
5959

6060
export type TaskProviderEvents = {
6161
[RooCodeEventName.TaskCreated]: [task: TaskLike]
62-
63-
// Proxied from the Task EventEmitter.
6462
[RooCodeEventName.TaskStarted]: [taskId: string]
6563
[RooCodeEventName.TaskCompleted]: [taskId: string, tokenUsage: TokenUsage, toolUsage: ToolUsage]
6664
[RooCodeEventName.TaskAborted]: [taskId: string]
@@ -71,6 +69,9 @@ export type TaskProviderEvents = {
7169
[RooCodeEventName.TaskResumable]: [taskId: string]
7270
[RooCodeEventName.TaskIdle]: [taskId: string]
7371
[RooCodeEventName.TaskSpawned]: [taskId: string]
72+
73+
[RooCodeEventName.TaskUserMessage]: [taskId: string]
74+
7475
[RooCodeEventName.ModeChanged]: [mode: string]
7576
[RooCodeEventName.ProviderProfileChanged]: [config: { name: string; provider?: string }]
7677
}
@@ -105,11 +106,12 @@ export type TaskMetadata = z.infer<typeof taskMetadataSchema>
105106

106107
export interface TaskLike {
107108
readonly taskId: string
108-
readonly taskStatus: TaskStatus
109-
readonly taskAsk: ClineMessage | undefined
109+
readonly rootTask?: TaskLike
110110
readonly metadata: TaskMetadata
111111

112-
readonly rootTask?: TaskLike
112+
readonly taskStatus: TaskStatus
113+
readonly taskAsk: ClineMessage | undefined
114+
readonly queuedMessages: QueuedMessage[]
113115

114116
on<K extends keyof TaskEvents>(event: K, listener: (...args: TaskEvents[K]) => void | Promise<void>): this
115117
off<K extends keyof TaskEvents>(event: K, listener: (...args: TaskEvents[K]) => void | Promise<void>): this
@@ -141,6 +143,7 @@ export type TaskEvents = {
141143
[RooCodeEventName.Message]: [{ action: "created" | "updated"; message: ClineMessage }]
142144
[RooCodeEventName.TaskModeSwitched]: [taskId: string, mode: string]
143145
[RooCodeEventName.TaskAskResponded]: []
146+
[RooCodeEventName.TaskUserMessage]: [taskId: string]
144147

145148
// Task Analytics
146149
[RooCodeEventName.TaskToolFailed]: [taskId: string, tool: ToolName, error: string]
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { EventEmitter } from "events"
2+
3+
import { v4 as uuidv4 } from "uuid"
4+
5+
import { QueuedMessage } from "@roo-code/types"
6+
7+
export interface MessageQueueState {
8+
messages: QueuedMessage[]
9+
isProcessing: boolean
10+
isPaused: boolean
11+
}
12+
13+
export interface QueueEvents {
14+
stateChanged: [messages: QueuedMessage[]]
15+
}
16+
17+
export class MessageQueueService extends EventEmitter<QueueEvents> {
18+
private _messages: QueuedMessage[]
19+
20+
constructor() {
21+
super()
22+
23+
this._messages = []
24+
}
25+
26+
private findMessage(id: string) {
27+
const index = this._messages.findIndex((msg) => msg.id === id)
28+
29+
if (index === -1) {
30+
return { index, message: undefined }
31+
}
32+
33+
return { index, message: this._messages[index] }
34+
}
35+
36+
public addMessage(text: string, images?: string[]): QueuedMessage | undefined {
37+
if (!text && !images?.length) {
38+
return undefined
39+
}
40+
41+
const message: QueuedMessage = {
42+
timestamp: Date.now(),
43+
id: uuidv4(),
44+
text,
45+
images,
46+
}
47+
48+
this._messages.push(message)
49+
this.emit("stateChanged", this._messages)
50+
51+
return message
52+
}
53+
54+
public removeMessage(id: string): boolean {
55+
const { index, message } = this.findMessage(id)
56+
57+
if (!message) {
58+
return false
59+
}
60+
61+
this._messages.splice(index, 1)
62+
this.emit("stateChanged", this._messages)
63+
return true
64+
}
65+
66+
public updateMessage(id: string, text: string, images?: string[]): boolean {
67+
const { message } = this.findMessage(id)
68+
69+
if (!message) {
70+
return false
71+
}
72+
73+
message.timestamp = Date.now()
74+
message.text = text
75+
message.images = images
76+
this.emit("stateChanged", this._messages)
77+
return true
78+
}
79+
80+
public dequeueMessage(): QueuedMessage | undefined {
81+
const message = this._messages.shift()
82+
this.emit("stateChanged", this._messages)
83+
return message
84+
}
85+
86+
public get messages(): QueuedMessage[] {
87+
return this._messages
88+
}
89+
90+
public isEmpty(): boolean {
91+
return this._messages.length === 0
92+
}
93+
94+
public dispose(): void {
95+
this._messages = []
96+
this.removeAllListeners()
97+
}
98+
}

src/core/task-persistence/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
export { readApiMessages, saveApiMessages } from "./apiMessages"
1+
export { type ApiMessage, readApiMessages, saveApiMessages } from "./apiMessages"
22
export { readTaskMessages, saveTaskMessages } from "./taskMessages"
33
export { taskMetadata } from "./taskMetadata"

0 commit comments

Comments
 (0)