Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions packages/ipc/README.md
Original file line number Diff line number Diff line change
@@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent documentation! The ResumeTask command is clearly documented with its parameters and usage example. This makes it easy for external applications to understand how to use this new functionality.


**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}`
2 changes: 1 addition & 1 deletion packages/types/npm/package.metadata.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
75 changes: 75 additions & 0 deletions packages/types/src/__tests__/ipc.test.ts
Original file line number Diff line number Diff line change
@@ -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", () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good test coverage for the schema validation! These tests ensure the ResumeTask command is properly validated. Could we also consider adding an integration test that verifies the actual task resumption flow works end-to-end?

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 changes: 5 additions & 0 deletions packages/types/src/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export enum TaskCommandName {
StartNewTask = "StartNewTask",
CancelTask = "CancelTask",
CloseTask = "CloseTask",
ResumeTask = "ResumeTask",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean addition to the enum and schema! The ResumeTask command follows the same pattern as the other task commands, maintaining consistency in the API design.

}

/**
Expand All @@ -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>
Expand Down
11 changes: 11 additions & 0 deletions src/extension/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
})
}
Expand Down
Loading