--- title: Provider options description: Sandboxing, permissions, and network rules for Codex, Claude, and OpenCode agents created from the SDK. nav: Provider options order: 55 category: TypeScript SDK --- # Provider options Provider options are an opaque `Record` passed to the provider. The provider decides how to validate and apply it. Claude, Codex, and OpenCode use strict schemas: an unknown or misspelled key fails agent creation with a message naming the bad key. Providers without option handling ignore the record. Pi, OMP, ACP, and plugin providers interpret their own keys. You set options in two places: - **Provider defaults:** `config.json` → `agents.providers..options`. - **One agent:** `config.options` in `client.agents.create()`. Its wire name is `providerOptions`. The daemon deep-merges the agent's options over the provider defaults at session launch or resume. Plain objects merge recursively; arrays, scalars, and `null` replace the base value. Only the agent's own options are stored. Changing `config.json` defaults takes effect on the next launch or resume. For example, set Codex defaults once: ```json { "agents": { "providers": { "codex": { "options": { "sandbox_mode": "read-only", "approval_policy": "never", "sandbox_workspace_write": { "network_access": false } } } } } } ``` Then create one agent with `config.options: { sandbox_mode: "workspace-write" }`. It receives `workspace-write`, retains `approval_policy: "never"`, and retains `sandbox_workspace_write.network_access: false`. With no per-agent options it receives the defaults above. Keys are provider-native; use the sections below for the options each provider accepts. Provider options are not a host boundary. They constrain the agent CLI, which runs as your user on your machine. For untrusted work, run the daemon in a container or on a separate machine. See [Security](/docs/security). ## Codex Confine writes to one directory, cut off network access, and stop approval prompts so the run is unattended: ```ts import { createPaseoClient } from "@getpaseo/client"; const client = createPaseoClient({ url: "ws://127.0.0.1:6767/ws" }); await client.connect(); const agent = await client.agents.create({ config: { provider: "codex/gpt-5.5", options: { approval_policy: "never", sandbox_mode: "workspace-write", sandbox_workspace_write: { writable_roots: ["/Users/me/dev/storefront"], network_access: false, exclude_slash_tmp: true, }, web_search: "disabled", }, }, cwd: "/Users/me/dev/storefront", prompt: "Fix the failing checkout test.", }); const result = await agent.waitForFinish(); console.log(result.status, result.lastMessage); await client.close(); ``` | Option | Values | | ------------------------- | --------------------------------------------------------------------------------- | | `approval_policy` | `untrusted`, `on-request`, `never`, or `{ granular: { … } }` | | `sandbox_mode` | `read-only`, `workspace-write`, `danger-full-access` | | `sandbox_workspace_write` | `writable_roots`, `network_access`, `exclude_slash_tmp`, `exclude_tmpdir_env_var` | | `web_search` | `disabled`, `cached`, `indexed`, `live` | | `features` | `multi_agent_v2`, `network_proxy` (boolean or a proxy/domain policy object) | `approval_policy: "never"` only removes the prompts. What the agent is allowed to touch is `sandbox_mode`. Setting `never` without a sandbox mode gives an unattended agent full access. ## Claude Turn on Claude's own sandbox, restrict writes to the project, deny reads of credential directories, and allow only two domains: ```ts import { createPaseoClient } from "@getpaseo/client"; const client = createPaseoClient({ url: "ws://127.0.0.1:6767/ws" }); await client.connect(); const agent = await client.agents.create({ config: { provider: "claude/claude-sonnet-5", options: { disallowedTools: ["WebFetch"], sandbox: { enabled: true, failIfUnavailable: true, allowUnsandboxedCommands: false, filesystem: { allowWrite: ["/Users/me/dev/storefront"], denyRead: ["/Users/me/.ssh", "/Users/me/.aws"], }, network: { allowedDomains: ["registry.npmjs.org", "github.com"], strictAllowlist: true, }, }, }, }, cwd: "/Users/me/dev/storefront", prompt: "Install dependencies and run the focused test.", }); const result = await agent.waitForFinish(); console.log(result.status, result.lastMessage); await client.close(); ``` | Option | Values | | ----------------------- | ------------------------------------------------------------------------------------------------------- | | `allowedTools` | Tool names the agent may use without asking | | `disallowedTools` | Tool names the agent may never use | | `additionalDirectories` | Extra directories the agent may access | | `sandbox` | `enabled`, `failIfUnavailable`, `allowUnsandboxedCommands`, `excludedCommands`, `filesystem`, `network` | | `settings` | `permissions` (`allow`/`ask`/`deny` rule lists) and a nested `sandbox` | `failIfUnavailable: true` makes agent startup fail when the sandbox cannot be established. Leave it off and Claude runs unsandboxed instead. `sandbox.filesystem` takes `allowWrite`, `denyWrite`, `allowRead`, and `denyRead`. `sandbox.network` takes `allowedDomains`, `deniedDomains`, `strictAllowlist`, and proxy settings. ## OpenCode Allow reads and edits, deny anything that reaches outside the project, and gate shell commands by pattern: ```ts import { createPaseoClient } from "@getpaseo/client"; const client = createPaseoClient({ url: "ws://127.0.0.1:6767/ws" }); await client.connect(); const agent = await client.agents.create({ config: { provider: "opencode/opencode/gpt-5.5", options: { permission: { read: "allow", edit: "allow", webfetch: "deny", external_directory: "deny", bash: { "git status": "allow", "git diff*": "allow", "git push*": "deny", "*": "ask", }, }, }, }, cwd: "/Users/me/dev/storefront", prompt: "Implement the requested change and show me the diff.", }); const result = await agent.waitForFinish(); console.log(result.status, result.lastMessage); await client.close(); ``` Every permission is `ask`, `allow`, or `deny`. Tools that take a target — `read`, `edit`, `glob`, `grep`, `list`, `bash`, `task`, `external_directory`, `repo_clone`, `repo_overview`, `lsp`, `skill` — also accept a pattern map, where later keys act as the fallback. Tools without a target — `todowrite`, `question`, `webfetch`, `websearch`, `codesearch`, `doom_loop` — take a bare action. `permission: "deny"` as a bare string applies to everything at once. An `ask` resolves to a permission request in Paseo, and `waitForFinish()` returns `permission` while it is pending. Use `allow` and `deny` for unattended runs. See [Events](/docs/sdk/events) for reacting to a pending request. ## Modes and options `modeId` picks a Paseo mode from the provider's published list, which the daemon shows in the app and reports through [provider discovery](/docs/sdk/providers). `options` is the provider's own configuration. They are separate controls and you can set both: ```ts config: { provider: "codex/gpt-5.5", modeId: "full-access", options: { approval_policy: "never", sandbox_mode: "read-only", }, } ``` Where the two overlap, `options` wins. Above, Codex runs read-only even though `full-access` would otherwise grant more.