fix(core): deny subagent tool for default subagents

This commit is contained in:
Dax Raad 2026-06-30 23:11:06 -04:00
commit c26f6f95f7
2 changed files with 34 additions and 1 deletions

View file

@ -158,7 +158,12 @@ export const Plugin = define({
item.description =
"General-purpose agent for researching complex questions and executing multi-step tasks. Use this agent to execute multiple units of work in parallel."
item.mode = "subagent"
item.permissions.push(...PermissionV2.merge(defaults, [{ action: "todowrite", resource: "*", effect: "deny" }]))
item.permissions.push(
...PermissionV2.merge(defaults, [
{ action: "subagent", resource: "*", effect: "deny" },
{ action: "todowrite", resource: "*", effect: "deny" },
]),
)
})
draft.update(AgentV2.ID.make("explore"), (item) => {
@ -176,6 +181,7 @@ export const Plugin = define({
{ action: "webfetch", resource: "*", effect: "allow" },
{ action: "websearch", resource: "*", effect: "allow" },
{ action: "read", resource: "*", effect: "allow" },
{ action: "subagent", resource: "*", effect: "deny" },
],
readonlyExternalDirectory,
),

View file

@ -3,6 +3,7 @@ import { Effect, Exit, Fiber, Layer, Scope, Stream } from "effect"
import { AgentV2 } from "@opencode-ai/core/agent"
import { EventV2 } from "@opencode-ai/core/event"
import { Location } from "@opencode-ai/core/location"
import { PermissionV2 } from "@opencode-ai/core/permission"
import { AgentPlugin } from "@opencode-ai/core/plugin/agent"
import { AbsolutePath } from "@opencode-ai/core/schema"
import { location } from "./fixture/location"
@ -152,4 +153,30 @@ describe("AgentV2", () => {
}
}),
)
it.effect("denies the subagent tool for built-in subagents", () =>
Effect.gen(function* () {
const agent = yield* AgentV2.Service
yield* AgentPlugin.Plugin.effect(
host({
agent: agentHost(agent),
}),
).pipe(
Effect.provideService(
Location.Service,
Location.Service.of(location({ directory: AbsolutePath.make("/project") })),
),
)
yield* Effect.forEach(["general", "explore"], (id) =>
Effect.gen(function* () {
const info = yield* agent.get(AgentV2.ID.make(id))
if (!info) throw new Error(`expected built-in agent: ${id}`)
expect(info.mode).toBe("subagent")
expect(info.permissions).toContainEqual({ action: "subagent", resource: "*", effect: "deny" })
expect(PermissionV2.evaluate("subagent", "*", info.permissions).effect).toBe("deny")
}),
)
}),
)
})