* 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
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.optionsinclient.agents.create(). Its wire name isproviderOptions.
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.