mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Very rough proof-of-concept of subtask communication
This commit is contained in:
parent
79e5f56ccf
commit
91ec85eddf
3 changed files with 98 additions and 6 deletions
|
|
@ -75,6 +75,7 @@ type UserContent = Array<
|
|||
|
||||
export class Cline {
|
||||
readonly taskId: string
|
||||
readonly parentTaskId?: string
|
||||
api: ApiHandler
|
||||
private terminalManager: TerminalManager
|
||||
private urlContentFetcher: UrlContentFetcher
|
||||
|
|
@ -129,12 +130,14 @@ export class Cline {
|
|||
images?: string[] | undefined,
|
||||
historyItem?: HistoryItem | undefined,
|
||||
experiments?: Record<string, boolean>,
|
||||
parentTaskId?: string,
|
||||
) {
|
||||
if (!task && !images && !historyItem) {
|
||||
throw new Error("Either historyItem or task/images must be provided")
|
||||
}
|
||||
|
||||
this.taskId = crypto.randomUUID()
|
||||
this.parentTaskId = parentTaskId
|
||||
this.api = buildApiHandler(apiConfiguration)
|
||||
this.terminalManager = new TerminalManager()
|
||||
this.urlContentFetcher = new UrlContentFetcher(provider.context)
|
||||
|
|
@ -2553,7 +2556,7 @@ export class Cline {
|
|||
const provider = this.providerRef.deref()
|
||||
if (provider) {
|
||||
await provider.handleModeSwitch(mode)
|
||||
await provider.initClineWithTask(message)
|
||||
await provider.initClineWithTask(message, undefined, this.taskId)
|
||||
pushToolResult(
|
||||
`Successfully created new task in ${targetMode.name} mode with message: ${message}`,
|
||||
)
|
||||
|
|
@ -2667,10 +2670,22 @@ export class Cline {
|
|||
await this.say("completion_result", result, undefined, false)
|
||||
}
|
||||
|
||||
// If this is a child task, return result to parent
|
||||
if (this.parentTaskId) {
|
||||
const provider = this.providerRef.deref()
|
||||
if (provider) {
|
||||
try {
|
||||
await provider.handleTaskResult(this.parentTaskId, result)
|
||||
} catch (error) {
|
||||
console.error("Failed to return result to parent task:", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we already sent completion_result says, an empty string asks relinquishes control over button and field
|
||||
const { response, text, images } = await this.ask("completion_result", "", false)
|
||||
if (response === "yesButtonClicked") {
|
||||
pushToolResult("") // signals to recursive loop to stop (for now this never happens since yesButtonClicked will trigger a new task)
|
||||
pushToolResult("") // signals to recursive loop to stop
|
||||
break
|
||||
}
|
||||
await this.say("user_feedback", text ?? "", images)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import WorkspaceTracker from "../../integrations/workspace/WorkspaceTracker"
|
|||
import { McpHub } from "../../services/mcp/McpHub"
|
||||
import { ApiConfiguration, ApiProvider, ModelInfo } from "../../shared/api"
|
||||
import { findLast } from "../../shared/array"
|
||||
import { ApiConfigMeta, ExtensionMessage } from "../../shared/ExtensionMessage"
|
||||
import { ApiConfigMeta, ExtensionMessage, ClineMessage } from "../../shared/ExtensionMessage"
|
||||
import { HistoryItem } from "../../shared/HistoryItem"
|
||||
import { checkoutDiffPayloadSchema, checkoutRestorePayloadSchema, WebviewMessage } from "../../shared/WebviewMessage"
|
||||
import { Mode, CustomModePrompts, PromptComponent, defaultModeSlug } from "../../shared/modes"
|
||||
|
|
@ -277,7 +277,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
return
|
||||
}
|
||||
|
||||
await visibleProvider.initClineWithTask(prompt)
|
||||
await visibleProvider.initClineWithTask(prompt, undefined, undefined)
|
||||
}
|
||||
|
||||
public static async handleTerminalAction(
|
||||
|
|
@ -397,7 +397,80 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
this.outputChannel.appendLine("Webview view resolved")
|
||||
}
|
||||
|
||||
public async initClineWithTask(task?: string, images?: string[]) {
|
||||
/**
|
||||
* Adds a child task's result as a text message to a parent task's messages and saves it
|
||||
*/
|
||||
private async addChildTaskResultToParent(taskId: string, result: string): Promise<void> {
|
||||
const taskDir = path.join(this.context.globalStorageUri.fsPath, "tasks", taskId)
|
||||
const messagesPath = path.join(taskDir, GlobalFileNames.uiMessages)
|
||||
|
||||
// Read existing messages
|
||||
let messages: ClineMessage[] = []
|
||||
if (await fileExistsAtPath(messagesPath)) {
|
||||
messages = JSON.parse(await fs.readFile(messagesPath, "utf8"))
|
||||
}
|
||||
|
||||
// Add result as text message from child task
|
||||
messages.push({
|
||||
ts: Date.now(),
|
||||
type: "say",
|
||||
say: "text",
|
||||
text: result,
|
||||
})
|
||||
|
||||
// Save updated messages
|
||||
await fs.writeFile(messagesPath, JSON.stringify(messages))
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles returning a result to a parent task
|
||||
*/
|
||||
public async handleTaskResult(parentTaskId: string, result: string): Promise<void> {
|
||||
try {
|
||||
// Get parent task data
|
||||
const { historyItem, apiConversationHistory } = await this.getTaskWithId(parentTaskId)
|
||||
|
||||
// Add text message to parent task's UI messages
|
||||
await this.addChildTaskResultToParent(parentTaskId, result)
|
||||
|
||||
// Add message to parent task's API conversation history
|
||||
const message: Anthropic.MessageParam & { ts?: number } = {
|
||||
role: "assistant",
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: result,
|
||||
},
|
||||
],
|
||||
ts: Date.now(),
|
||||
}
|
||||
apiConversationHistory.push(message)
|
||||
|
||||
// Save updated API conversation history
|
||||
const taskDir = path.join(this.context.globalStorageUri.fsPath, "tasks", parentTaskId)
|
||||
const apiHistoryPath = path.join(taskDir, GlobalFileNames.apiConversationHistory)
|
||||
await fs.writeFile(apiHistoryPath, JSON.stringify(apiConversationHistory))
|
||||
|
||||
// Switch back to parent task
|
||||
await this.initClineWithHistoryItem(historyItem)
|
||||
await this.postMessageToWebview({
|
||||
type: "action",
|
||||
action: "chatButtonClicked",
|
||||
})
|
||||
// Wait for chat view to load before clicking the button
|
||||
await delay(300)
|
||||
// Hit the primary button to continue the parent task
|
||||
await this.postMessageToWebview({
|
||||
type: "invoke",
|
||||
invoke: "primaryButtonClick",
|
||||
})
|
||||
} catch (error) {
|
||||
this.outputChannel.appendLine(`Failed to handle task result: ${error}`)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
public async initClineWithTask(task?: string, images?: string[], parentTaskId?: string) {
|
||||
await this.clearTask()
|
||||
const {
|
||||
apiConfiguration,
|
||||
|
|
@ -424,6 +497,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
images,
|
||||
undefined,
|
||||
experiments,
|
||||
parentTaskId,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -774,7 +848,9 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
// Could also do this in extension .ts
|
||||
//this.postMessageToWebview({ type: "text", text: `Extension: ${Date.now()}` })
|
||||
// initializing new instance of Cline will make sure that any agentically running promises in old instance don't affect our new task. this essentially creates a fresh slate for the new task
|
||||
await this.initClineWithTask(message.text, message.images)
|
||||
// Pass the current task's ID as the parent ID if it exists
|
||||
const parentTaskId = this.cline?.taskId
|
||||
await this.initClineWithTask(message.text, message.images, parentTaskId)
|
||||
break
|
||||
case "apiConfiguration":
|
||||
if (message.apiConfiguration) {
|
||||
|
|
|
|||
|
|
@ -8,4 +8,5 @@ export type HistoryItem = {
|
|||
cacheReads?: number
|
||||
totalCost: number
|
||||
size?: number
|
||||
parentTaskId?: string // ID of the parent task if this was created via new_task
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue