Reference

Configuration

TOML

panto is configured by layered config.toml files, plus a separate models.toml for model aliases and pricing. Define providers, pick a default model, and gate tools and extensions — everything you specify, nothing you don’t.

Files & layers

Four files are read and merged, lowest precedence first. Missing files are skipped silently.

LayerPathRole
base$PANTO_HOME/config.toml · else $XDG_DATA_HOME/panto/config.tomlAuto-generated by bootstrap. Lowest precedence.
user$XDG_CONFIG_HOME/panto/config.toml · else ~/.config/panto/config.tomlYour personal, machine-wide settings.
project./.panto/config.tomlChecked-in, per-project settings.
local./.panto/config.local.tomlPersonal per-project overrides — git-ignore this. Highest precedence.

Merge semantics: tables merge recursively, but scalars and arrays from a higher layer overwrite wholesale. A project file that sets tools.deny replaces the array entirely — it doesn’t append to a user-level list. Tables accumulate, so a provider defined only at the base layer survives even when a higher layer adds another.

config.tomltoml
# ~/.config/panto/config.toml  (user layer)

[defaults]
model = "anthropic:sonnet"          # <provider>:<alias>

[providers.anthropic]
style    = "anthropic_messages"
base_url = "https://api.anthropic.com"
auth     = "anthropic_api"          # draws its key from [auth.anthropic_api]

[auth.anthropic_api]
key = "${env:ANTHROPIC_API_KEY}"    # resolved at load

[tools]
deny = ["std.bash"]                 # allow every tool except the shell

The local layer

./.panto/config.local.toml is meant to be git-ignored: apply your own overrides on top of the checked-in project layer without committing them.

[providers.<name>]

A provider is pure transport: an API shape and a base URL, plus the name of the auth session that supplies its credential. The chosen name is what you reference on the left of a provider:alias model.

stylestringrequired
The wire protocol: anthropic_messages, openai_chat, or openai_responses.
dialect"codex"
A sub-dialect for styles that share a protocol family. Only style = "openai_responses" accepts it today, with the single value "codex" (the Codex Responses variant).
base_urlstringrequired
The API base URL requests are sent to.
authstringrequired
Names the [auth.<name>] session that supplies this provider’s credential. (Provider-level api_key is no longer accepted — auth lives in one place.)
prompt_cachebooldefault true
anthropic_messages only. Place one advancing cache_control breakpoint on each request. Ignored for other styles.
extra_headerstable
A table of static string headers merged onto every model request (see below).
model_catalog_namestring
Maps this provider onto a models.dev catalog key (e.g. "github-copilot") for panto models sync. Defaults to the provider name.
config.tomltoml
[providers.copilot]
style    = "openai_chat"
base_url = "https://api.individual.githubcopilot.com"
auth     = "github_copilot"

[providers.copilot.extra_headers]
Copilot-Integration-Id = "vscode-chat"
X-Initiator            = "user"

Unresolved providers vanish

If a provider’s api_key auth session resolves to empty (its env var isn’t set), the provider is dropped — export the key to bring it back. OAuth providers always survive and resolve at turn time.

[auth.<name>]

A named auth session supplies a credential. Its type is inferred when omitted: a key implies api_key; a client_id implies oauth_device.

Substitution

Every auth value supports ${…} substitution. ${env:VAR} reads the environment; ${name} reads another key in the same section (handy for a shared domain). An unresolved reference becomes the empty string.

API-key sessions

keystringrequired
The API key — usually "${env:VAR}", but a literal works too. Resolved eagerly at load; an empty result leaves the session unresolved.
type"api_key"
Optional; inferred from the presence of key.

OAuth device sessions

client_idstringrequired
The OAuth client id.
device_code_urlstringrequired
Endpoint that issues the device + user codes.
token_urlstringrequired
Endpoint polled for the access token.
dialect"token" | "codex"default token
Flow dialect. codex additionally requires device_poll_url.
scopestring
Requested OAuth scopes.
verification_urlstring
Override the URL shown to the user.
device_poll_urlstring
Distinct poll endpoint (required for codex).
token_request_format"form" | "json"default form
How the token request body is encoded.
redirect_uri / account_id_jwt_claimstring
Optional flow details.
exchange_urlstring
Enables an optional secondary token exchange. Companions: exchange_method (default GET), exchange_token_path (default token), exchange_expires_path, exchange_base_url_path — JSON paths into the exchange response.
config.tomltoml
[auth.github_copilot]
client_id       = "Iv1.b507a08c87ecfe98"
domain          = "github.com"          # an ordinary sibling key…
device_code_url = "https://${domain}/login/device/code"   # …reused here
token_url       = "https://${domain}/login/oauth/access_token"
scope           = "read:user"

# optional secondary token exchange
exchange_url           = "https://api.${domain}/copilot_internal/v2/token"
exchange_expires_path  = "expires_at"
exchange_base_url_path = "endpoints.api"

[defaults]

modelstring
The default model, as "<provider>:<alias>" (exactly one colon, both halves non-empty). The provider must resolve.

Model selection falls through in order: an explicit override (a future --model), then defaults.model, then — if exactly one provider resolved and it has exactly one alias in models.toml — that one. Otherwise panto asks you to set a default.

Tools & extensions

[tools] and [extensions] share the same shape: allow- and deny-lists of glob patterns over dotted names (the standard tools live under the std. namespace — std.read, std.write, std.edit, std.bash).

allowstring[]
Glob patterns. Empty means allow-all (still subject to deny).
denystring[]
Glob patterns. A name is denied if it matches any deny pattern.

A name is permitted when it matches at least one allow pattern (or allow is empty) and matches no deny pattern.

config.tomltoml
[tools]
allow = ["std.*"]          # only the standard tools
deny  = ["std.bash"]       # …but never the shell

Conflicts are fatal

The same pattern in both allow and deny for one kind is a hard error — panto refuses to start rather than guess.

[compaction]

panto compacts long conversations automatically; this section tunes it. The manual /compact command uses the same settings.

keep_verbatimintdefault 20000
Token budget for the recent tail kept verbatim when compacting.
modelstring
Optional provider:alias override for the model that writes the summary. Defaults to the active chat model.

models.toml

Model aliases live in $XDG_CONFIG_HOME/panto/models.toml, separate from config.toml. Each entry is keyed [<provider>.<alias>] — the provider matches a [providers.<name>], the alias is the short name you reference. A referenced alias with no entry isn’t an error: it’s used verbatim as the wire name with default knobs.

models.toml is layered exactly like config.toml (base → user → project → local). The base layer is auto-generated — panto models sync fetches the catalog and writes it — so your user and project entries always layer on top and win.

Common keys

modelstring
Wire model id sent to the API. Defaults to the alias.
context_windowint
Total input context window, used for the footer’s context-usage display. Optional.
max_tokensint
Per-request output token cap.
input / output / cache_read / cache_writefloat
Pricing in USD per million tokens. All optional; omitted means unknown.

openai_chat only

reasoningstringdefault default
One of default, off, minimal, low, medium, high.

anthropic_messages only

thinkingstringdefault disabled
disabled, enabled, or adaptive.
effortstringdefault medium
low · medium · high · xhigh · max. Used when thinking = "adaptive".
thinking_budget_tokensintdefault 32000
Reasoning-token budget when thinking = "enabled".
thinking_interleavedbooldefault false
Send the interleaved-thinking beta header (honoured when thinking = "enabled").
api_versionstringdefault 2023-06-01
The anthropic-version header.
models.tomltoml
# ~/.config/panto/models.toml

[anthropic.sonnet]
model      = "claude-sonnet-4-20250514"   # wire name; defaults to the alias
max_tokens = 8192
thinking   = "enabled"
thinking_budget_tokens = 16000
input  = 3.0        # USD per million tokens
output = 15.0
cache_read  = 0.30
cache_write = 3.75

[openai.gpt]
model     = "gpt-4o"
reasoning = "medium"
input  = 2.5
output = 10.0

System prompt

The agent’s system prompt is sourced by convention from Markdown files across the same base → user → project layers (base is $PANTO_HOME/agent, where bootstrap stages the bundled default):

SYSTEM.md
Replaces the system prompt for that layer.
APPEND_SYSTEM.md
Appends to it — for small, additive house rules.
COMPACTION.md
Overrides the summary prompt used during compaction (last layer wins; a built-in default applies otherwise).

On resume, prompt changes are reconciled without rewriting the existing conversation.