Skip to content

Configuration

File location and loading

flowchart TD
  A["XUN_HOME, else ./.xun"] --> B["config.json"]
  B --> C["deep-merged onto the built-in template"]
  C --> D["substitute ${XUN_...} placeholders"]
  D --> E["pydantic validation"]
  E --> F["Agent.config takes a deep clone"]
  • get_home_dir(): XUN_HOME wins, otherwise .xun/ under the current working directory. The container image pins XUN_HOME=/.xun.
  • The same directory also holds: conversation/ (history), extensions/ (extensions), x/xunx.db (the xunx user database).
  • When xunc / xunx create a container they copy only HOME_COPY_INCLUDE (config.json, extensions) from the host xun home.
  • xun_version() (exported at package level) returns the installed release version, or None when running from source without package metadata; the extension version gate relies on that, see Extension system.
  • The reading entry point load_config(force_reload: bool = False) -> AgentConfig caches in-process; Agent.config is a deep copy of it by default, so changing one agent's configuration does not affect the global one.
  • load_dotenv() is called while loading; keep secrets in .env.
  • There is no write-back API: config.json is written by hand by the user; to_json() exists only for the /config printout, and changes made by /yolo or by extensions live in memory only.

Structure and fields

Every configuration model is extra="forbid": a misspelled field name raises an error instead of being silently ignored.

Field Type Default Description
auto_confirm bool ${XUN_AUTO_CONFIRM}, falls back to false automatically confirm prompts (choices take the default option, source="auto"); does not write to the allowlist; the ask_preference tool skips auto-confirmation and always asks
enable_extensions bool true whether extensions are loaded; internal helper agents (risk assessment, summarisation) turn it off themselves
provider.openai_base_url str ${XUN_OPENAI_BASE_URL} OpenAI-compatible endpoint, an error is raised when missing
provider.openai_api_key str ${XUN_OPENAI_API_KEY} the API key, an error is raised when missing
model.name str ${XUN_OPENAI_MODEL}, falls back to "" when empty, the first entry of models.list() is taken during initialization (a warning is raised when there is more than one, an error when there is none)
model.capabilities set["vision"] ["vision"] the value domain is only vision for now (ModelCapabilityType), any other value fails validation; used to filter tools by required_capabilities
model.temperature float \| None None when None, the parameter is not sent
model.reasoning_field str \| None None reasoning or reasoning_content; None means detect it automatically on first sight and cache the result
model.reasoning_effort "none"\|"minimal"\|"low"\|"medium"\|"high"\|"xhigh"\|"max" | None None some models support only a few of these levels
auto_compact.enabled bool true switch for automatic compaction
auto_compact.token_threshold int 192000 compaction is triggered once the tokens of the previous model call exceed this value

The built-in template (the configuration file is deep merged over it field by field):

{
  "auto_confirm": "${XUN_AUTO_CONFIRM}",
  "enable_extensions": true,
  "auto_compact": { "enabled": true, "token_threshold": 192000 },
  "provider": {
    "openai_base_url": "${XUN_OPENAI_BASE_URL}",
    "openai_api_key": "${XUN_OPENAI_API_KEY}"
  },
  "model": { "name": "${XUN_OPENAI_MODEL}", "capabilities": ["vision"] }
}

Placeholder rules

  • A placeholder must start with XUN_, otherwise RuntimeError: Invalid placeholder ....
  • Resolution order: os.environ → the built-in fallback table (XUN_OPENAI_MODEL="", XUN_AUTO_CONFIRM="false") → if neither is present, RuntimeError: Missing environment variable ....
  • Substitution happens before JSON parsing, and the substituted value is always a string, converted to bool/int by pydantic in lax mode. So XUN_AUTO_CONFIRM="" (present but empty) is not rescued by the fallback value and fails during validation.

Environment variable list

Variable Read by Purpose
XUN_HOME get_home_dir() root directory for configuration/extensions/history; excluded when container environment variables are forwarded
XUN_OPENAI_BASE_URL configuration template the endpoint
XUN_OPENAI_API_KEY configuration template the API key
XUN_OPENAI_MODEL configuration template model name, auto-detected when empty
XUN_AUTO_CONFIRM configuration template automatic confirmation
_XUN_DEFAULT_MAX_ITER agent.py the default max_iterations of execute() (512 built in)
_XUN_INFO_TOOL_OVERRIDE toolbox.py print tool override/replacement information
XUN_* / _XUN_* container entry points the pattern always forwarded by xunc --env / xunx serve --env

Names starting with _XUN_ are internal switches (get_internal_env() / get_internal_env_bool()), not user-facing configuration options.

Common ways to change it

{
  "model": { "name": "my-model", "temperature": 0.2, "reasoning_effort": "low" },
  "auto_compact": { "token_threshold": 64000 }
}
export XUN_OPENAI_BASE_URL=https://api.example.com/v1
export XUN_OPENAI_API_KEY=sk-...
export XUN_AUTO_CONFIRM=true
from xun import Agent
from xun.config import load_config

cfg = load_config().clone()   # config.json (and .env), deep-copied
cfg.model.name = "my-model"
cfg.auto_compact.enabled = False
agent = Agent(config=cfg)
# $XUN_HOME/extensions/use_weak_model.py
"""Pin a small model for every agent."""
from xun import ExtensionContext

def setup_extension(ctx: ExtensionContext) -> None:
    ctx.agent.config.model.name = "small-model"

Extensions run before the model is auto-detected, so an override here takes effect.

Prompts

The system prompt is not a configuration option but a constant in src/xun/prompt.py: the main agent uses get_system_prompt(), sub-agents use get_subagent_prompt(); compaction has its own templates (see Session and auto compaction). To replace it:

agent = setup_agent(default_system_prompt=False)
agent.system("You are a terse assistant. Answer in one sentence.")

system() overwrites message 0 of the session and can be called again at any time (summary compaction rewrites this message as well). The AGENTS.md convention used by compaction also comes from the prompt text: the framework asks the agent to look at AGENTS.md in the working directory first and to follow it — this is a prompt-level convention, the code does not parse that file.