dotfiles/dot_reasonix/private_config.toml
Anton Volnuhin 5b58460618 Track agent CLI configs, with codex secrets externalized
codex's config.toml held nine live API keys inline under
[mcp_servers.*.env] -- deepseek, gemini, kimi, mimo, qwen, glm, cursor,
an OpenAI key and a Home Assistant token -- so it could not be tracked
as it stood. TOML env tables take literal values only, with no ${VAR}
indirection or env_file, so the keys cannot simply be named there.

Move them to ~/.codex/secrets.env (0600, ignored) and route the four
affected servers through mcp-with-secrets.sh, which sources that file
and execs the real command. Doing it in the wrapper rather than relying
on exported shell vars means it does not depend on how codex handles
environment inheritance, and works on ssh-only boxes with no keychain or
1Password agent, which is why this was preferred over a chezmoi
template.

Adds configs for claude, codex, pi, grok, kimi, kimi-code and reasonix,
and extends .chezmoiignore so credentials, session logs, local state and
.bak files stay out even if a directory is ever added whole.

Held back for still containing live keys: opencode.json (bailian), and
reasonix config.json and .env.
2026-08-06 02:23:49 +03:00

242 lines
11 KiB
TOML

# Reasonix configuration.
# Resolution order: flag > ./reasonix.toml > ~/.reasonix/config.toml > built-in defaults.
# Fields marked user/global only are not overridden by ./reasonix.toml.
# Secrets are named via api_key_env and stored in Reasonix's global .env; never put keys here.
config_version = 5 # schema marker for diagnostics; old versions may ignore it
default_model = "deepseek-flash/deepseek-v4-flash"
language = "EN" # ui/model language; empty = auto-detect from $LANG / $REASONIX_LANG
credentials_store = "auto" # legacy compatibility; provider keys are saved in Reasonix's global .env
[ui]
theme = "auto" # auto|dark|light; CLI colors only; REASONIX_THEME can override per run
# theme_style = "graphite" # graphite|aurora|slate|carbon|nocturne|amber and legacy aliases
# shortcut_layout = "desktop" # classic|desktop; compatibility setting; Shift+Tab toggles Plan, Ctrl+Y toggles YOLO
# cursor_shape = "bar" # block|underline|bar; text input cursor shape
# show_reasoning = true # CLI: show thinking text by default; false = collapsed (toggle with Ctrl+O)
[desktop]
language = "en" # desktop UI language; empty/auto = browser/OS auto-detect
# currency = "USD" # official pricing currency: CNY|USD; empty/auto follows language
layout_style = "workbench" # desktop layout: classic|workbench|creation
theme = "auto" # desktop only: auto|dark|light
terminal_theme = "auto" # integrated terminal: auto|dark|light; auto follows the desktop app
# theme_style = "graphite" # graphite|aurora|slate|carbon|nocturne|amber and legacy aliases
# external_opener = "vscode" # desktop Open control: installed application id
close_behavior = "background" # desktop: quit|background when the window close button is clicked
status_bar_style = "text" # desktop: icon|text metric labels in the bottom status bar
status_bar_items = ["model", "workspace", "git_branch", "cache", "cache_avg", "session_tokens", "turn_tokens", "turn_cost", "session_turns", "context", "compact", "cost", "balance"] # desktop: ordered visible bottom status bar items
default_tool_approval_mode = "auto" # desktop: Ask/Auto/YOLO default for newly-created sessions
check_updates = true # desktop: check for new versions on startup
telemetry = true # desktop: anonymous launch ping + scrubbed next-launch native crash diagnostics; never content
metrics = true # desktop: aggregate quality/lifecycle metrics (anonymous signal/bucket counts); never content
expand_thinking = false # desktop: show reasoning text expanded by default; false = collapsed
display_mode = "standard" # desktop: standard|compact transcript display mode
[telemetry]
cli_metrics = "auto" # CLI content-free usage metrics: auto|on|off; auto requires a local interactive terminal
[notifications]
enabled = false # system notifications for CLI and desktop turns; default off
turn_done = true # notify when a turn finishes
approval_request = true # notify when a tool approval is waiting
ask_request = true # notify when a question is waiting
[network]
proxy_mode = "auto" # auto|env|custom|off; auto currently uses env proxy
# proxy_url = "socks5://127.0.0.1:7890" # optional custom override
# no_proxy = "localhost,127.0.0.1,.local" # honored for proxy_mode = "custom"
[network.proxy]
type = "socks5" # http|https|socks5|socks5h
# server = "127.0.0.1"
# port = 7890
# username = ""
# password = "${REASONIX_PROXY_PASSWORD}" # optional; supports ${VAR} expansion
[environment]
enabled = true # inject a stable startup environment summary into the model prompt
# [environment.tools]
# go = "/opt/homebrew/bin/go" # trusted executable path; workspace-local paths are not auto-executed
[agent]
# system_prompt = """...""" # omit to use the built-in prompt for this version
# system_prompt_file = "prompts/system.md" # project paths stay in <workspace>; user paths may fall back to <reasonix home>
temperature = 0.0
# recovery_model = "deepseek-pro" # optional; falls back to guardian then main model
# reasoning_language = "zh" # visible reasoning language: auto|zh|en
soft_compact_ratio = 0.5 # notice only; keeps cache-first prefix intact
tool_result_snip_ratio = 0.6 # snip stale tool results at this fraction before summary compaction
compact_ratio = 0.8 # try compacting when prompt reaches this fraction
compact_force_ratio = 0.9 # force compacting at this high-water mark
# keep = ["errors"] # compaction keep policy: errors, user_marked
# recent_keep = 2 # minimum recent messages kept verbatim
cold_resume_prune = true # elide stale tool results when reopening a session past the provider cache window
# plan_mode_read_only_commands = ["gh issue view"] # legacy compatibility only; Plan bash uses Permissions
# planner_model = "deepseek-pro" # optional: enable two-model collaboration
# subagent_model = "deepseek-pro" # optional default for runAs=subagent skills
# subagent_models = { review = "deepseek-pro", security_review = "deepseek-pro" } # per-skill overrides
# subagent_effort = "high" # optional default effort for subagents
# subagent_efforts = { review = "max", task = "high" } # per-tool/skill effort overrides
# max_subagent_depth = 2 # nested subagent delegation depth; set 1 to disable nested delegation
# max_subagent_concurrency = 6 # session-wide sub-agent concurrency (task/fleet/skills)
# max_parallel_writers = 3 # concurrent writers with non-overlapping write_paths
# output_style = "explanatory" # explanatory | learning | concise | custom; empty = default
[[providers]]
name = "deepseek-flash"
kind = "openai"
base_url = "https://api.deepseek.com"
model = "deepseek-v4-flash"
api_key_env = "DEEPSEEK_API_KEY"
balance_url = "https://api.deepseek.com/user/balance" # optional; wallet-balance endpoint shown in the status bar
context_window = 1000000 # tokens; compaction triggers near this limit
price = { cache_hit = 0.0028, input = 0.14, output = 0.28, currency = "$" } # provider-wide fallback, per 1M tokens
[[providers]]
name = "deepseek-pro"
kind = "openai"
base_url = "https://api.deepseek.com"
model = "deepseek-v4-pro"
api_key_env = "DEEPSEEK_API_KEY"
balance_url = "https://api.deepseek.com/user/balance" # optional; wallet-balance endpoint shown in the status bar
context_window = 1000000 # tokens; compaction triggers near this limit
price = { cache_hit = 0.003625, input = 0.435, output = 0.87, currency = "$" } # provider-wide fallback, per 1M tokens
[tools]
enabled = [] # empty = all built-in tools
bash_timeout_seconds = 120 # foreground safety cap; set 0 for no tool-local cap
mcp_startup_timeout_seconds = 30 # background initialize + tools/list safety cap; per-plugin overrides may raise it
mcp_call_timeout_seconds = 300 # default MCP call safety cap; per-plugin/tool overrides may raise it
[tools.background_jobs]
stalled_warning_seconds = 900 # warn once per background job after this many quiet seconds; 0 disables
[tools.shell]
# prefer = "auto" # auto|bash|powershell|pwsh; empty/default = auto-detect
# path = "/opt/homebrew/bin/bash" # absolute path to the shell executable; empty = PATH lookup
[lsp]
enabled = true # language server tools; servers launch lazily when used
# [lsp.servers.go]
# command = "gopls"
# args = []
# extensions = [".go"]
[skills]
# paths = ["~/my-skills", "../shared/skills"] # extra custom skill roots
# excluded_paths = ["~/.agents/skills"] # hide convention roots without deleting folders
# max_depth = 3 # nested scan depth; set 1 for legacy root-only discovery
# disabled_skills = ["review"] # hide noisy or unwanted skills
[permissions]
# Per-call gating. mode = writer fallback when no rule matches: ask|allow|deny.
# Readers always default to allow. Precedence: deny > ask > allow > fallback.
# Rules are "Tool" or "Tool(specifier)"; e.g. Bash(go test:*), Edit(src/**).
mode = "ask"
# allow_dynamic_bash = false # advanced opt-in; deny/ask and exact rules still take precedence
# deny = ["Bash(rm -rf*)", "Bash(git push*)"] # hard-blocked in every mode
# allow = ["Bash(go test:*)", "Bash(git status:*)"] # never prompted
# ask = ["Edit(src/**)"] # force a prompt even if otherwise allowed
[sandbox]
# Confine tool blast radius. File-writers (write_file/edit_file/multi_edit/move_file)
# may only write under workspace_root (empty = current dir) and allow_write extras.
# bash = "enforce" jails each command in an OS sandbox when available;
# without one, bash execution is refused. Empty defaults to enforce on macOS/Linux.
# Windows has no OS-level Bash sandbox and fixes bash = "off".
# network allows sandboxed bash egress.
# workspace_root = "" # default: current working directory
# allow_write = ["/tmp"] # extra dirs writers may also modify
# forbid_read = [] # dirs the agent cannot read or list
bash = "enforce"
network = true
[statusline]
# A custom status line: a command whose first stdout line replaces the built-in
# data row. It receives {"model","contextUsed","contextWindow","cwd"} as JSON on stdin.
# command = "my-statusline.sh"
# Bot gateway: multi-channel IM bot for QQ, Feishu/Lark, and WeChat.
[bot]
enabled = false
# model = "" # empty = default_model
tool_approval_mode = "ask" # ask|auto|yolo; yolo skips tool approvals only
max_steps = 25
debounce_ms = 1500
queue_mode = "steer" # steer|followup|collect|interrupt
queue_cap = 20
queue_drop = "summarize" # summarize|old|new
ignore_self_messages = true # ignore bot echo by returned message_id and configured self user ids
[bot.self_user_ids]
qq = []
feishu = []
weixin = []
[bot.control]
enabled = false # local loopback HTTP API for status/send; requires Bearer token
addr = "127.0.0.1:37913"
token_env = "REASONIX_BOT_CONTROL_TOKEN"
[bot.pairing]
enabled = true
request_ttl_minutes = 60
max_pending_per_platform = 3
[bot.allowlist]
enabled = true
allow_all = false
qq_users = []
feishu_users = []
weixin_users = []
qq_approvers = []
feishu_approvers = []
weixin_approvers = []
qq_admins = []
feishu_admins = []
weixin_admins = []
qq_groups = []
feishu_groups = []
weixin_groups = []
[bot.qq]
enabled = false
app_id = ""
app_secret_env = "QQ_BOT_APP_SECRET"
sandbox = false
[bot.feishu]
enabled = false
app_id = ""
domain = "feishu"
app_secret_env = "FEISHU_BOT_APP_SECRET"
verification_token = ""
mode = "webhook"
webhook_port = 8080
require_mention = true
[bot.weixin]
enabled = false
account_id = "default"
token_env = "WEIXIN_BOT_TOKEN"
api_base = "https://ilinkai.weixin.qq.com"
[secrets] # credential protection; user/global only, ./reasonix.toml cannot override
# filter_subprocess_env = false # opt-in; stripping tokens breaks gh, HTTPS git push, npm publish
# protect_sensitive_files = false # opt-in; hiding credential files can break legitimate edit workflows
# External MCP servers. type: "stdio" (default, a subprocess) | "http" | "sse".
# ${VAR} / ${VAR:-default} are expanded from the environment in command/args/env/url/headers.
# [[plugins]]
# name = "example"
# command = "reasonix-plugin-example"
# startup_timeout_seconds = 60 # optional initialize + tools/list cap
# call_timeout_seconds = 600 # optional per-server MCP call timeout
# tool_timeout_seconds = { "generate_video" = 1800 } # raw MCP tool names
# [[plugins]] # a remote server over Streamable HTTP
# name = "stripe"
# type = "http"
# url = "https://mcp.stripe.com"
# headers = { Authorization = "Bearer ${STRIPE_KEY}" }