mirror of
https://github.com/anomalyco/opencode.git
synced 2026-04-24 14:55:19 +00:00
Permission revamp docs + new API endpoints
This commit is contained in:
@@ -384,7 +384,7 @@ You can also use wildcards to control multiple tools at once. For example, to di
|
||||
|
||||
### 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. Most built-in tools support permissions (for example, `edit`, `bash`, `read`, `webfetch`, `glob`, `grep`, `task`, etc.), plus special gates like `external_directory` and `doom_loop`.
|
||||
|
||||
- `"ask"` — Prompt for approval before running the tool
|
||||
- `"allow"` — Allow all operations without approval
|
||||
|
||||
@@ -11,6 +11,7 @@ By default, OpenCode allows most operations without approval, except `doom_loop`
|
||||
"permission": {
|
||||
"edit": "allow",
|
||||
"bash": "ask",
|
||||
"read": "allow",
|
||||
"skill": "ask",
|
||||
"webfetch": "deny",
|
||||
"doom_loop": "ask",
|
||||
@@ -19,7 +20,7 @@ By default, OpenCode allows most operations without approval, except `doom_loop`
|
||||
}
|
||||
```
|
||||
|
||||
This lets you configure granular controls for the `edit`, `bash`, `skill`, `webfetch`, `doom_loop`, and `external_directory` tools.
|
||||
This lets you configure granular controls for most built-in tools (and many plugin tools). Note that `write`, `patch`, and `multiedit` are all covered by `permission.edit`.
|
||||
|
||||
- `"ask"` — Prompt for approval before running the tool
|
||||
- `"allow"` — Allow all operations without approval
|
||||
@@ -29,7 +30,26 @@ This lets you configure granular controls for the `edit`, `bash`, `skill`, `webf
|
||||
|
||||
## Tools
|
||||
|
||||
Currently, the permissions for the `edit`, `bash`, `skill`, `webfetch`, `doom_loop`, and `external_directory` tools can be configured through the `permission` option.
|
||||
Any tool that might access your filesystem, run commands, or use the network can be gated behind permissions.
|
||||
|
||||
Common permission keys:
|
||||
|
||||
- `edit` (also covers `write`, `patch`, `multiedit`)
|
||||
- `bash`
|
||||
- `read`
|
||||
- `list`
|
||||
- `glob`
|
||||
- `grep`
|
||||
- `lsp`
|
||||
- `task`
|
||||
- `todowrite` / `todoread`
|
||||
- `skill`
|
||||
- `webfetch`
|
||||
- `websearch` / `codesearch`
|
||||
- `doom_loop`
|
||||
- `external_directory`
|
||||
|
||||
Each permission key can be either a single action (string), or an object mapping patterns to actions.
|
||||
|
||||
---
|
||||
|
||||
@@ -135,13 +155,38 @@ The wildcard uses simple regex globbing patterns.
|
||||
|
||||
#### Scope of the `"ask"` option
|
||||
|
||||
When the agent asks for permission to run a particular bash command, it will
|
||||
request feedback with the three options "accept", "accept always" and "deny".
|
||||
The "accept always" answer applies for the rest of the current session.
|
||||
When the agent asks for permission to run a particular bash command, it will request feedback with three options: `once`, `always`, and `reject`.
|
||||
|
||||
In addition, command permissions are applied to the first two elements of a command. So, an "accept always" response for a command like `git log` would whitelist `git log *` but not `git commit ...`.
|
||||
The `always` option stores a rule for a normalized **command prefix** (for example, `git status*` or `npm run dev*`). The prefix is derived from a built-in command-arity table (flags don’t count); for unknown commands it falls back to the first token.
|
||||
|
||||
When an agent asks for permission to run a command in a pipeline, we use tree sitter to parse each command in the pipeline. The "accept always" permission thus applies separately to each command in the pipeline.
|
||||
When an agent asks for permission to run a command in a pipeline, each command is parsed separately, and the saved `always` rule applies per command.
|
||||
|
||||
---
|
||||
|
||||
### read
|
||||
|
||||
Use the `permission.read` key to control whether file reads require approval.
|
||||
|
||||
The permission patterns for `read` are absolute file paths.
|
||||
|
||||
```json title="opencode.json" {4}
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"permission": {
|
||||
"read": "ask"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### list, glob, grep
|
||||
|
||||
You can also gate other "read-only" tools:
|
||||
|
||||
- `permission.list` — directory listings (patterns are directory paths)
|
||||
- `permission.glob` — file globs (patterns are the glob patterns)
|
||||
- `permission.grep` — content search (patterns are the regex patterns)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -170,8 +170,8 @@ Plugins can subscribe to events as seen below in the Examples section. Here is a
|
||||
|
||||
#### Permission Events
|
||||
|
||||
- `permission.asked`
|
||||
- `permission.replied`
|
||||
- `permission.updated`
|
||||
|
||||
#### Server Events
|
||||
|
||||
|
||||
@@ -226,27 +226,36 @@ const { providers, default: defaults } = await client.config.providers()
|
||||
|
||||
### Sessions
|
||||
|
||||
| Method | Description | Notes |
|
||||
| ---------------------------------------------------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `session.list()` | List sessions | Returns <a href={typesUrl}><code>Session[]</code></a> |
|
||||
| `session.get({ path })` | Get session | Returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `session.children({ path })` | List child sessions | Returns <a href={typesUrl}><code>Session[]</code></a> |
|
||||
| `session.create({ body })` | Create session | Returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `session.delete({ path })` | Delete session | Returns `boolean` |
|
||||
| `session.update({ path, body })` | Update session properties | Returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `session.init({ path, body })` | Analyze app and create `AGENTS.md` | Returns `boolean` |
|
||||
| `session.abort({ path })` | Abort a running session | Returns `boolean` |
|
||||
| `session.share({ path })` | Share session | Returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `session.unshare({ path })` | Unshare session | Returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `session.summarize({ path, body })` | Summarize session | Returns `boolean` |
|
||||
| `session.messages({ path })` | List messages in a session | Returns `{ info: `<a href={typesUrl}><code>Message</code></a>`, parts: `<a href={typesUrl}><code>Part[]</code></a>`}[]` |
|
||||
| `session.message({ path })` | Get message details | Returns `{ info: `<a href={typesUrl}><code>Message</code></a>`, parts: `<a href={typesUrl}><code>Part[]</code></a>`}` |
|
||||
| `session.prompt({ path, body })` | Send prompt message | `body.noReply: true` returns UserMessage (context only). Default returns <a href={typesUrl}><code>AssistantMessage</code></a> with AI response |
|
||||
| `session.command({ path, body })` | Send command to session | Returns `{ info: `<a href={typesUrl}><code>AssistantMessage</code></a>`, parts: `<a href={typesUrl}><code>Part[]</code></a>`}` |
|
||||
| `session.shell({ path, body })` | Run a shell command | Returns <a href={typesUrl}><code>AssistantMessage</code></a> |
|
||||
| `session.revert({ path, body })` | Revert a message | Returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `session.unrevert({ path })` | Restore reverted messages | Returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `postSessionByIdPermissionsByPermissionId({ path, body })` | Respond to a permission request | Returns `boolean` |
|
||||
| Method | Description | Notes |
|
||||
| ----------------------------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `session.list()` | List sessions | Returns <a href={typesUrl}><code>Session[]</code></a> |
|
||||
| `session.get({ path })` | Get session | Returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `session.children({ path })` | List child sessions | Returns <a href={typesUrl}><code>Session[]</code></a> |
|
||||
| `session.create({ body })` | Create session | Returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `session.delete({ path })` | Delete session | Returns `boolean` |
|
||||
| `session.update({ path, body })` | Update session properties | Returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `session.init({ path, body })` | Analyze app and create `AGENTS.md` | Returns `boolean` |
|
||||
| `session.abort({ path })` | Abort a running session | Returns `boolean` |
|
||||
| `session.share({ path })` | Share session | Returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `session.unshare({ path })` | Unshare session | Returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `session.summarize({ path, body })` | Summarize session | Returns `boolean` |
|
||||
| `session.messages({ path })` | List messages in a session | Returns `{ info: `<a href={typesUrl}><code>Message</code></a>`, parts: `<a href={typesUrl}><code>Part[]</code></a>`}[]` |
|
||||
| `session.message({ path })` | Get message details | Returns `{ info: `<a href={typesUrl}><code>Message</code></a>`, parts: `<a href={typesUrl}><code>Part[]</code></a>`}` |
|
||||
| `session.prompt({ path, body })` | Send prompt message | `body.noReply: true` returns UserMessage (context only). Default returns <a href={typesUrl}><code>AssistantMessage</code></a> with AI response |
|
||||
| `session.command({ path, body })` | Send command to session | Returns `{ info: `<a href={typesUrl}><code>AssistantMessage</code></a>`, parts: `<a href={typesUrl}><code>Part[]</code></a>`}` |
|
||||
| `session.shell({ path, body })` | Run a shell command | Returns <a href={typesUrl}><code>AssistantMessage</code></a> |
|
||||
| `session.revert({ path, body })` | Revert a message | Returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `session.unrevert({ path })` | Restore reverted messages | Returns <a href={typesUrl}><code>Session</code></a> |
|
||||
|
||||
---
|
||||
|
||||
### Permissions
|
||||
|
||||
| Method | Description | Notes |
|
||||
| ----------------------------------------------------------- | ------------------------------- | ------------------------------------------------------------------ |
|
||||
| `permission.list()` | List pending permissions | Returns <a href={typesUrl}><code>PermissionRequest[]</code></a> |
|
||||
| `permission.reply({ requestID, reply })` | Respond to a permission request | `reply` is `"once"`, `"always"`, or `"reject"` (returns `boolean`) |
|
||||
| `permission.respond({ sessionID, permissionID, response })` | Respond to a permission request | Deprecated alias (returns `boolean`) |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -134,26 +134,37 @@ The opencode server exposes the following APIs.
|
||||
|
||||
### Sessions
|
||||
|
||||
| Method | Path | Description | Notes |
|
||||
| -------- | ---------------------------------------- | ------------------------------------- | ---------------------------------------------------------------------------------- |
|
||||
| `GET` | `/session` | List all sessions | Returns <a href={typesUrl}><code>Session[]</code></a> |
|
||||
| `POST` | `/session` | Create a new session | body: `{ parentID?, title? }`, returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `GET` | `/session/status` | Get session status for all sessions | Returns `{ [sessionID: string]: `<a href={typesUrl}>SessionStatus</a>` }` |
|
||||
| `GET` | `/session/:id` | Get session details | Returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `DELETE` | `/session/:id` | Delete a session and all its data | Returns `boolean` |
|
||||
| `PATCH` | `/session/:id` | Update session properties | body: `{ title? }`, returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `GET` | `/session/:id/children` | Get a session's child sessions | Returns <a href={typesUrl}><code>Session[]</code></a> |
|
||||
| `GET` | `/session/:id/todo` | Get the todo list for a session | Returns <a href={typesUrl}><code>Todo[]</code></a> |
|
||||
| `POST` | `/session/:id/init` | Analyze app and create `AGENTS.md` | body: `{ messageID, providerID, modelID }`, returns `boolean` |
|
||||
| `POST` | `/session/:id/fork` | Fork an existing session at a message | body: `{ messageID? }`, returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `POST` | `/session/:id/abort` | Abort a running session | Returns `boolean` |
|
||||
| `POST` | `/session/:id/share` | Share a session | Returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `DELETE` | `/session/:id/share` | Unshare a session | Returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `GET` | `/session/:id/diff` | Get the diff for this session | query: `messageID?`, returns <a href={typesUrl}><code>FileDiff[]</code></a> |
|
||||
| `POST` | `/session/:id/summarize` | Summarize the session | body: `{ providerID, modelID }`, returns `boolean` |
|
||||
| `POST` | `/session/:id/revert` | Revert a message | body: `{ messageID, partID? }`, returns `boolean` |
|
||||
| `POST` | `/session/:id/unrevert` | Restore all reverted messages | Returns `boolean` |
|
||||
| `POST` | `/session/:id/permissions/:permissionID` | Respond to a permission request | body: `{ response, remember? }`, returns `boolean` |
|
||||
| Method | Path | Description | Notes |
|
||||
| -------- | ------------------------ | ------------------------------------- | ---------------------------------------------------------------------------------- |
|
||||
| `GET` | `/session` | List all sessions | Returns <a href={typesUrl}><code>Session[]</code></a> |
|
||||
| `POST` | `/session` | Create a new session | body: `{ parentID?, title? }`, returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `GET` | `/session/status` | Get session status for all sessions | Returns `{ [sessionID: string]: `<a href={typesUrl}>SessionStatus</a>` }` |
|
||||
| `GET` | `/session/:id` | Get session details | Returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `DELETE` | `/session/:id` | Delete a session and all its data | Returns `boolean` |
|
||||
| `PATCH` | `/session/:id` | Update session properties | body: `{ title? }`, returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `GET` | `/session/:id/children` | Get a session's child sessions | Returns <a href={typesUrl}><code>Session[]</code></a> |
|
||||
| `GET` | `/session/:id/todo` | Get the todo list for a session | Returns <a href={typesUrl}><code>Todo[]</code></a> |
|
||||
| `POST` | `/session/:id/init` | Analyze app and create `AGENTS.md` | body: `{ messageID, providerID, modelID }`, returns `boolean` |
|
||||
| `POST` | `/session/:id/fork` | Fork an existing session at a message | body: `{ messageID? }`, returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `POST` | `/session/:id/abort` | Abort a running session | Returns `boolean` |
|
||||
| `POST` | `/session/:id/share` | Share a session | Returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `DELETE` | `/session/:id/share` | Unshare a session | Returns <a href={typesUrl}><code>Session</code></a> |
|
||||
| `GET` | `/session/:id/diff` | Get the diff for this session | query: `messageID?`, returns <a href={typesUrl}><code>FileDiff[]</code></a> |
|
||||
| `POST` | `/session/:id/summarize` | Summarize the session | body: `{ providerID, modelID }`, returns `boolean` |
|
||||
| `POST` | `/session/:id/revert` | Revert a message | body: `{ messageID, partID? }`, returns `boolean` |
|
||||
| `POST` | `/session/:id/unrevert` | Restore all reverted messages | Returns `boolean` |
|
||||
|
||||
---
|
||||
|
||||
### Permissions
|
||||
|
||||
| Method | Path | Description | Notes |
|
||||
| ------ | ---------------------------------------- | -------------------------------------------- | --------------------------------------------------------------- |
|
||||
| `GET` | `/permission` | List pending permissions | Returns <a href={typesUrl}><code>PermissionRequest[]</code></a> |
|
||||
| `POST` | `/permission/:requestID/reply` | Respond to a permission request | body: `{ reply }`, returns `boolean` |
|
||||
| `POST` | `/session/:id/permissions/:permissionID` | Respond to a permission request (deprecated) | body: `{ response }`, returns `boolean` |
|
||||
|
||||
`reply` / `response` can be one of: `"once"`, `"always"`, or `"reject"`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user