Three things configure a run, in increasing order of precedence: bundle defaults, the host config file, and argv flags. There is no implicit settings.yaml; with no config file and no flags, bundle defaults and argv are the whole story.
Provider is auto-detected from environment variables in this precedence:
ANTHROPIC_API_KEYOPENAI_API_KEYAZURE_OPENAI_API_KEY+AZURE_OPENAI_ENDPOINTOLLAMA_HOST(defaults tohttp://localhost:11434)GOOGLE_API_KEY(GEMINI_API_KEYis also accepted by the provider module,GOOGLE_API_KEYtakes precedence)
github-copilot, openai-chatgpt, chat-completions, and vllm are excluded from this auto-detect
chain -- none of them resolves from a single API-key environment variable. github-copilot reads its
own token chain from the environment (see below). openai-chatgpt has no credential env var at all: it
authenticates via OAuth device-code, caching tokens to ~/.amplifier/openai-chatgpt-oauth.json.
chat-completions needs an endpoint rather than a key (CHAT_COMPLETIONS_BASE_URL, plus optional
CHAT_COMPLETIONS_API_KEY) and has no implicit default endpoint to fall back to. vllm is the same
shape, pointed at a self-hosted or remote vLLM server instead: VLLM_BASE_URL (required), plus
optional VLLM_API_KEY, with no implicit default endpoint either. All four must be selected explicitly
with provider.module in a host config file, rather than silently winning a "first match" race.
Override by passing --config <path> at a host config file that names a provider explicitly.
Deprecated alias:
AZURE_OPENAI_KEY(without_API_) is still accepted as a fallback for backwards compatibility and triggers a one-time stderr warning when used. PreferAZURE_OPENAI_API_KEY. The legacy name will be removed in a future release.
Enumerate what a provider actually serves:
amplifier-agent models list # aggregate across all configured providers
amplifier-agent models list --provider anthropic # one provider only
amplifier-agent models list --latest # only the newest of each family
amplifier-agent providers list # credential-resolution reportingGitHub Copilot resells models from several vendors, so a model id like claude-sonnet-5 is served both by it and by its original vendor. Copilot's models are therefore namespaced on the wire so the two stay separately addressable, and their display names carry a (GitHub) suffix so they are distinguishable in a picker. Native providers keep their bare ids.
claude-sonnet-5 Claude Sonnet 5 (anthropic)
github-copilot/claude-sonnet-5 Claude Sonnet 5 (GitHub) (github-copilot)
The namespace applies only to the HTTP serve surface, where a single model list spans every enabled provider. provider.config.default_model in a host config already names one provider, so it takes the bare id (claude-sonnet-5, not github-copilot/claude-sonnet-5).
For "set once, works everywhere" instead of editing shell rc files, the auth subcommand persists provider credentials at ~/.amplifier-agent/credentials.json (mode 0600):
amplifier-agent auth set anthropic sk-ant-...
amplifier-agent auth set openai sk-...
amplifier-agent auth set azure-openai sk-... --endpoint https://...
amplifier-agent auth set gemini AIza...
amplifier-agent auth list # configured providers, api keys masked
amplifier-agent auth status # diagnose env-vs-file precedence per provider
amplifier-agent auth remove openai # delete a single entry
amplifier-agent auth clear --force # delete the whole fileResolution is env-first, so existing shell-rc workflows keep working unchanged:
- Shell environment variable (
ANTHROPIC_API_KEY,OPENAI_API_KEY, and so on): wins when set ~/.amplifier-agent/credentials.json: fallback for the set-once path- Empty: the caller decides whether the missing credential is an error or a no-op
This matters for hosts that spawn amplifier-agent as a subprocess: once you have run auth set a single time, every subsequent invocation picks the key up automatically, from any terminal, from any directory, with or without exported environment variables.
github-copilotis environment-only. The other providers receive their credential through the mount config, soauth setworks for them. The Copilot provider reads its token directly from the environment and ignores the config value, soauth set github-copilotis refused rather than storing a token the provider can never see. Set one of these instead (first non-empty wins):COPILOT_AGENT_TOKEN,COPILOT_GITHUB_TOKEN,GH_TOKEN,GITHUB_TOKEN.
openai-chatgpthas no static key to store, soauth set openai-chatgptis also refused. It authenticates via OAuth device-code instead: the provider module drives an interactive login at mount time (login_on_mount, default true) and caches tokens to~/.amplifier/openai-chatgpt-oauth.json, refreshing them itself. Requires "Sign in with device code" enabled in the account's ChatGPT Security settings.export GITHUB_TOKEN=$(gh auth token)An existing
ghor VS Code login may already authenticate it through the SDK's cached OAuth, so try it before exporting anything. This is a temporary limitation: the real fix is in the provider module, whose token resolver needs to read the agent-delivered credential from its config before falling back to the environment.auth setsupport returns once that lands.
chat-completionsis environment-only too, but for a different reason. Its "credential" is the targetbase_url, not an API key, so it is read purely from the environment:CHAT_COMPLETIONS_BASE_URL(required — the server to talk to) andCHAT_COMPLETIONS_API_KEY(optional; local servers like llama.cpp, vLLM, LM Studio, and LocalAI commonly need none). The persisted credentials file is not consulted for this provider.export CHAT_COMPLETIONS_BASE_URL=http://localhost:8000/v1 # export CHAT_COMPLETIONS_API_KEY=... # only if your server requires one
vllmis the same shape aschat-completions, environment-only for the same reason. Its "credential" is the targetbase_url-- the self-hosted or remote vLLM server to talk to -- not an API key:VLLM_BASE_URL(required) andVLLM_API_KEY(optional; a local vLLM server needs none, and both the module and the agent fall back to the placeholder"EMPTY"when it is unset, so a keyless server works for bothrunandmodels list). The persisted credentials file is not consulted for this provider either. Unlikechat-completions, which speaks the OpenAI Chat Completions wire,vllmtalks vLLM's OpenAI-compatible Responses API, and supports reasoning models, reasoning-block separation, and tool calling.export VLLM_BASE_URL=http://localhost:8000/v1 # export VLLM_API_KEY=... # only if your server requires one
The file format is a versioned JSON envelope:
Unknown providers and unknown fields round-trip through reads and writes, so future releases can extend the schema without dropping pre-existing user configuration. The file is plaintext, matching aws credentials, gh hosts.yml, and claude/credentials.json. OS keychain integration is a future concern.
Tool calls can request approval before they run. The policy is resolved once, at startup:
-y/--yesapproves every request-n/--nodeclines every requestapproval.modein the host config file ("yes","no", or"prompt")- stdin is a TTY, so prompt
- none of the above, so exit 2 with
approval_unconfigured
# Interactive: prompts on stderr when a tool wants approval
amplifier-agent run "Create /tmp/test.txt containing 'hello'"
# Auto-approve everything
amplifier-agent run -y "Create /tmp/test.txt"
# Auto-deny everything
amplifier-agent run -n "Create /tmp/test.txt"
# Headless with no policy: exits 2, runs nothing
echo "" | amplifier-agent run "Create /tmp/test.txt"Step 5 is deliberate. A headless run with no declared policy used to auto-deny every tool call and still exit 0, which looked like success while doing no work. Headless callers have to say what they want.
The interactive prompt is one line on stderr:
Approve [<kind>] <summary> [y/N]:
<summary> is the tool name when the payload carries one, otherwise a JSON dump of the payload truncated to 80 characters. y or yes in any case approves; anything else, including a bare Enter, declines. There is no cancel-the-turn answer.
The shipped bundle does not currently mount hooks-approval (see ISSUES.md, ISSUE-001), so few tool calls raise a request in practice. The startup policy gate still applies to every run.
Persistent settings live in a JSON file passed as --config <path>, or named by $AMPLIFIER_AGENT_CONFIG. Argv flags beat the file; the file beats bundle defaults.
The smallest useful file, enough to make a headless run legal:
{ "approval": { "mode": "yes" } }Pick a provider and a model:
{
"approval": { "mode": "yes" },
"provider": {
"module": "anthropic",
"config": { "default_model": "claude-sonnet-5" }
}
}Point tool-mcp at a host-managed MCP server file:
{ "mcp": { "configPath": "/var/run/amplifier/mcp.json" } }Add skill sources and tune visibility. Host sources append to the bundle's rather than replacing them:
{
"skills": {
"skills": ["/var/run/amplifier/host-managed-skills"],
"visibility": { "max_skills_visible": 20 }
}
}A subprocess host typically writes one file per agent instance and passes --config <path> on every turn:
{
"approval": { "mode": "yes" },
"provider": { "module": "anthropic", "config": { "default_model": "claude-sonnet-5" } },
"mcp": { "configPath": "/var/run/paperclip/instances/<agent-id>/mcp.json" },
"skills": { "skills": ["/var/run/paperclip/instances/<agent-id>/skills"] }
}Capture the full raw LLM request and response for each turn — a developer diagnostic, off by default:
{ "debug": { "rawLlmPayloads": true } }debug is closed to the single key rawLlmPayloads, which must be a real JSON boolean — a string like "false" is rejected rather than coerced (read as truthy, it would silently enable capture). When on, it folds into the provider config as raw: true, so an explicit provider.config.raw overrides it. Captured payloads have credentials redacted but are otherwise uncapped and untruncated, and land in the session's context-intelligence log:
~/.amplifier-agent/state/workspaces/<workspace>/sessions/<session-id>/context-intelligence/events.jsonl
as llm:request / llm:response events, with the payload under data.raw. That root honours $AMPLIFIER_AGENT_HOME, which relocates the whole tree. What each call captures is the provider module's own observability contract and varies by provider. Treat it as a developer switch, not an audit feature — see spec/host-config.md.
The top level is closed: an unknown key is an error, not a warning. Full schema, merge rules, and error codes are in spec/host-config.md.
Inspect what actually resolved, with source annotations per value:
amplifier-agent config show- Normative provider contract:
spec/providers-and-models.md - Normative host config contract:
spec/host-config.md - Storage locations and workspace scoping:
spec/storage-and-workspace.md
{ "version": 1, "providers": { "anthropic": { "api_key": "sk-ant-..." }, "openai": { "api_key": "sk-..." }, "azure-openai": { "api_key": "...", "endpoint": "https://..." } } }