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:
co-authored by
Aiden Cline
Aiden Cline
parent
1e191ba815
commit
0efc6163f1
@@ -15,7 +15,10 @@ import type { Argv } from "yargs"
|
|||||||
|
|
||||||
type AgentMode = "all" | "primary" | "subagent"
|
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({
|
const AgentCreateCommand = cmd({
|
||||||
command: "create",
|
command: "create",
|
||||||
@@ -35,9 +38,10 @@ const AgentCreateCommand = cmd({
|
|||||||
describe: "agent mode",
|
describe: "agent mode",
|
||||||
choices: ["all", "primary", "subagent"] as const,
|
choices: ["all", "primary", "subagent"] as const,
|
||||||
})
|
})
|
||||||
.option("tools", {
|
.option("permissions", {
|
||||||
type: "string",
|
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", {
|
.option("model", {
|
||||||
type: "string",
|
type: "string",
|
||||||
@@ -51,9 +55,9 @@ const AgentCreateCommand = cmd({
|
|||||||
const cliPath = args.path
|
const cliPath = args.path
|
||||||
const cliDescription = args.description
|
const cliDescription = args.description
|
||||||
const cliMode = args.mode as AgentMode | undefined
|
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) {
|
if (!isFullyNonInteractive) {
|
||||||
UI.empty()
|
UI.empty()
|
||||||
@@ -120,21 +124,21 @@ const AgentCreateCommand = cmd({
|
|||||||
})
|
})
|
||||||
spinner.stop(`Agent ${generated.identifier} generated`)
|
spinner.stop(`Agent ${generated.identifier} generated`)
|
||||||
|
|
||||||
// Select tools
|
// Select permissions to allow
|
||||||
let selectedTools: string[]
|
let selected: string[]
|
||||||
if (cliTools !== undefined) {
|
if (perms !== undefined) {
|
||||||
selectedTools = cliTools ? cliTools.split(",").map((t) => t.trim()) : AVAILABLE_TOOLS
|
selected = perms ? perms.split(",").map((t) => t.trim()) : AVAILABLE_PERMISSIONS
|
||||||
} else {
|
} else {
|
||||||
const result = await prompts.multiselect({
|
const result = await prompts.multiselect({
|
||||||
message: "Select tools to enable (Space to toggle)",
|
message: "Select permissions to allow (Space to toggle)",
|
||||||
options: AVAILABLE_TOOLS.map((tool) => ({
|
options: AVAILABLE_PERMISSIONS.map((permission) => ({
|
||||||
label: tool,
|
label: permission,
|
||||||
value: tool,
|
value: permission,
|
||||||
})),
|
})),
|
||||||
initialValues: AVAILABLE_TOOLS,
|
initialValues: AVAILABLE_PERMISSIONS,
|
||||||
})
|
})
|
||||||
if (prompts.isCancel(result)) throw new UI.CancelledError()
|
if (prompts.isCancel(result)) throw new UI.CancelledError()
|
||||||
selectedTools = result
|
selected = result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get mode
|
// Get mode
|
||||||
@@ -167,11 +171,11 @@ const AgentCreateCommand = cmd({
|
|||||||
mode = modeResult
|
mode = modeResult
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build tools config
|
// Build permissions config — deny anything not explicitly selected.
|
||||||
const tools: Record<string, boolean> = {}
|
const permissions: Record<string, "deny"> = {}
|
||||||
for (const tool of AVAILABLE_TOOLS) {
|
for (const permission of AVAILABLE_PERMISSIONS) {
|
||||||
if (!selectedTools.includes(tool)) {
|
if (!selected.includes(permission)) {
|
||||||
tools[tool] = false
|
permissions[permission] = "deny"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,13 +183,13 @@ const AgentCreateCommand = cmd({
|
|||||||
const frontmatter: {
|
const frontmatter: {
|
||||||
description: string
|
description: string
|
||||||
mode: AgentMode
|
mode: AgentMode
|
||||||
tools?: Record<string, boolean>
|
permission?: Record<string, "deny">
|
||||||
} = {
|
} = {
|
||||||
description: generated.whenToUse,
|
description: generated.whenToUse,
|
||||||
mode,
|
mode,
|
||||||
}
|
}
|
||||||
if (Object.keys(tools).length > 0) {
|
if (Object.keys(permissions).length > 0) {
|
||||||
frontmatter.tools = tools
|
frontmatter.permission = permissions
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write file
|
// Write file
|
||||||
|
|||||||
@@ -149,19 +149,17 @@ Configure agents in your `opencode.json` config file:
|
|||||||
"mode": "primary",
|
"mode": "primary",
|
||||||
"model": "anthropic/claude-sonnet-4-20250514",
|
"model": "anthropic/claude-sonnet-4-20250514",
|
||||||
"prompt": "{file:./prompts/build.txt}",
|
"prompt": "{file:./prompts/build.txt}",
|
||||||
"tools": {
|
"permission": {
|
||||||
"write": true,
|
"edit": "allow",
|
||||||
"edit": true,
|
"bash": "allow"
|
||||||
"bash": true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"plan": {
|
"plan": {
|
||||||
"mode": "primary",
|
"mode": "primary",
|
||||||
"model": "anthropic/claude-haiku-4-20250514",
|
"model": "anthropic/claude-haiku-4-20250514",
|
||||||
"tools": {
|
"permission": {
|
||||||
"write": false,
|
"edit": "deny",
|
||||||
"edit": false,
|
"bash": "deny"
|
||||||
"bash": false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"code-reviewer": {
|
"code-reviewer": {
|
||||||
@@ -169,9 +167,8 @@ Configure agents in your `opencode.json` config file:
|
|||||||
"mode": "subagent",
|
"mode": "subagent",
|
||||||
"model": "anthropic/claude-sonnet-4-20250514",
|
"model": "anthropic/claude-sonnet-4-20250514",
|
||||||
"prompt": "You are a code reviewer. Focus on security, performance, and maintainability.",
|
"prompt": "You are a code reviewer. Focus on security, performance, and maintainability.",
|
||||||
"tools": {
|
"permission": {
|
||||||
"write": false,
|
"edit": "deny"
|
||||||
"edit": false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -193,10 +190,9 @@ description: Reviews code for quality and best practices
|
|||||||
mode: subagent
|
mode: subagent
|
||||||
model: anthropic/claude-sonnet-4-20250514
|
model: anthropic/claude-sonnet-4-20250514
|
||||||
temperature: 0.1
|
temperature: 0.1
|
||||||
tools:
|
permission:
|
||||||
write: false
|
edit: deny
|
||||||
edit: false
|
bash: deny
|
||||||
bash: false
|
|
||||||
---
|
---
|
||||||
|
|
||||||
You are in code review mode. Focus on:
|
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
|
### 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
|
- `"ask"` — Prompt for approval before running the tool
|
||||||
- `"allow"` — Allow all operations without approval
|
- `"allow"` — Allow all operations without approval
|
||||||
- `"deny"` — Disable the tool
|
- `"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"
|
```json title="opencode.json"
|
||||||
{
|
{
|
||||||
"$schema": "https://opencode.ai/config.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.
|
1. Ask where to save the agent; global or project-specific.
|
||||||
2. Description of what the agent should do.
|
2. Description of what the agent should do.
|
||||||
3. Generate an appropriate system prompt and identifier.
|
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.
|
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
|
description: Writes and maintains project documentation
|
||||||
mode: subagent
|
mode: subagent
|
||||||
tools:
|
permission:
|
||||||
bash: false
|
bash: deny
|
||||||
---
|
---
|
||||||
|
|
||||||
You are a technical writer. Create clear, comprehensive documentation.
|
You are a technical writer. Create clear, comprehensive documentation.
|
||||||
@@ -735,9 +758,8 @@ Focus on:
|
|||||||
---
|
---
|
||||||
description: Performs security audits and identifies vulnerabilities
|
description: Performs security audits and identifies vulnerabilities
|
||||||
mode: subagent
|
mode: subagent
|
||||||
tools:
|
permission:
|
||||||
write: false
|
edit: deny
|
||||||
edit: false
|
|
||||||
---
|
---
|
||||||
|
|
||||||
You are a security expert. Focus on identifying potential security issues.
|
You are a security expert. Focus on identifying potential security issues.
|
||||||
|
|||||||
@@ -93,7 +93,19 @@ Create a new agent with custom configuration.
|
|||||||
opencode agent create
|
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.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user