Files
paseo/public-docs/sdk/provider-options.md
Mohamed Boudra 29c198f955 fix(providers): apply configured options to every agent provider (#5780)
* fix(providers): apply configured options at session startup

Treat provider options as one opaque record, with provider-owned validation and per-agent overrides over configured defaults. Keep agent overrides separate from defaults so configuration changes apply on the next launch or resume.

* test(providers): verify provider-owned validation through Hub

* fix(providers): preserve the complete wrapped session interface
2026-09-30 19:37:14 +01:00

8.1 KiB

title, description, nav, order, category
title description nav order category
Provider options Sandboxing, permissions, and network rules for Codex, Claude, and OpenCode agents created from the SDK. Provider options 55 TypeScript SDK

Provider options

Provider options are an opaque Record<string, unknown> 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.<id>.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:

{
  "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.

Codex

Confine writes to one directory, cut off network access, and stop approval prompts so the run is unattended:

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:

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:

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 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. options is the provider's own configuration. They are separate controls and you can set both:

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.