Просмотр исходного кода

respect subagent in command, add `subtask` flag (#2569)

Aiden Cline 5 месяцев назад
Родитель
Сommit
16d66c209d

+ 2 - 0
packages/opencode/src/command/index.ts

@@ -10,6 +10,7 @@ export namespace Command {
       agent: z.string().optional(),
       model: z.string().optional(),
       template: z.string(),
+      subtask: z.boolean().optional(),
     })
     .openapi({
       ref: "Command",
@@ -28,6 +29,7 @@ export namespace Command {
         model: command.model,
         description: command.description,
         template: command.template,
+        subtask: command.subtask,
       }
     }
 

+ 1 - 0
packages/opencode/src/config/config.ts

@@ -244,6 +244,7 @@ export namespace Config {
     description: z.string().optional(),
     agent: z.string().optional(),
     model: z.string().optional(),
+    subtask: z.boolean().optional(),
   })
   export type Command = z.infer<typeof Command>
 

+ 119 - 6
packages/opencode/src/session/prompt.ts

@@ -38,6 +38,7 @@ import { MCP } from "../mcp"
 import { LSP } from "../lsp"
 import { ReadTool } from "../tool/read"
 import { ListTool } from "../tool/ls"
+import { TaskTool } from "../tool/task"
 import { FileTime } from "../file/time"
 import { Permission } from "../permission"
 import { Snapshot } from "../snapshot"
@@ -1315,7 +1316,7 @@ export namespace SessionPrompt {
   export async function command(input: CommandInput) {
     log.info("command", input)
     const command = await Command.get(input.command)
-    const agent = command.agent ?? input.agent ?? "build"
+    const agentName = command.agent ?? input.agent ?? "build"
 
     let template = command.template.replace("$ARGUMENTS", input.arguments)
 
@@ -1385,22 +1386,134 @@ export namespace SessionPrompt {
         return Provider.parseModel(command.model)
       }
       if (command.agent) {
-        const agent = await Agent.get(command.agent)
-        if (agent.model) {
-          return agent.model
+        const cmdAgent = await Agent.get(command.agent)
+        if (cmdAgent.model) {
+          return cmdAgent.model
         }
       }
       if (input.model) {
         return Provider.parseModel(input.model)
       }
-      return undefined
+      return await Provider.defaultModel()
     })()
 
+    const agent = await Agent.get(agentName)
+    if (agent.mode === "subagent" || command.subtask) {
+      using abort = lock(input.sessionID)
+
+      const userMsg: MessageV2.User = {
+        id: Identifier.ascending("message"),
+        sessionID: input.sessionID,
+        time: {
+          created: Date.now(),
+        },
+        role: "user",
+      }
+      await Session.updateMessage(userMsg)
+      const userPart: MessageV2.Part = {
+        type: "text",
+        id: Identifier.ascending("part"),
+        messageID: userMsg.id,
+        sessionID: input.sessionID,
+        text: "The following tool was executed by the user",
+        synthetic: true,
+      }
+      await Session.updatePart(userPart)
+
+      const assistantMsg: MessageV2.Assistant = {
+        id: Identifier.ascending("message"),
+        sessionID: input.sessionID,
+        system: [],
+        mode: agentName,
+        cost: 0,
+        path: {
+          cwd: Instance.directory,
+          root: Instance.worktree,
+        },
+        time: {
+          created: Date.now(),
+        },
+        role: "assistant",
+        tokens: {
+          input: 0,
+          output: 0,
+          reasoning: 0,
+          cache: { read: 0, write: 0 },
+        },
+        modelID: model.modelID,
+        providerID: model.providerID,
+      }
+      await Session.updateMessage(assistantMsg)
+
+      const args = {
+        description: "Consulting " + agent.name,
+        subagent_type: agent.name,
+        prompt: template,
+      }
+      const toolPart: MessageV2.ToolPart = {
+        type: "tool",
+        id: Identifier.ascending("part"),
+        messageID: assistantMsg.id,
+        sessionID: input.sessionID,
+        tool: "task",
+        callID: ulid(),
+        state: {
+          status: "running",
+          time: {
+            start: Date.now(),
+          },
+          input: {
+            description: args.description,
+            subagent_type: args.subagent_type,
+            // truncate prompt to preserve context
+            prompt: args.prompt.length > 100 ? args.prompt.substring(0, 97) + "..." : args.prompt,
+          },
+        },
+      }
+      await Session.updatePart(toolPart)
+
+      const result = await TaskTool.init().then((t) =>
+        t.execute(args, {
+          sessionID: input.sessionID,
+          abort: abort.signal,
+          agent: agent.name,
+          messageID: assistantMsg.id,
+          extra: {},
+          metadata: async (metadata) => {
+            if (toolPart.state.status === "running") {
+              toolPart.state.metadata = metadata.metadata
+              toolPart.state.title = metadata.title
+              await Session.updatePart(toolPart)
+            }
+          },
+        }),
+      )
+
+      assistantMsg.time.completed = Date.now()
+      await Session.updateMessage(assistantMsg)
+      if (toolPart.state.status === "running") {
+        toolPart.state = {
+          status: "completed",
+          time: {
+            ...toolPart.state.time,
+            end: Date.now(),
+          },
+          input: toolPart.state.input,
+          title: "",
+          metadata: result.metadata,
+          output: result.output,
+        }
+        await Session.updatePart(toolPart)
+      }
+
+      return { info: assistantMsg, parts: [toolPart] }
+    }
+
     return prompt({
       sessionID: input.sessionID,
       messageID: input.messageID,
       model,
-      agent,
+      agent: agentName,
       parts,
     })
   }