fix(opencode): agent create generates permissions field with deny ins… (#24482)

Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
This commit is contained in:
21pounder
2026-04-27 13:17:08 +08:00
committed by GitHub
parent 1e191ba815
commit 0efc6163f1
3 changed files with 84 additions and 46 deletions

View File

@@ -15,7 +15,10 @@ import type { Argv } from "yargs"
type AgentMode = "all" | "primary" | "subagent"
const AVAILABLE_TOOLS = ["bash", "read", "write", "edit", "glob", "grep", "webfetch", "task", "todowrite"]
// Permission keys (not raw tool names). Multiple tools can map to a single
// permission — e.g. write/edit/apply_patch all gate on `edit` — so we configure
// agents at the permission level to match how the runtime actually enforces it.
const AVAILABLE_PERMISSIONS = ["bash", "read", "edit", "glob", "grep", "webfetch", "task", "todowrite", "websearch", "codesearch", "lsp", "skill"]
const AgentCreateCommand = cmd({
command: "create",
@@ -35,9 +38,10 @@ const AgentCreateCommand = cmd({
describe: "agent mode",
choices: ["all", "primary", "subagent"] as const,
})
.option("tools", {
.option("permissions", {
type: "string",
describe: `comma-separated list of tools to enable (default: all). Available: "${AVAILABLE_TOOLS.join(", ")}"`,
alias: ["tools"],
describe: `comma-separated list of permissions to allow (default: all). Available: "${AVAILABLE_PERMISSIONS.join(", ")}"`,
})
.option("model", {
type: "string",
@@ -51,9 +55,9 @@ const AgentCreateCommand = cmd({
const cliPath = args.path
const cliDescription = args.description
const cliMode = args.mode as AgentMode | undefined
const cliTools = args.tools
const perms = args.permissions
const isFullyNonInteractive = cliPath && cliDescription && cliMode && cliTools !== undefined
const isFullyNonInteractive = cliPath && cliDescription && cliMode && perms !== undefined
if (!isFullyNonInteractive) {
UI.empty()
@@ -120,21 +124,21 @@ const AgentCreateCommand = cmd({
})
spinner.stop(`Agent ${generated.identifier} generated`)
// Select tools
let selectedTools: string[]
if (cliTools !== undefined) {
selectedTools = cliTools ? cliTools.split(",").map((t) => t.trim()) : AVAILABLE_TOOLS
// Select permissions to allow
let selected: string[]
if (perms !== undefined) {
selected = perms ? perms.split(",").map((t) => t.trim()) : AVAILABLE_PERMISSIONS
} else {
const result = await prompts.multiselect({
message: "Select tools to enable (Space to toggle)",
options: AVAILABLE_TOOLS.map((tool) => ({
label: tool,
value: tool,
message: "Select permissions to allow (Space to toggle)",
options: AVAILABLE_PERMISSIONS.map((permission) => ({
label: permission,
value: permission,
})),
initialValues: AVAILABLE_TOOLS,
initialValues: AVAILABLE_PERMISSIONS,
})
if (prompts.isCancel(result)) throw new UI.CancelledError()
selectedTools = result
selected = result
}
// Get mode
@@ -167,11 +171,11 @@ const AgentCreateCommand = cmd({
mode = modeResult
}
// Build tools config
const tools: Record<string, boolean> = {}
for (const tool of AVAILABLE_TOOLS) {
if (!selectedTools.includes(tool)) {
tools[tool] = false
// Build permissions config — deny anything not explicitly selected.
const permissions: Record<string, "deny"> = {}
for (const permission of AVAILABLE_PERMISSIONS) {
if (!selected.includes(permission)) {
permissions[permission] = "deny"
}
}
@@ -179,13 +183,13 @@ const AgentCreateCommand = cmd({
const frontmatter: {
description: string
mode: AgentMode
tools?: Record<string, boolean>
permission?: Record<string, "deny">
} = {
description: generated.whenToUse,
mode,
}
if (Object.keys(tools).length > 0) {
frontmatter.tools = tools
if (Object.keys(permissions).length > 0) {
frontmatter.permission = permissions
}
// Write file

View File

@@ -149,19 +149,17 @@ Configure agents in your `opencode.json` config file:
"mode": "primary",
"model": "anthropic/claude-sonnet-4-20250514",
"prompt": "{file:./prompts/build.txt}",
"tools": {
"write": true,
"edit": true,
"bash": true
"permission": {
"edit": "allow",
"bash": "allow"
}
},
"plan": {
"mode": "primary",
"model": "anthropic/claude-haiku-4-20250514",
"tools": {
"write": false,
"edit": false,
"bash": false
"permission": {
"edit": "deny",
"bash": "deny"
}
},
"code-reviewer": {
@@ -169,9 +167,8 @@ Configure agents in your `opencode.json` config file:
"mode": "subagent",
"model": "anthropic/claude-sonnet-4-20250514",
"prompt": "You are a code reviewer. Focus on security, performance, and maintainability.",
"tools": {
"write": false,
"edit": false
"permission": {
"edit": "deny"
}
}
}
@@ -193,10 +190,9 @@ description: Reviews code for quality and best practices
mode: subagent
model: anthropic/claude-sonnet-4-20250514
temperature: 0.1
tools:
write: false
edit: false
bash: false
permission:
edit: deny
bash: deny
---
You are in code review mode. Focus on:
@@ -417,12 +413,39 @@ You can also use wildcards in legacy `tools` entries to control multiple tools a
### Permissions
You can configure permissions to manage what actions an agent can take. Currently, the permissions for the `edit`, `bash`, and `webfetch` tools can be configured to:
You can configure permissions to manage what actions an agent can take. Each permission key can be set to:
- `"ask"` — Prompt for approval before running the tool
- `"allow"` — Allow all operations without approval
- `"deny"` — Disable the tool
The available permission keys are:
| Key | Tools it gates |
| -------------------- | ----------------------------------------------------------------------------- |
| `read` | `read` |
| `edit` | `write`, `edit`, `apply_patch` |
| `glob` | `glob` |
| `grep` | `grep` |
| `list` | `list` |
| `bash` | `bash` |
| `task` | `task` |
| `external_directory` | Any tool that reads or writes files outside the project worktree |
| `todowrite` | `todowrite`, `todoread` |
| `webfetch` | `webfetch` |
| `websearch` | `websearch` |
| `codesearch` | `codesearch` |
| `lsp` | `lsp` |
| `skill` | `skill` |
| `question` | `question` |
| `doom_loop` | Recovery prompts when an agent appears stuck |
`read`, `edit`, `glob`, `grep`, `list`, `bash`, `task`, `external_directory`, `lsp`, and `skill` accept either a shorthand action (`"allow" | "ask" | "deny"`) or an object of glob/pattern → action for fine-grained control. The remaining keys accept the shorthand action only.
:::note
Permission keys are matched as wildcard patterns against the underlying tool name, so the same syntax works for built-ins, custom tools, and MCP tools — for example `"mymcp_*": "deny"` denies every tool from an MCP server, and `"mymcp_search": "ask"` targets a single one.
:::
```json title="opencode.json"
{
"$schema": "https://opencode.ai/config.json",
@@ -680,7 +703,7 @@ This interactive command will:
1. Ask where to save the agent; global or project-specific.
2. Description of what the agent should do.
3. Generate an appropriate system prompt and identifier.
4. Let you select which tools the agent can access.
4. Let you select which permissions the agent should be allowed (anything you don't select is denied).
5. Finally, create a markdown file with the agent configuration.
---
@@ -713,8 +736,8 @@ Do you have an agent you'd like to share? [Submit a PR](https://github.com/anoma
---
description: Writes and maintains project documentation
mode: subagent
tools:
bash: false
permission:
bash: deny
---
You are a technical writer. Create clear, comprehensive documentation.
@@ -735,9 +758,8 @@ Focus on:
---
description: Performs security audits and identifies vulnerabilities
mode: subagent
tools:
write: false
edit: false
permission:
edit: deny
---
You are a security expert. Focus on identifying potential security issues.

View File

@@ -93,7 +93,19 @@ Create a new agent with custom configuration.
opencode agent create
```
This command will guide you through creating a new agent with a custom system prompt and tool configuration.
This command will guide you through creating a new agent with a custom system prompt and permission configuration. Anything you don't allow is denied in the generated agent's frontmatter.
#### Flags
| Flag | Description |
| ---------------- | ---------------------------------------------------------------------------------------------------------- |
| `--path` | Directory to write the agent file to (defaults to global or `.opencode/agent` based on the prompt) |
| `--description` | What the agent should do |
| `--mode` | Agent mode: `all`, `primary`, or `subagent` |
| `--permissions` | Comma-separated list of permissions to allow (default: all). Available: `bash`, `read`, `edit`, `glob`, `grep`, `webfetch`, `task`, `todowrite`, `websearch`, `codesearch`, `lsp`, `skill`. Anything omitted is denied. Alias: `--tools` |
| `--model`, `-m` | Model to use, in `provider/model` format |
Passing all of `--path`, `--description`, `--mode`, and `--permissions` runs the command non-interactively.
---