Skip to content

Commit 5d20eae

Browse files
fix(task): align subagent tool visibility with permissions
1 parent 5bfbd62 commit 5d20eae

File tree

2 files changed

+48
-38
lines changed

2 files changed

+48
-38
lines changed

packages/opencode/src/session/prompt.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -654,6 +654,8 @@ export namespace SessionPrompt {
654654
using _ = log.time("resolveTools")
655655
const tools: Record<string, AITool> = {}
656656

657+
const ruleset = PermissionNext.merge(input.agent.permission, input.session.permission ?? [])
658+
657659
const context = (args: any, options: ToolCallOptions): Tool.Context => ({
658660
sessionID: input.session.id,
659661
abort: options.abortSignal!,
@@ -688,10 +690,17 @@ export namespace SessionPrompt {
688690
},
689691
})
690692

691-
for (const item of await ToolRegistry.tools(
693+
const items = await ToolRegistry.tools(
692694
{ modelID: input.model.api.id, providerID: input.model.providerID },
693695
input.agent,
694-
)) {
696+
)
697+
const denied = PermissionNext.disabled(
698+
items.map((x) => x.id),
699+
ruleset,
700+
)
701+
702+
for (const item of items) {
703+
if (denied.has(item.id)) continue
695704
const schema = ProviderTransform.schema(input.model, z.toJSONSchema(item.parameters))
696705
tools[item.id] = tool({
697706
id: item.id as any,

packages/opencode/src/tool/task.ts

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import { Agent } from "../agent/agent"
99
import { SessionPrompt } from "../session/prompt"
1010
import { iife } from "@/util/iife"
1111
import { defer } from "@/util/defer"
12-
import { Config } from "../config/config"
1312
import { PermissionNext } from "@/permission/next"
13+
import { Config } from "../config/config"
1414

1515
const parameters = z.object({
1616
description: z.string().describe("A short (3-5 words) description of the task"),
@@ -39,8 +39,6 @@ export const TaskTool = Tool.define("task", async (ctx) => {
3939
description,
4040
parameters,
4141
async execute(params: z.infer<typeof parameters>, ctx) {
42-
const config = await Config.get()
43-
4442
// Skip permission check when user explicitly invoked via @ or command subtask
4543
if (!ctx.extra?.bypassAgentCheck) {
4644
await ctx.ask({
@@ -58,42 +56,51 @@ export const TaskTool = Tool.define("task", async (ctx) => {
5856
if (!agent) throw new Error(`Unknown agent type: ${params.subagent_type} is not a valid agent type`)
5957

6058
const hasTaskPermission = agent.permission.some((rule) => rule.permission === "task")
59+
const config = await Config.get()
60+
const primaryTools = config.experimental?.primary_tools ?? []
61+
62+
const permissions = [
63+
{
64+
permission: "todowrite",
65+
pattern: "*",
66+
action: "deny" as const,
67+
},
68+
{
69+
permission: "todoread",
70+
pattern: "*",
71+
action: "deny" as const,
72+
},
73+
...(hasTaskPermission
74+
? []
75+
: [
76+
{
77+
permission: "task" as const,
78+
pattern: "*" as const,
79+
action: "deny" as const,
80+
},
81+
]),
82+
...primaryTools.map((t) => ({
83+
permission: t,
84+
pattern: "*" as const,
85+
action: "deny" as const,
86+
})),
87+
]
6188

6289
const session = await iife(async () => {
6390
if (params.session_id) {
6491
const found = await Session.get(params.session_id).catch(() => {})
65-
if (found) return found
92+
if (found) {
93+
await Session.update(found.id, (draft) => {
94+
draft.permission = PermissionNext.merge(draft.permission ?? [], permissions)
95+
})
96+
return found
97+
}
6698
}
6799

68100
return await Session.create({
69101
parentID: ctx.sessionID,
70102
title: params.description + ` (@${agent.name} subagent)`,
71-
permission: [
72-
{
73-
permission: "todowrite",
74-
pattern: "*",
75-
action: "deny",
76-
},
77-
{
78-
permission: "todoread",
79-
pattern: "*",
80-
action: "deny",
81-
},
82-
...(hasTaskPermission
83-
? []
84-
: [
85-
{
86-
permission: "task" as const,
87-
pattern: "*" as const,
88-
action: "deny" as const,
89-
},
90-
]),
91-
...(config.experimental?.primary_tools?.map((t) => ({
92-
pattern: "*",
93-
action: "allow" as const,
94-
permission: t,
95-
})) ?? []),
96-
],
103+
permission: permissions,
97104
})
98105
})
99106
const msg = await MessageV2.get({ sessionID: ctx.sessionID, messageID: ctx.messageID })
@@ -152,12 +159,6 @@ export const TaskTool = Tool.define("task", async (ctx) => {
152159
providerID: model.providerID,
153160
},
154161
agent: agent.name,
155-
tools: {
156-
todowrite: false,
157-
todoread: false,
158-
...(hasTaskPermission ? {} : { task: false }),
159-
...Object.fromEntries((config.experimental?.primary_tools ?? []).map((t) => [t, false])),
160-
},
161162
parts: promptParts,
162163
})
163164
unsub()

0 commit comments

Comments
 (0)