Explorar o código

Add an API for resuming tasks by ID (#7122)

Matt Rubens hai 6 meses
pai
achega
9a734d0cab

+ 90 - 0
packages/ipc/README.md

@@ -0,0 +1,90 @@
+# IPC (Inter-Process Communication)
+
+This package provides IPC functionality for Roo Code, allowing external applications to communicate with the extension through a socket-based interface.
+
+## Available Commands
+
+The IPC interface supports the following task commands:
+
+### StartNewTask
+
+Starts a new task with optional configuration and initial message.
+
+**Parameters:**
+
+- `configuration`: RooCode settings object
+- `text`: Initial task message (string)
+- `images`: Array of image data URIs (optional)
+- `newTab`: Whether to open in a new tab (boolean, optional)
+
+### CancelTask
+
+Cancels a running task.
+
+**Parameters:**
+
+- `data`: Task ID to cancel (string)
+
+### CloseTask
+
+Closes a task and performs cleanup.
+
+**Parameters:**
+
+- `data`: Task ID to close (string)
+
+### ResumeTask
+
+Resumes a task from history.
+
+**Parameters:**
+
+- `data`: Task ID to resume (string)
+
+**Error Handling:**
+
+- If the task ID is not found in history, the command will fail gracefully without crashing the IPC server
+- Errors are logged for debugging purposes but do not propagate to the client
+
+## Usage Example
+
+```typescript
+import { IpcClient } from "@roo-code/ipc"
+
+const client = new IpcClient("/path/to/socket")
+
+// Resume a task
+client.sendCommand({
+	commandName: "ResumeTask",
+	data: "task-123",
+})
+
+// Start a new task
+client.sendCommand({
+	commandName: "StartNewTask",
+	data: {
+		configuration: {
+			/* RooCode settings */
+		},
+		text: "Hello, world!",
+		images: [],
+		newTab: false,
+	},
+})
+```
+
+## Events
+
+The IPC interface also emits task events that clients can listen to:
+
+- `TaskStarted`: When a task begins
+- `TaskCompleted`: When a task finishes
+- `TaskAborted`: When a task is cancelled
+- `Message`: When a task sends a message
+
+## Socket Path
+
+The socket path is typically located in the system's temporary directory and follows the pattern:
+
+- Unix/Linux/macOS: `/tmp/roo-code-{id}.sock`
+- Windows: `\\.\pipe\roo-code-{id}`

+ 1 - 1
packages/types/npm/package.metadata.json

@@ -1,6 +1,6 @@
 {
 	"name": "@roo-code/types",
-	"version": "1.49.0",
+	"version": "1.50.0",
 	"description": "TypeScript type definitions for Roo Code.",
 	"publishConfig": {
 		"access": "public",

+ 75 - 0
packages/types/src/__tests__/ipc.test.ts

@@ -0,0 +1,75 @@
+import { describe, it, expect } from "vitest"
+import { TaskCommandName, taskCommandSchema } from "../ipc.js"
+
+describe("IPC Types", () => {
+	describe("TaskCommandName", () => {
+		it("should include ResumeTask command", () => {
+			expect(TaskCommandName.ResumeTask).toBe("ResumeTask")
+		})
+
+		it("should have all expected task commands", () => {
+			const expectedCommands = ["StartNewTask", "CancelTask", "CloseTask", "ResumeTask"]
+			const actualCommands = Object.values(TaskCommandName)
+
+			expectedCommands.forEach((command) => {
+				expect(actualCommands).toContain(command)
+			})
+		})
+
+		describe("Error Handling", () => {
+			it("should handle ResumeTask command gracefully when task not found", () => {
+				// This test verifies the schema validation - the actual error handling
+				// for invalid task IDs is tested at the API level, not the schema level
+				const resumeTaskCommand = {
+					commandName: TaskCommandName.ResumeTask,
+					data: "non-existent-task-id",
+				}
+
+				const result = taskCommandSchema.safeParse(resumeTaskCommand)
+				expect(result.success).toBe(true)
+
+				if (result.success) {
+					expect(result.data.commandName).toBe("ResumeTask")
+					expect(result.data.data).toBe("non-existent-task-id")
+				}
+			})
+		})
+	})
+
+	describe("taskCommandSchema", () => {
+		it("should validate ResumeTask command with taskId", () => {
+			const resumeTaskCommand = {
+				commandName: TaskCommandName.ResumeTask,
+				data: "task-123",
+			}
+
+			const result = taskCommandSchema.safeParse(resumeTaskCommand)
+			expect(result.success).toBe(true)
+
+			if (result.success) {
+				expect(result.data.commandName).toBe("ResumeTask")
+				expect(result.data.data).toBe("task-123")
+			}
+		})
+
+		it("should reject ResumeTask command with invalid data", () => {
+			const invalidCommand = {
+				commandName: TaskCommandName.ResumeTask,
+				data: 123, // Should be string
+			}
+
+			const result = taskCommandSchema.safeParse(invalidCommand)
+			expect(result.success).toBe(false)
+		})
+
+		it("should reject ResumeTask command without data", () => {
+			const invalidCommand = {
+				commandName: TaskCommandName.ResumeTask,
+				// Missing data field
+			}
+
+			const result = taskCommandSchema.safeParse(invalidCommand)
+			expect(result.success).toBe(false)
+		})
+	})
+})

+ 5 - 0
packages/types/src/ipc.ts

@@ -44,6 +44,7 @@ export enum TaskCommandName {
 	StartNewTask = "StartNewTask",
 	CancelTask = "CancelTask",
 	CloseTask = "CloseTask",
+	ResumeTask = "ResumeTask",
 }
 
 /**
@@ -68,6 +69,10 @@ export const taskCommandSchema = z.discriminatedUnion("commandName", [
 		commandName: z.literal(TaskCommandName.CloseTask),
 		data: z.string(),
 	}),
+	z.object({
+		commandName: z.literal(TaskCommandName.ResumeTask),
+		data: z.string(),
+	}),
 ])
 
 export type TaskCommand = z.infer<typeof taskCommandSchema>

+ 11 - 0
src/extension/api.ts

@@ -78,6 +78,17 @@ export class API extends EventEmitter<RooCodeEvents> implements RooCodeAPI {
 						await vscode.commands.executeCommand("workbench.action.files.saveFiles")
 						await vscode.commands.executeCommand("workbench.action.closeWindow")
 						break
+					case TaskCommandName.ResumeTask:
+						this.log(`[API] ResumeTask -> ${data}`)
+						try {
+							await this.resumeTask(data)
+						} catch (error) {
+							const errorMessage = error instanceof Error ? error.message : String(error)
+							this.log(`[API] ResumeTask failed for taskId ${data}: ${errorMessage}`)
+							// Don't rethrow - we want to prevent IPC server crashes
+							// The error is logged for debugging purposes
+						}
+						break
 				}
 			})
 		}