summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAiden Cline <[email protected]>2025-09-13 12:47:18 -0500
committerGitHub <[email protected]>2025-09-13 12:47:18 -0500
commit16d66c209ded6475967cdd5d20815eafb2be6a1d (patch)
tree290d25765c4d6da7e6372be2635c255ed625d527
parent6506e48c54dc6b3cdf07089d420c136206ef710c (diff)
downloadopencode-16d66c209ded6475967cdd5d20815eafb2be6a1d.tar.gz
opencode-16d66c209ded6475967cdd5d20815eafb2be6a1d.zip
respect subagent in command, add `subtask` flag (#2569)
-rw-r--r--packages/opencode/src/command/index.ts2
-rw-r--r--packages/opencode/src/config/config.ts1
-rw-r--r--packages/opencode/src/session/prompt.ts125
3 files changed, 122 insertions, 6 deletions
diff --git a/packages/opencode/src/command/index.ts b/packages/opencode/src/command/index.ts
index a9356cdd4..8ee1df2b4 100644
--- a/packages/opencode/src/command/index.ts
+++ b/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,
}
}
diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts
index f4b8608d8..22ff58db8 100644
--- a/packages/opencode/src/config/config.ts
+++ b/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>
diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts
index 8e70fa73f..ef3deeb3a 100644
--- a/packages/opencode/src/session/prompt.ts
+++ b/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,
})
}