Reference
Configuration
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.
| Layer | Path | Role |
|---|---|---|
| base | $PANTO_HOME/config.toml · else $XDG_DATA_HOME/panto/config.toml | Auto-generated by bootstrap. Lowest precedence. |
| user | $XDG_CONFIG_HOME/panto/config.toml · else ~/.config/panto/config.toml | Your personal, machine-wide settings. |
| project | ./.panto/config.toml | Checked-in, per-project settings. |
| local | ./.panto/config.local.toml | Personal 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/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 shellThe 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.
anthropic_messages, openai_chat, or openai_responses.style = "openai_responses" accepts it today, with the single value "codex" (the Codex Responses variant).[auth.<name>] session that supplies this provider’s credential. (Provider-level api_key is no longer accepted — auth lives in one place.)anthropic_messages only. Place one advancing cache_control breakpoint on each request. Ignored for other styles."github-copilot") for panto models sync. Defaults to the provider name.[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’sapi_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
"${env:VAR}", but a literal works too. Resolved eagerly at load; an empty result leaves the session unresolved.key.OAuth device sessions
codex additionally requires device_poll_url.codex).exchange_method (default GET), exchange_token_path (default token), exchange_expires_path, exchange_base_url_path — JSON paths into the exchange response.[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]
"<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).
deny).A name is permitted when it matches at least one allow pattern (or allow is empty) and matches no deny pattern.
[tools]
allow = ["std.*"] # only the standard tools
deny = ["std.bash"] # …but never the shellConflicts are fatal
The same pattern in bothallow 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.
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
openai_chat only
default, off, minimal, low, medium, high.anthropic_messages only
disabled, enabled, or adaptive.low · medium · high · xhigh · max. Used when thinking = "adaptive".thinking = "enabled".thinking = "enabled").anthropic-version header.# ~/.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.0System 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):
On resume, prompt changes are reconciled without rewriting the existing conversation.