This document describes the runtime configuration options available in Conductor workflows.
The runtime section of your workflow defines provider settings and global defaults.
workflow:
runtime:
provider: copilot # or 'claude', 'claude-agent-sdk', 'hermes'
default_model: gpt-5.2
temperature: 0.7
max_tokens: 4096
default_reasoning_effort: medium # low | medium | high | xhigh | max (optional)
default_context_tier: default # default | long_context (optional, Copilot only)
idle_timeout_seconds: 90 # Copilot only (optional)
max_idle_recovery_attempts: 5 # Copilot only (optional)
# Provider-specific settings...The default_reasoning_effort field sets a workflow-wide default for model
reasoning / extended-thinking effort that every provider-backed agent inherits
unless it declares its own reasoning.effort override. See
Reasoning Effort for the per-provider translation and
constraints.
The default_context_tier field sets a workflow-wide default for the model's
context-window tier that every provider-backed agent inherits unless it
declares its own context_tier override. See Context Tier
for details. This is a Copilot-only capability.
The idle_timeout_seconds and max_idle_recovery_attempts fields tune the
Copilot provider's idle watchdog: when a session stops emitting SDK events
for idle_timeout_seconds (default 90s), Conductor sends a "please continue"
recovery prompt, up to max_idle_recovery_attempts times (default 5) before
failing the session. A tool call that is still executing suppresses the
watchdog — most tools emit nothing while running, so a stale idle clock
during a long-running tool call usually means the tool is still running, not
that the session is stuck. Suppression is bounded by max_session_seconds
(default 1800s), which is still enforced while a tool is in flight and is the
only limit that can end a genuinely hung tool call. Raise it alongside
idle_timeout_seconds if your workflow has tool calls that legitimately run
longer than 30 minutes. Both idle_timeout_seconds and
max_idle_recovery_attempts are Copilot-only; other providers ignore them.
Uses GitHub Copilot SDK for agent execution.
workflow:
runtime:
provider: copilot
default_model: gpt-5.2
mcp_servers:
web-search:
command: npx
args: ["-y", "open-websearch@latest"]
tools: ["*"]
idle_timeout_seconds: 90
max_idle_recovery_attempts: 5Features:
- Tool support (MCP servers)
- Streaming responses
- GitHub authentication
Models: gpt-5.2, gpt-5.2-mini, o1-preview
Uses Anthropic Claude SDK for agent execution.
workflow:
runtime:
provider: claude
default_model: claude-sonnet-4.5
temperature: 0.7
max_tokens: 4096Features:
- 200K context window (all models)
- Pay-per-token pricing
Models: claude-sonnet-4.5, claude-haiku-4.5, claude-opus-4.5
See: Claude Provider Documentation
Experimental — see Experimental Providers for stability policy.
Uses the NousResearch hermes-agent library for agent execution. Supports any OpenRouter-style model identifier.
workflow:
runtime:
provider: hermes
default_model: anthropic/claude-sonnet-4
max_agent_iterations: 25
max_tokens: 4096
temperature: 0.7Features:
- Access to Anthropic, OpenAI, and OpenRouter models via one provider
- Built-in hermes tool ecosystem (no MCP config required)
- Custom endpoint routing via structured
provider:config
Models: anthropic/claude-sonnet-4, openai/gpt-4o, any OpenRouter provider/model string
See: Hermes Provider Documentation
runtime.provider accepts either the bare string shorthand
(provider: copilot) or a structured object that forwards a
ProviderConfig to the Copilot SDK's create_session(provider=…)
parameter. This lets workflows route the Copilot SDK at:
- Local OpenAI-compatible servers — Ollama, vLLM, LM Studio, llamafile
- Azure OpenAI deployments
- Anthropic-compatible proxies
- Any other OpenAI-compatible REST endpoint
workflow:
runtime:
provider:
name: copilot
type: openai # openai | azure | anthropic
wire_api: completions # completions | responses
base_url: http://localhost:11434/v1
api_key: ${OPENAI_API_KEY:-ollama}
default_model: llama3.1 # required for non-Copilot endpointsAzure OpenAI variant:
workflow:
runtime:
provider:
name: copilot
type: azure
base_url: https://<your-resource>.openai.azure.com
api_key: ${AZURE_OPENAI_API_KEY}
azure:
api_version: "2024-10-21"
default_model: gpt-4oCustom routing activates only when at least one non-name field is
set in YAML. Ambient OPENAI_* environment variables alone will NOT
divert default Copilot traffic — that would be too easy a way to break
a workflow based on unrelated shell state. A bare provider: copilot
always means default GitHub Copilot routing.
Once a structured object opts in, missing fields fall back to env vars in this precedence:
| Field | Env-var chain |
|---|---|
base_url |
COPILOT_PROVIDER_BASE_URL → OPENAI_BASE_URL |
api_key |
COPILOT_PROVIDER_API_KEY (only) |
bearer_token |
COPILOT_PROVIDER_BEARER_TOKEN (only) |
type |
defaults to "openai" when base_url is set |
Ambient OPENAI_API_KEY is intentionally not consulted as an
implicit fallback — that would silently send an OpenAI dev credential
to whatever base_url points at, which is a real credential-leak risk.
Users who want OpenAI-environment-style behavior must opt in
explicitly via api_key: ${OPENAI_API_KEY} interpolation in YAML.
For name: claude, missing fields fall back to Anthropic environment variables:
| Field | Env-var fallback |
|---|---|
base_url |
ANTHROPIC_BASE_URL |
api_key |
ANTHROPIC_API_KEY |
auth_token |
ANTHROPIC_AUTH_TOKEN |
Unlike the Copilot chains above, the two credential rows are not independent.
The Anthropic SDK resolves them as a unit: set either one in YAML and it reads
neither env var. Only base_url falls back on its own.
| Field | copilot |
claude |
claude-agent-sdk |
|---|---|---|---|
base_url |
Supported | Supported | Rejected |
api_key |
Supported | Supported | Rejected |
auth_token |
Rejected | Supported | Rejected |
bearer_token |
Supported | Rejected | Rejected |
type |
Supported | Rejected | Rejected |
wire_api |
Supported | Rejected | Rejected |
headers |
Supported | Rejected | Rejected |
azure |
Supported | Rejected | Rejected |
runtime_url |
Supported | Rejected | Rejected |
runtime_token |
Supported | Rejected | Rejected |
setting_sources |
Rejected | Rejected | Supported |
setting_sources (user / project / local) selects which Claude Code
settings tiers a session may load; it is empty by default and only the
claude-agent-sdk provider reads it, so it is rejected on every other provider
name rather than accepted and dropped. An enabled tier brings that tier's
hooks — see
claude-agent-sdk: skills and ambient settings.
api_key and bearer_token are stored as Pydantic SecretStr — they
redact in model_dump, dashboard payloads, event logs, and
checkpoints. Prefer ${VAR} env interpolation for the values in YAML
so the literal secret never lands in workflow_started events:
api_key: ${OPENAI_API_KEY} # good — interpolated at load time
api_key: sk-aaaaaaaaaaaaaaaa # avoid — literal in yaml_sourceIf both api_key and bearer_token resolve (from any combination of
YAML and env), both are forwarded; the Copilot SDK silently prefers
bearer_token, and conductor logs a warning so the precedence is
visible.
ProviderSettings is frozen after construction. The schema rejects
the following misconfigurations at config load time so they cannot
silently produce a no-op SDK call:
- Structured provider config for other provider names is not yet implemented.
- Unsupported structured provider fields for the selected provider name.
For
name: claude, onlybase_url,api_key, andauth_tokenare supported (fieldstype,wire_api,headers,azure,bearer_token,runtime_url, andruntime_tokenremain Copilot-only and are rejected forclaude). type: azurewithout anazure: { api_version: ... }block (and the reverse:azureblock withouttype: azure).- Anchorless routing fields:
wire_api,type,headers, orazurecannot stand alone — at least one ofbase_url,api_key,bearer_tokenmust also be set (in YAML or via theCOPILOT_PROVIDER_*env vars). - Empty
headers: {}, emptyapi_key: "", emptybearer_token: "", emptyazure: { api_version: null }.
When custom routing activates but every resolved field ends up empty
(for example, the workflow expects COPILOT_PROVIDER_* env vars and
none are set), the resolver raises ProviderError with a clear
message rather than silently routing back to default Copilot.
--provider <name> (and -p) replaces the entire ProviderSettings
with the bare-string default for that name. When YAML had structured
fields, conductor logs a notice telling the user the custom routing
was dropped:
Provider override: claude
Provider override discards structured runtime.provider settings (base_url/type/etc.) from YAML; using SDK defaults.
The resolved provider config is attached to every Copilot
create_session call this provider makes — including the dialog-mode
turns used by agent.dialog evaluators. All sessions hit the same
endpoint, so you can mix custom-routed agents with dialog mode without
worrying about per-call drift.
examples/copilot-local-llm.yaml
demonstrates the full pattern with both Ollama (active) and Azure
OpenAI (commented variant).
By default the Copilot provider spawns its own nested copilot runtime
process (one per provider instance) to run agents. Instead, you can point
Conductor at an already-running Copilot runtime that was started in
server mode by some other process. Conductor then connects to that runtime
and reuses the authenticated runtime process for every agent. Each agent
still gets its own SDK session, and no nested copilot process is spawned.
This is the recommended way to run Conductor inside an external
orchestrator that already owns an authenticated Copilot process. For
example, an orchestrator can launch one authenticated
copilot --headless process and hand Conductor a connection handle, so all of
Conductor's agents/models are just new sessions on that shared server.
Authentication is handled once, at the server — Conductor never needs the
runtime's GitHub credentials. The optional runtime connection token only
authenticates access to the server socket.
workflow:
runtime:
provider:
name: copilot
runtime_url: localhost:3000 # "port", "host:port", or a full URL
runtime_token: ${COPILOT_RUNTIME_TOKEN} # optional shared secret
default_model: gpt-4oruntime_url— where the running runtime is listening. Accepts a bare"port","host:port", or a full URL. URL schemes are parsing syntax only: the SDK opens a raw TCP connection and does not provide TLS.runtime_token— the shared secret the runtime was started with, if any. Stored as aSecretStr(redacted in events/checkpoints/dashboard); prefer${VAR}interpolation. Requiresruntime_url.
The connection also activates from environment variables alone, so an orchestrator can enable it without editing the workflow YAML:
| Field | Env var |
|---|---|
runtime_url |
COPILOT_PROVIDER_RUNTIME_URL |
runtime_token |
COPILOT_PROVIDER_RUNTIME_TOKEN |
The YAML value takes precedence; the environment variable is used as a
fallback when the YAML field is unset. These variables are namespaced under COPILOT_PROVIDER_* on
purpose: unlike ambient OPENAI_* variables, they can safely activate the
connection because they are specific to this feature.
# Orchestrator side, conceptually:
export COPILOT_CONNECTION_TOKEN=<connection-token>
copilot --headless --port 3000 & # one authenticated runtime
export COPILOT_PROVIDER_RUNTIME_URL=localhost:3000
export COPILOT_PROVIDER_RUNTIME_TOKEN="$COPILOT_CONNECTION_TOKEN"
conductor run review.yaml # connects; spawns no nested runtimeruntime_urlmay be combined with custom model-provider routing. The runtime URL selects the CLI transport;base_url/api_key/ related fields are forwarded on each SDK session to select the model endpoint.runtime_tokenrequiresruntime_url(a token with nowhere to connect is a misconfiguration) and, like the other secrets, may not be empty.- The connection token authenticates the socket but does not encrypt it. Keep
the default loopback binding where possible. Remote runtimes require
copilot --headless --host ...plus a trusted private network, firewall, or TLS tunnel. - The external runtime executes against its own host environment. If it runs in another container or machine, make the Conductor workspace available at the same working-directory path.
- Closing the provider does not terminate the external runtime — the
SDK only shuts down runtimes it spawned itself, so the orchestrator-owned
server keeps running. The orchestrator is also responsible for runtime
health checks and restarts: a lost connection to an external runtime
(a
BrokenPipeErrororConnectionResetErrorat the SDK boundary) fails the affected agent immediately (is_retryable=false) and is never retried or respawned by Conductor. This differs from the default spawned runtime, which Conductor restarts automatically after a detected crash (a dead child process, or aBrokenPipeError/ConnectionResetErrorat the SDK boundary) and retries against, up to a fixed cap of 2 consecutive restarts without an intervening successful call (not configurable via YAML or an environment variable). Note this covers a lost connection only; a failed initial connect to an external runtime (e.g. it is not reachable at all) is not classified by this mechanism and surfaces as a generic SDK error instead. - Runtime-spawn-only options (custom CLI path, injected env, etc.) do not apply when connecting to an existing runtime.
examples/copilot-existing-runtime.yaml
demonstrates connecting to an already-running Copilot runtime.
These options work with both providers:
Set the default model for all agents:
workflow:
runtime:
default_model: gpt-5.2 # or claude-sonnet-4.5Override per agent:
agents:
- name: fast_agent
model: claude-haiku-4.5 # Override default
prompt: "Quick task..."Controls randomness (0.0 = deterministic, 1.0 = creative):
workflow:
runtime:
temperature: 0.7 # BalancedRanges:
- Copilot (OpenAI): 0.0 - 2.0
- Claude: 0.0 - 1.0 (enforced by SDK)
Guidelines:
0.0 - 0.3: Factual, deterministic (data extraction, classification)0.4 - 0.7: Balanced (general Q&A, analysis)0.8 - 1.0: Creative (brainstorming, content generation)
Maximum OUTPUT tokens per response:
workflow:
runtime:
max_tokens: 4096 # Optional: defaults to 16384 when unsetLimits:
- Haiku: 4096 max
- Sonnet/Opus: Conductor defaults
max_tokensto 16384 when unset and does not clamp the configured value to the model's advertised cap — a value above the model limit is rejected by the API. The provider-advertisedModelInfo.max_tokensis used only to size the compaction output reserve.
Note: This is output tokens, not context window (200K separate limit)
Conductor exposes a single, unified reasoning.effort knob that controls how
much "thinking" budget the underlying model uses, and translates it to each
provider's native API. Allowed values: low, medium, high, xhigh, max.
Set a workflow-wide default and/or override per agent:
workflow:
runtime:
provider: copilot
default_model: gpt-5.2
default_reasoning_effort: medium # workflow-wide default
agents:
- name: explainer
# No reasoning block — inherits `medium` from the runtime default.
prompt: "Explain {{ workflow.input.topic }}"
- name: architect
reasoning:
effort: high # per-agent override wins
prompt: "Design a system for {{ workflow.input.topic }}"Per-agent overrides always win over the workflow-wide default. The
reasoning.effort field is only valid on standard agent-type agents; it
is rejected on script, human_gate, workflow, wait, and terminate
agents (none of which call a model).
-
Copilot — Forwards the chosen effort as
reasoning_efforttoCopilotClient.create_session. The value is validated against the model's advertisedsupported_reasoning_effortscapability metadata; aValidationErroris raised at startup if the model does not support the requested effort. Validation is skipped in mock mode or when capability metadata is unavailable. -
Claude — Enables Anthropic's extended thinking via
messages.create(thinking={"type": "enabled", "budget_tokens": N})with the following effort → budget mapping:Effort Budget tokens low2 048 medium8 192 high16 384 xhigh32 768 max59 904 The
maxbudget is pinned to64000 - 4096, which is the largest budget that still leaves the default answer headroom under the 64000-token cap (atmax,max_tokenslands exactly on the cap).Extended thinking is only valid on thinking-capable models, including
claude-3-7-*,claude-opus-4*,claude-sonnet-4*, andclaude-haiku-4*formats. AValidationErroris raised otherwise. The provider also auto-coercestemperatureto1.0(required by the Anthropic API for extended thinking, logged at INFO) and bumpsmax_tokensto at leastbudget + 4096, capped at64000(logged at INFO when clamped). Forlowandmediumefforts, because the budget plus headroom is below 16384, the defaultmax_tokensof 16384 is sent.
Reasoning / thinking content emitted by the model is surfaced via
agent_reasoning events and rendered in the dashboard, JSONL logs, and
-vv console output for both providers.
Some models expose a larger context window (e.g. a 1M-token tier) selected via
a separate session parameter rather than the model name. Conductor surfaces
this as a unified context_tier knob. Allowed values: default,
long_context.
Use long_context for heavy-reasoning agents that ingest large evidence
(multi-MB logs, many candidate source files) and would otherwise truncate at
the default (~200K) tier.
context_tier composes independently with reasoning.effort — they map to two
separate create_session kwargs, so an agent may set both.
Set a workflow-wide default and/or override per agent:
workflow:
runtime:
provider: copilot
default_context_tier: default # workflow-wide default
agents:
- name: triage
# No context_tier — inherits `default` from the runtime default.
prompt: "Triage {{ workflow.input.topic }}"
- name: analyze
context_tier: long_context # per-agent override wins
reasoning:
effort: high # composes with context_tier
prompt: "Deeply analyze {{ workflow.input.topic }}"Per-agent overrides always win over the workflow-wide default. The
context_tier field is only valid on standard agent-type agents; it is
rejected on script, human_gate, and workflow agents (none of which call a
model).
- Copilot — Forwards the chosen tier as
context_tiertoCopilotClient.create_session. No static capability validation is performed; the SDK accepts or rejects the value at session creation. - Other providers — The value is ignored; there is no equivalent knob.
Configure Model Context Protocol (MCP) servers for tool access. Both the Copilot and Claude providers support MCP tools.
workflow:
runtime:
provider: copilot
mcp_servers:
web-search:
command: npx
args: ["-y", "open-websearch@latest"]
tools: ["*"] # All tools, or ["search", "scrape"]
context7:
command: npx
args: ["-y", "@upstash/context7-mcp@latest"]
tools: ["*"]Provider note: The Claude provider supports
stdioservers only. HTTP and SSE servers are Copilot-only.
For full details on server types, tool filtering, environment variables, and OAuth authentication, see the MCP Tools guide.
Control how context flows between agents:
workflow:
context:
mode: accumulate # or 'last_only', 'explicit'
max_tokens: 4000
trim_strategy: drop_oldest # or 'truncate', 'summarize'accumulate (default):
- All prior agent outputs available
- Good for synthesis workflows
- Can grow large quickly
last_only:
- Only previous agent's output
- Good for linear workflows
- Minimal token usage
explicit:
- Only declared inputs available
- Good for complex workflows
- Maximum control, minimal tokens
Example:
workflow:
context:
mode: explicit
agents:
- name: agent2
input:
- workflow.input.question # Explicit declaration
- agent1.output.summarySafety limits prevent runaway execution:
workflow:
limits:
max_iterations: 10 # Default: 10, max: 500
timeout_seconds: 600 # Default: None (unlimited)
budget_usd: 5.00 # Default: None (no budget tracking)
budget_mode: audit # Default: audit. Options: audit, enforcemax_iterations:
- Prevents infinite loops
- Counts agent executions in routing cycles
timeout_seconds:
- Total workflow timeout
- Includes all agent executions
budget_usd and budget_mode:
- Tracks cumulative cost and acts when the budget is exceeded
auditmode (default): emits abudget_exceededevent and logs a warning, but the workflow continues — use this to discover cost profilesenforcemode: emits abudget_exceededevent, saves a checkpoint, and stops the workflow with aBudgetExceededError. Resuming withconductor resumestarts a fresh budget window (cumulative spend resets to $0), so raising the budget first is optional- Sub-workflow spend is merged into the parent budget, so a parent-level
budget accounts for delegated
type: workflowcost - When
budget_usdis not set, no budget tracking occurs
Recommended graduation path:
- Run workflows without a budget to see costs in the summary
- Add
budget_usdinauditmode to track overshoots without breaking workflows - Switch to
enforcemode once you know your cost profile
workflow:
name: claude-example
runtime:
provider: claude
default_model: claude-sonnet-4.5
temperature: 0.7
max_tokens: 4096
context:
mode: explicit
limits:
max_iterations: 15
timeout_seconds: 600
agents:
- name: classifier
model: claude-haiku-4.5 # Fast model override
input: [workflow.input.text]
prompt: "Classify: {{ workflow.input.text }}"
- name: analyzer
model: claude-sonnet-4.5 # Use default
input: [workflow.input.text, classifier.output]
prompt: "Analyze based on classification..."workflow:
name: copilot-example
runtime:
provider: copilot
default_model: gpt-5.2
temperature: 0.7
mcp_servers:
web-search:
command: npx
args: ["-y", "open-websearch@latest"]
tools: ["*"]
context:
mode: accumulate
max_tokens: 8000
limits:
max_iterations: 10
timeout_seconds: 300
agents:
- name: researcher
tools: [web_search]
prompt: "Research {{ topic }}"
- name: synthesizer
tools: [] # No tools needed
prompt: "Synthesize findings..."export ANTHROPIC_API_KEY=sk-ant-...# Configured via GitHub authentication
# No environment variable neededexport CONDUCTOR_LOG_LEVEL=DEBUG # INFO, DEBUG, WARNING, ERROR- Default to balanced models:
claude-sonnet-4.5orgpt-5.2 - Use fast models for simple tasks:
claude-haiku-4.5for classification - Reserve premium models:
claude-opus-4.5oro1-previewfor complex reasoning
- Low (0.0-0.3): Data extraction, classification, deterministic tasks
- Medium (0.4-0.7): General Q&A, balanced workflows
- High (0.8-1.0): Creative writing, brainstorming, diverse outputs
- Use
explicitmode for multi-agent workflows (reduce token costs) - Use
accumulatefor synthesis workflows (need full history) - Use
last_onlyfor linear pipelines (minimal overhead)
- Limit
max_tokensto minimum needed - Use Haiku for high-volume simple tasks
- Use
context: mode: explicitto reduce input tokens
- Set conservative limits initially (
max_iterations: 10) - Use timeout to prevent long-running workflows
- Set a cost budget — start with
budget_usdinauditmode to learn your cost profile, then switch toenforce - Test with dry-run before production
Always set max_tokens:
runtime:
max_tokens: 16384Claude enforces stricter range than OpenAI:
runtime:
temperature: 1.0 # Max for Claude (OpenAI allows 2.0)Check model name spelling:
# Good
default_model: claude-sonnet-4.5
# Bad
default_model: claude-3.5-sonnet # Wrong: dot instead of dashThe Claude provider only supports stdio MCP servers. If you are using http or sse servers, switch to the Copilot provider or use a stdio-based server instead. See the MCP Tools guide for provider-specific details.
Unlike everything above — which lives in a workflow's own YAML — some
settings are cross-cutting and apply to every conductor invocation on the
machine, regardless of which workflow is running. These live in a separate
TOML file, alongside the existing ~/.conductor/registries.toml.
~/.conductor/config.toml
Respects the CONDUCTOR_HOME environment variable the same way
registries.toml does: when set, the file is read from
$CONDUCTOR_HOME/config.toml instead. A missing file is normal — every
setting defaults cleanly, and conductor run is entirely unaffected by its
absence.
Read-only in v1. There is no conductor config set and no in-process
writer — the file is hand-edited. A malformed file only breaks an explicit
command that reads it (like conductor fleet prune with no --keep-last
override); it never breaks conductor run or conductor resume, which
swallow a settings load failure and simply skip the feature the setting
configures.
Controls the Fleet Manager's opportunistic event-log retention sweep —
bounding the otherwise-unbounded $TMPDIR/conductor/ directory of JSONL
event logs (one per run) that accumulates over time.
[fleet.retention]
enabled = true
keep_last = 200| Key | Default | Description |
|---|---|---|
enabled |
true |
Whether conductor run / conductor resume opportunistically prune old event logs at startup. |
keep_last |
200 |
Number of most-recent event logs to retain. A value of 0 or negative is treated as "prune nothing," not "delete everything" — mirroring the same keep_last semantics used by periodic checkpoint rotation (see Periodic Checkpoints). |
Retention never deletes:
- The
checkpoints/subdirectory (also under$TMPDIR/conductor/) or anything inside it. - An event log still referenced by a currently-running (or currently resuming) workflow.
- The
.bg.stderr.log/.bg.stdout.logcompanion files of a retained--web-bgrun's event log — the three artefacts of one run are always kept or removed together.
keep_last also bounds a run's terminal record — the small JSON
tombstone at ~/.conductor/runs/terminal/<run_id>.json that lets a
completed run's outcome still be looked up by run_id after its process
has exited (see Fleet Manager: Terminal records).
A terminal record is pruned or kept in the same sweep pass as its event
log, matched by run_id, so the two never drift apart — a record can't
outlive the log it points at. A terminal record whose event log has
already disappeared is bounded separately, by the same keep_last,
newest-first by when its run actually ended.
Consequence: pruning an event log makes that run's history unavailable to
conductor replay—replayreads the JSONL event log directly, so once it is deleted there is nothing left to replay. Setkeep_lastgenerously if you rely onreplayfor older runs.
enabled = false disables the automatic startup sweep (it defaults to
true). conductor fleet prune (see the CLI reference)
is the explicit manual entry point and always works regardless of this
setting — pass --keep-last to override the configured value for a single
invocation, or --dry-run to preview what would be deleted without
actually deleting anything.