返回 CodeWhale
config.example.toml
根目录 / config.example.toml
1 # ╔══════════════════════════════════════════════════════════════════════════════╗
2 # ║ Codewhale Configuration ║
3 # ║ ║
4 # ║ Terminal coding agent for any model — open models first. ║
5 # ╚══════════════════════════════════════════════════════════════════════════════╝
6
7 # See `docs/CONFIGURATION.md` for how config is loaded (profiles, env overrides, etc.).
8
9 # ─────────────────────────────────────────────────────────────────────────────────
10 # Active provider + DeepSeek defaults
11 # ─────────────────────────────────────────────────────────────────────────────────
12 # Choose which provider to use by default. Per-provider credentials live in the
13 # `[providers.*]` sections near the bottom of
14 # this file — keeping both stored at once means `/provider deepseek` and
15 # `/provider nvidia-nim` (or `--provider openai`, `--provider wanjie-ark`,
16 # `--provider volcengine`, `--provider openrouter`, `--provider xiaomi-mimo`,
17 # `--provider fireworks`, `--provider siliconflow`, `--provider siliconflow-CN`,
18 # `/provider arcee`, `/provider moonshot`, `/provider qianfan`, `/provider sglang`, `/provider vllm`,
19 # `/provider ollama`, `/provider huggingface`, `/provider stepfun`, `/provider openmodel`,
20 # `/provider opencode-go`, `/provider opencode-zen`, `/provider meta`, `/provider xai`) toggle without having to re-enter keys. Top-level
21 # `api_key` / `base_url` are
22 # still read as DeepSeek defaults when `[providers.deepseek]` is absent
23 # (backward compatibility).
24 provider = "deepseek" # deepseek | deepseek-cn | deepseek-anthropic | nvidia-nim | openai | atlascloud | wanjie-ark | volcengine | openrouter | orcarouter | xiaomi-mimo | novita | fireworks | siliconflow | siliconflow-CN | arcee | moonshot | zai | stepfun | minimax | sglang | vllm | ollama | ollama-cloud | huggingface | modelscope | together | qianfan | openai-codex | anthropic | openmodel | deepinfra | sakana | longcat | opencode-go | opencode-zen | meta | xai | mistral | telecomjs | google | edenai | zenmux | csdn | concentrate | modelstudio-token-plan | modelstudio-coding-plan | codewhale
25 api_key = "YOUR_DEEPSEEK_API_KEY" # must be non-empty
26 base_url = "https://api.deepseek.com/beta"
27 # provider = "deepseek-cn" # legacy alias (official host is still https://api.deepseek.com)
28 # base_url = "https://api.deepseek.com" # opt out of DeepSeek beta features
29 # Optional custom model request headers for OpenAI-compatible gateways.
30 # Authorization and Content-Type are managed by the client and cannot be overridden here.
31 # http_headers = { "X-Model-Provider-Id" = "your-model-provider" }
32
33 # ─────────────────────────────────────────────────────────────────────────────────
34 # Default Models
35 # ─────────────────────────────────────────────────────────────────────────────────
36 # DeepSeek V4 family:
37 # deepseek-v4-pro — flagship reasoning model on DeepSeek Platform
38 # deepseek-v4-flash — fast, cost-efficient (legacy aliases: deepseek-chat, deepseek-reasoner)
39 # deepseek-ai/deepseek-v4-pro — NVIDIA NIM-hosted Pro model ID
40 # deepseek-ai/deepseek-v4-flash — NVIDIA NIM-hosted Flash model ID
41 # deepseek/deepseek-v4-pro — default OpenRouter DeepSeek model ID
42 # arcee-ai/trinity-large-thinking — OpenRouter Arcee Trinity Large Thinking
43 # xiaomi/mimo-v2.5-pro — OpenRouter Xiaomi MiMo 2.5 Pro
44 # xiaomi/mimo-v2.5 — OpenRouter Xiaomi MiMo 2.5
45 # z-ai/glm-5.1 — OpenRouter Z.AI GLM 5.1
46 # z-ai/glm-5.2 — OpenRouter Z.AI GLM 5.2
47 # z-ai/glm-5.3 — OpenRouter Z.AI GLM 5.3 (live on Z.ai since 2026-08-13;
48 # metadata inherited from 5.2, unpriced)
49 # z-ai/glm-5.3-flash — OpenRouter Z.AI GLM 5.3 Flash (1M multimodal; $0.15/$0.50 list)
50 # z-ai/glm-5-turbo — OpenRouter Z.AI GLM 5 Turbo (scout fast sibling of 5.2)
51 # GLM-5.3 — default direct Z.AI Coding Plan model (live since 2026-08-13;
52 # metadata inherited from 5.2, unpriced)
53 # GLM-5.3-Flash — direct Z.AI GLM 5.3 Flash (faster/explore sibling of 5.3)
54 # GLM-5.2 — direct Z.AI GLM 5.2 (previous default; explicit selections keep it)
55 # GLM-5.1 — direct Z.AI smaller model
56 # GLM-5-Turbo — direct Z.AI fast model (scout fast sibling of 5.2)
57 # step-3.7-flash — default direct StepFun / StepFlash model ID
58 # kimi-k3 — direct Moonshot K3 model ID (1M context)
59 # kimi-k2.7-code — default direct Moonshot/Kimi K2.7 model ID
60 # k3 — Kimi Code membership K3 API model ID
61 # kimi-for-coding — Kimi Code membership K2.7 compatibility ID
62 # kimi-for-coding-highspeed — Kimi Code membership high-speed roster ID
63 # gpt-4.1 — default generic OpenAI-compatible model ID
64 # deepseek-ai/deepseek-v4-flash — default AtlasCloud model ID
65 # deepseek-reasoner — default Wanjie Ark model ID
66 # mimo-v2.5-pro — default Xiaomi MiMo model ID
67 # mimo-v2.5-pro-ultraspeed — Xiaomi MiMo V2.5 Pro UltraSpeed chat model ID
68 # mimo-v2.5 — Xiaomi MiMo V2.5 Omni model ID
69 # mimo-v2.5-tts — Xiaomi MiMo speech/TTS model ID
70 # mimo-v2.5-tts-voicedesign — Xiaomi MiMo voice-design TTS model ID
71 # mimo-v2.5-tts-voiceclone — Xiaomi MiMo voice-clone TTS model ID
72 # accounts/fireworks/models/deepseek-v4-pro — Fireworks AI Pro model ID
73 # deepseek-ai/DeepSeek-V4-Pro — SiliconFlow hosted Pro model ID
74 # deepseek-ai/DeepSeek-V4-Flash — SiliconFlow hosted Flash model ID
75 # trinity-large-thinking — default direct Arcee AI API model ID
76 # trinity-large-preview — direct Arcee AI API model ID
77 # deepseek-ai/DeepSeek-V4-Pro — SGLang self-hosted Pro model ID
78 # deepseek-ai/DeepSeek-V4-Flash — SGLang self-hosted Flash model ID
79 # auto — auto-select between flash and pro based on task complexity.
80 # Complex tasks (debugging, refactoring, architecture) → pro;
81 # simple tasks (lookups, formatting, Q&A) → flash.
82 default_text_model = "deepseek-flash"
83
84 # ─────────────────────────────────────────────────────────────────────────────────
85 # Thinking Mode (DeepSeek V4 reasoning effort)
86 # ─────────────────────────────────────────────────────────────────────────────────
87 # "off" — disables chain-of-thought (thinking.type = disabled)
88 # "low" — compat-maps to "high" server-side
89 # "medium" — compat-maps to "high" server-side
90 # "high" — reasoning_effort = high (DeepSeek default)
91 # "max" — reasoning_effort = max (deepest reasoning)
92 #
93 # Ctrl+T in the TUI cycles the reasoning tier. The header shows the current
94 # tier as a ⚡ chip. (Shift+Tab cycles the permission posture — Ask /
95 # Auto-Review / Full Access — not the reasoning tier.)
96 reasoning_effort = "max"
97
98 # NOTE: `show_thinking`, `thinking_default_expanded`, `thinking_preview_lines`,
99 # `help_expand_groups`, `pin_last_prompt`, and `cost_currency` live in
100 # `~/.codewhale/settings.toml`, not here — `Config` has no such fields and
101 # unknown keys are ignored. See crates/tui/src/settings.rs.
102 #
103 # Density (Grok-like compact defaults; turn these up if you want more shown):
104 # thinking_preview_lines = 2 # 0 header-only, 10 older dump
105 # thinking_default_expanded = false
106 # help_expand_groups = false # true = F1 starts fully expanded
107 # pin_last_prompt = true
108 # show_tool_details = false
109
110 # Signed catalog overlays are optional and inactive without approved trust keys.
111 # Explicit route/model selections and provider-owned rosters keep priority.
112 # CODEWHALE_DISABLE_CLOUD_FACTS=1 disables cache, local files and network too.
113 [cloud_facts]
114 enabled = false
115 channel = "stable"
116 ttl_hours = 6
117 # url = "https://codewhale.net/api/facts/v1/{channel}"
118
119 # ─────────────────────────────────────────────────────────────────────────────────
120 # Startup update check
121 # ─────────────────────────────────────────────────────────────────────────────────
122 # The TUI checks for newer Codewhale releases in the background at startup.
123 # Set check_for_updates = false in managed or air-gapped environments.
124 # The result is cached in ~/.codewhale/update-check.json, so the network is
125 # touched at most once per check_interval_hours while the notice still shows
126 # on every launch. Set 0 to check on every launch.
127 # Checks are skipped entirely in CI, and when CODEWHALE_NO_UPDATE_CHECK or
128 # NO_UPDATE_NOTIFIER is set.
129 # update_uri may point at a GitHub-compatible latest-release JSON endpoint.
130 [update]
131 check_for_updates = true
132 check_interval_hours = 24
133 # update_uri = "https://internal.mirror.example/codewhale/releases/latest"
134
135 # ─────────────────────────────────────────────────────────────────────────────────
136 # Hotbar slots (#2061 / #2064)
137 # ─────────────────────────────────────────────────────────────────────────────────
138 # Optional 1-8 workbar hotbar bindings. Since #3807 a missing `hotbar` key
139 # renders NO bar — the Hotbar is hidden until you configure [[hotbar]]
140 # tables here (or via `/hotbar`). An explicit `hotbar = []` also disables it.
141 #
142 # Invalid slots are skipped with a warning, duplicate slots use the last entry,
143 # and unknown actions are preserved so the UI can show a disabled entry.
144 # Slash commands can be bound as slash.<name>, for example slash.workflow.
145 # `/hotbar on` writes the default slots: slash.workflow, slash.goal, slash.auto,
146 # then Plan/Work/Operate, palette, and workbar toggle. Commands that require arguments
147 # pre-fill the composer instead of running incomplete.
148 #
149 # [[hotbar]]
150 # slot = 1
151 # label = "voice"
152 # action = "voice.toggle"
153 #
154 # [[hotbar]]
155 # slot = 2
156 # action = "session.compact"
157 #
158 # [[hotbar]]
159 # slot = 3
160 # label = "mode"
161 # action = "slash.mode"
162
163 # ─────────────────────────────────────────────────────────────────────────────────
164 # Paths
165 # ─────────────────────────────────────────────────────────────────────────────────
166 # New installs write product state under ~/.codewhale/. Existing ~/.deepseek/
167 # files are still read as compatibility fallbacks when the .codewhale file is
168 # absent.
169 skills_dir = "~/.codewhale/skills"
170 mcp_config_path = "~/.codewhale/mcp.json"
171 notes_path = "~/.codewhale/notes.txt"
172
173 # Anchors the native memory store. The filename itself is not written: under
174 # the Native backend (the only backend) the store is re-rooted to
175 # `<parent-of-this-path>/memory/global/MEMORY.md`. With the default below that
176 # resolves to `~/.codewhale/memory/global/MEMORY.md` (plus workspace-scoped
177 # files and a rebuildable SQLite FTS5 index). See docs/MEMORY.md.
178 memory_path = "~/.codewhale/memory.md"
179
180 # instructions = ["./AGENTS.md", "~/.codewhale/global.md"]
181 #
182 # Optional list of additional instruction files concatenated into the
183 # system prompt in declared order (#454). Useful for layering
184 # repo-specific rules on top of a global preferences file. Each entry
185 # is expanded so `~` and env vars work; missing files are skipped with
186 # a tracing warning. Files are capped at 100 KiB per entry.
187 #
188 # Project-level config (.codewhale/config.toml in the workspace) replaces
189 # the user-level array wholesale rather than merging — list `~/global.md`
190 # inside the project array if you want both. An explicit empty array
191 # (`instructions = []`) clears the user list for the current repo.
192
193 # ─────────────────────────────────────────────────────────────────────────────────
194 # User memory (#489) — opt-in. When enabled, the TUI loads the native store
195 # derived from memory_path (see above), injects a bounded recall block into
196 # the system prompt, intercepts `# foo` in the composer, and registers the
197 # `remember` / `memory_search` / `memory_get` tools.
198 # ─────────────────────────────────────────────────────────────────────────────────
199 [memory]
200 # enabled = true # turn the feature on (default: false)
201 # Override the env-var equivalent: `DEEPSEEK_MEMORY=on`
202
203 # Xiaomi MiMo speech/TTS defaults. Also configurable with
204 # XIAOMI_MIMO_SPEECH_OUTPUT_DIR / MIMO_SPEECH_OUTPUT_DIR.
205 [speech]
206 # output_dir = "./speech"
207
208 # ─────────────────────────────────────────────────────────────────────────────────
209 # Reasoning-only recovery
210 # ─────────────────────────────────────────────────────────────────────────────────
211 # When a reasoning model returns only hidden thinking without any answer text
212 # or tool call, the engine can re-request the answer automatically.
213 # Set max_reprompts = 0 to disable automatic recovery entirely.
214 # [reasoning_only]
215 # max_reprompts = 2
216 # reprompt_message = "So, what's up ? Keep running !"
217
218 # ───────────────────────────────────────────────────────────────────────────
219 # Model-bound key redaction ([redaction])
220 # ───────────────────────────────────────────────────────────────────────────
221 # Codewhale masks credential-looking values in tool output before it reaches
222 # the model (the "model boundary"), so a file that contains a configured API
223 # key, a bare provider token, or a credential-shaped opaque string never leaks
224 # those bytes to the model. Leave this enabled unless the model must read and
225 # edit files that contain real credentials.
226 #
227 # Disabling is a security decision, so it is never a plain flag:
228 # * Set model_bound = "disabled" here, restart Codewhale, and the startup
229 # gate asks twice - a first confirmation, then a red "are you really
230 # sure?" stage. Only the second confirmation takes effect, and it applies
231 # on later launches while model_bound stays "disabled".
232 # * Going back to "enabled" - or rewriting config.toml after the
233 # confirmation - invalidates it: requesting "disabled" again always
234 # asks for a fresh confirmation.
235 # * Until a confirmation exists - including in non-interactive/headless
236 # runs, which never confirm anything - masking stays on regardless of
237 # this key. Choosing "keep masking on" on the gate leaves the key
238 # untouched, so the next launch asks again.
239 # * The value is forgiving: false/"off" mean "disabled"; true/"on" mean
240 # "enabled".
241 # [redaction]
242 # model_bound = "enabled" # mask keys before they reach the model (default)
243 # model_bound = "disabled" # request the opt-out (restart + confirm required)
244
245 # Native tool catalog controls (#2076). By default only the core tool surface
246 # is loaded into the model context; less common native tools are discoverable
247 # through ToolSearch and loaded on first use.
248 # [tools]
249 # always_load = ["git_show", "notify"]
250 #
251 # `request_user_input` payload ceilings (#5949). The model may ask up to
252 # `user_input_max_questions` questions per call, each offering up to
253 # `user_input_max_options` options. Out-of-range values are clamped to the
254 # supported ranges (1..=10 and 2..=10) with a warning.
255 # user_input_max_questions = 6
256 # user_input_max_options = 4
257 #
258 # Seconds a question or an approval decision waits before it cancels (#6003).
259 # 0 disables the timeout entirely; values above 86400 (24h) are clamped.
260 # user_input_timeout_seconds = 300
261 #
262 # Bound an interactive approval card's wait (#6101). When the window elapses
263 # the card resolves to deny (fail-closed) and the transcript says the bound
264 # denied the call; omitted or 0 waits indefinitely.
265 # [approval]
266 # timeout_seconds = 300
267
268 # ─────────────────────────────────────────────────────────────────────────────────
269 # Product telemetry — optional, enabled by default with a startup disclosure
270 # ─────────────────────────────────────────────────────────────────────────────────
271 # Anonymous usage analytics are enabled when this key is omitted, unless an
272 # existing opt-out or a run-scoped kill switch disables them. No first-run
273 # affirmative opt-in is required. See docs/TELEMETRY.md for the complete schema.
274 # Local session history and usage diagnostics work with telemetry disabled;
275 # they require no account or hosted analytics service.
276 #
277 # Setting it to `false` here is an answer, not just a flag: it deletes the
278 # random install id, truncates every buffered event, and leaves a tombstone
279 # that a session already running re-checks before it sends anything. Every
280 # later run re-reads this key and re-asserts that tombstone, so it stands for
281 # as long as the `false` does — and nothing outranks it, not `--telemetry true`
282 # and not `CODEWHALE_TELEMETRY=1`. Turning telemetry back on means writing
283 # `telemetry = true` here.
284 #
285 # The environment variable and the flag are different: they stop the run and
286 # erase nothing, so a harness that disables telemetry for one command does not
287 # discard the machine owner's install id and dry-run records.
288 #
289 # codewhale config set telemetry false # opt out: stops it and erases state
290 # CODEWHALE_TELEMETRY=0 codewhale # kill switch: stops it, erases nothing
291 #
292 # What is never collected: prompts, completions, tool arguments, diffs, file
293 # contents, filenames, paths, git remotes, repo or branch names, memory
294 # entries, chat history, API keys or tokens (not even a boolean saying one
295 # exists), model ids, custom provider table names, MCP server names, error or
296 # panic message bodies, per-event timestamps, keystrokes, clipboard,
297 # screenshots, or location. The complete schema is `docs/TELEMETRY.md`, and a
298 # test asserts this file and the serializer agree.
299 #
300 # A repo-local `.codewhale/config.toml` can set neither key: someone else's
301 # repository cannot turn your telemetry on or aim it at a host of their choosing.
302 # telemetry = false
303
304 # Where batches are POSTed. Leaving this unset selects the shipped default,
305 # the first-party ingest service:
306 #
307 # https://telemetry.codewhale.net/v1/telemetry
308 #
309 # That default is only ever consulted for a session that is already enabled —
310 # it decides where a batch goes, never whether one exists. Nothing is sent
311 # until `telemetry` is on AND the first-run notice was answered with Enable.
312 #
313 # Two overrides, both of which beat the default:
314 #
315 # telemetry_endpoint = "https://collector.internal/v1/batch" # your own sink
316 # telemetry_endpoint = "" # contact nobody
317 #
318 # The empty string is the local dry-run sink: batches are serialized exactly as
319 # a real endpoint would see them, appended to
320 # `$CODEWHALE_HOME/telemetry/dryrun.jsonl`, and no HTTP client is ever
321 # constructed. Read that file to see precisely what would have been sent.
322 # `CODEWHALE_TELEMETRY_ENDPOINT` overrides this file, and setting it to the
323 # empty string means the same "contact nobody".
324 #
325 # `https://` is required; plain `http://` is accepted only for loopback, and no
326 # environment variable overrides that refusal.
327 # telemetry_endpoint = "https://telemetry.codewhale.net/v1/telemetry"
328
329 # ─────────────────────────────────────────────────────────────────────────────────
330 # Security
331 # ─────────────────────────────────────────────────────────────────────────────────
332 allow_shell = true
333 approval_policy = "on-request" # on-request | untrusted | never
334 sandbox_mode = "workspace-write" # read-only | workspace-write | danger-full-access | external-sandbox
335
336 # Whether a workspace-write sandbox also lets shell commands reach the
337 # network. Default false: being allowed to edit this repository is not a
338 # reason to be allowed to open outbound connections, so `curl`, package
339 # installs, and `git fetch` are denied by the OS sandbox unless you opt in.
340 # When a command is denied, Codewhale offers an elevation prompt that grants
341 # network for that call only; set this to `true` to grant it for the whole
342 # session instead. `danger-full-access` and `yolo` are unsandboxed and
343 # unaffected by this key. Note that on platforms with no OS sandbox backend
344 # (default Linux without bubblewrap, and Windows) nothing is enforced either
345 # way -- `/status` and `doctor` both say so.
346 # sandbox_network_access = false
347
348 # Which other agents' instruction files to import as project instructions.
349 # Empty by default. Codewhale reads AGENTS.md (the cross-agent standard) and
350 # its own .codewhale/instructions.md without being asked; a CLAUDE.md,
351 # .cursorrules, or .github/copilot-instructions.md written as law for a
352 # different tool is not treated as law here until you say so. Codewhale's own
353 # files always outrank anything imported.
354 # Accepts: "claude", "cursor", "cline", "windsurf", "gemini", "copilot",
355 # "muse", or "all". Env: CODEWHALE_PROJECT_INSTRUCTION_IMPORTS (comma-separated).
356 # project_instruction_imports = ["claude"]
357 #
358 # Everything that reaches the model as standing project instruction authority —
359 # the repository-root -> workspace AGENTS.md chain, the global fallback layer,
360 # .codewhale/rules/*.md, and any imported foreign files — shares one 48 KiB
361 # aggregate budget. Instructions claim it first and are trimmed from the
362 # broadest scope inward, so the nearest-scope file is the last thing dropped.
363 # prompt_suggestion = true # opt-in: show ghost-text follow-up question in composer after each turn
364
365 # Optional tab/window title shown as `[title] …` in front of the terminal
366 # window title (the `Codewhale` / `reasoning…` / `using tool…` / `done`
367 # states). Multi-window setups can give each workspace its own `--config`
368 # file (or profile) so alt-tabbed sessions are identifiable at a glance.
369 # The `/title` command overrides this per session; `/config title … --save`
370 # persists a new default here. Run `/title off` to drop a session override.
371 # title = "workspace-x"
372
373 # Typed permission rules live in a sibling `permissions.toml` file, not in
374 # config.toml. Each `[[rules]]` entry accepts `tool`, optional `command`
375 # or `path`, optional absolute `workspace`, optional `command_exact = true`,
376 # and an `action` field: `"deny"`, `"ask"` (default), or `"allow"`.
377 # Deny always wins over ask, which wins over allow. The active user's sibling
378 # file is the only rule source today; project config overlays do not load a
379 # project-local permissions.toml. `workspace` is a repo scope, not a source.
380 # Globs and broad directory rules remain future work.
381 #
382 # In supported approval cards, press `S` to allow once and save an exact ask
383 # rule. Eligible safe calls also offer `P` / "Always allow this exact rule in
384 # this repo", which saves an exact `allow` rule scoped to the current absolute
385 # workspace. Dangerous/critical calls and repo-law prompts cannot save allow
386 # grants. The approval UI still does not save deny rules:
387 # exec_shell -> exact approved command string
388 # write_file -> exact workspace-relative target path
389 # edit_file -> exact workspace-relative target path
390 # apply_patch -> one exact workspace-relative path per validated touched file
391 # `read_file` rules can be written manually, but the approval UI does not save
392 # them.
393 #
394 # `/permissions list` shows the active source, effective matcher, repo/global
395 # scope, and whether each rule applies in the current workspace.
396 # `/permissions remove <number>` previews a deletion and prints a confirmation
397 # command bound to the current file snapshot. If the file changes first, the
398 # confirmation fails safely. Removal preserves unrelated comments/formatting,
399 # uses the same lock as approval-card appends, and atomically replaces the file.
400 # `/config ask-rules` remains a compatibility entry to the same list.
401 #
402 # Example ~/.codewhale/permissions.toml:
403 #
404 # [[rules]]
405 # tool = "exec_shell"
406 # command = "cargo test"
407 # action = "ask"
408 #
409 # # Block dangerous commands
410 # [[rules]]
411 # tool = "exec_shell"
412 # command = "sed"
413 # action = "deny"
414 #
415 # [[rules]]
416 # tool = "exec_shell"
417 # command = "awk"
418 # action = "deny"
419 #
420 # # Allow trusted commands without asking
421 # [[rules]]
422 # tool = "exec_shell"
423 # command = "git status"
424 # command_exact = true
425 # workspace = "/absolute/path/to/project"
426 # action = "allow"
427 #
428 # # Path-based deny
429 # [[rules]]
430 # tool = "write_file"
431 # path = "src/main.rs"
432 # action = "deny"
433 #
434 # [[rules]]
435 # tool = "edit_file"
436 # path = "src/lib.rs"
437 # action = "ask"
438 #
439 # [[rules]]
440 # tool = "apply_patch"
441 # path = "src/patch-target.rs"
442 #
443 # [[rules]]
444 # tool = "read_file"
445 # path = "secrets/api_key.txt"
446 # action = "deny"
447 # ─────────────────────────────────────────────────────────────────────────────────
448 # External Sandbox Backend (pluggable remote execution)
449 # ─────────────────────────────────────────────────────────────────────────────────
450 # When sandbox_backend is set to "opensandbox", all exec_shell calls are
451 # routed through an external OpenSandbox-compatible HTTP API instead of
452 # spawning a local process. The backend sends `POST {sandbox_url}/v1/sandbox/run`
453 # with `{"cmd": "...", "env": {...}}` and expects
454 # `{"stdout": "...", "stderr": "...", "exit_code": 0}`.
455 #
456 # sandbox_backend = "none" # "none" (default) or "opensandbox"
457 # sandbox_url = "http://localhost:8080" # OpenSandbox-compatible API base URL
458 # sandbox_api_key = "YOUR_API_KEY" # Optional Bearer token sent with requests
459 #
460 # Env-var overrides:
461 # DEEPSEEK_SANDBOX_BACKEND → sandbox_backend
462 # DEEPSEEK_SANDBOX_URL → sandbox_url
463 # DEEPSEEK_SANDBOX_API_KEY → sandbox_api_key
464 #
465 # Example OpenSandbox setup:
466 #
467 # sandbox_backend = "opensandbox"
468 # sandbox_url = "http://localhost:8080"
469 # sandbox_api_key = "sk-opensandbox-secret"
470 #
471 # The backend uses a 30-second HTTP timeout. Background, interactive, and
472 # TTY modes are not supported with external backends — all commands run
473 # synchronously via HTTP.
474 # ─────────────────────────────────────────────────────────────────────────────────
475 # Bubblewrap (Linux only, additional filesystem isolation)
476 # ─────────────────────────────────────────────────────────────────────────────────
477 # When set to true and `/usr/bin/bwrap` is present, exec_shell commands are
478 # routed through bubblewrap instead of relying solely on Landlock. Bubblewrap
479 # creates a read-only view of the root filesystem with write access limited to
480 # the working directory. Install separately:
481 #
482 # Ubuntu/Debian: apt install bubblewrap
483 # Fedora: dnf install bubblewrap
484 # Arch: pacman -S bubblewrap
485 #
486 # prefer_bwrap = false # default — use Landlock only
487 #
488 # Env override: CODEWHALE_PREFER_BWRAP=true
489 # Legacy alias (deprecated until 0.10.0): DEEPSEEK_PREFER_BWRAP=true
490 #
491 # With prefer_bwrap = true, the sandbox gets a private /dev and /proc plus a
492 # writable isolated /tmp by default, so toolchains work without widening the
493 # filesystem policy (#5410). Two optional escape hatches:
494 #
495 # bwrap_ro_roots = [] # extra host paths bind-mounted read-only, e.g.
496 # # ["/usr/lib", "/usr/lib64"] for system-library
497 # # linking when the root bind is narrowed
498 # bwrap_dev_roots = [] # host device nodes bind-mounted read-write, e.g.
499 # # ["/dev/null"] for redirection against the host
500 # # node — character/block devices only, never
501 # # directories, so this cannot widen file writes
502
503 # auto_allow entries match by command prefix, not raw string.
504 # See command_safety.rs for the prefix dictionary.
505 #
506 # Examples:
507 # auto_allow = ["git status"] # auto-approves: git status, git status -s, git status --porcelain
508 # # does NOT auto-approve: git push, git checkout
509 # auto_allow = ["cargo check", "npm run"]
510 #
511 # auto_allow = []
512 max_subagents = 10 # optional (default 64, clamped to 1-128)
513
514 # Optional sub-agent tuning. max_concurrent overrides top-level max_subagents.
515 # [subagents]
516 # max_concurrent = 10
517 # api_timeout_secs = 600 # per-step API timeout, clamped to 1..=3600
518 #
519 # How many levels of nested sub-agents the `agent` tool may spawn:
520 # max_depth = 0 # opt out completely — the agent never spawns sub-agents
521 # max_depth = 1 # the agent may spawn sub-agents, but those may not spawn more
522 # max_depth = 2 # one more level of nesting, etc.
523 # Unset defaults to 3; any value is clamped to the hard ceiling (8). The depth
524 # limit is enforced in code, not requested of the model — a sub-agent past the
525 # limit cannot be spawned regardless of what the model decides.
526 # max_depth = 3
527
528 # Optional managed policy paths (defaults to /etc/deepseek/*.toml on unix):
529 # managed_config_path = "/etc/deepseek/managed_config.toml"
530 # requirements_path = "/etc/deepseek/requirements.toml"
531
532 # ─────────────────────────────────────────────────────────────────────────────────
533 # Per-provider credentials (peer providers — NIM is first-class, not a flag)
534 # ─────────────────────────────────────────────────────────────────────────────────
535 # Providers can be stored at once; `provider = "..."` (top of file) or
536 # `/provider deepseek` / `/provider nvidia-nim` / `--provider openai` /
537 # `--provider wanjie-ark` / `/provider volcengine` / `/provider fireworks` /
538 # `--provider siliconflow` / `/provider arcee` / `/provider moonshot`
539 # switches between them without having to re-enter keys. Env vars override anything set here:
540 # DeepSeek: DEEPSEEK_API_KEY, DEEPSEEK_BASE_URL, DEEPSEEK_MODEL
541 # DeepSeek Anthropic-compatible: DEEPSEEK_API_KEY, DEEPSEEK_ANTHROPIC_BASE_URL
542 # NIM: NVIDIA_API_KEY (or NVIDIA_NIM_API_KEY), NIM_BASE_URL
543 # (or NVIDIA_NIM_BASE_URL / NVIDIA_BASE_URL), NVIDIA_NIM_MODEL
544 # OpenAI-compatible: OPENAI_API_KEY, OPENAI_BASE_URL, OPENAI_MODEL
545 # Wanjie Ark: WANJIE_ARK_API_KEY (or WANJIE_API_KEY), WANJIE_ARK_BASE_URL, WANJIE_ARK_MODEL
546 # Volcengine Ark: VOLCENGINE_API_KEY (or VOLCENGINE_ARK_API_KEY / ARK_API_KEY), VOLCENGINE_BASE_URL, VOLCENGINE_MODEL
547 # OpenRouter: OPENROUTER_API_KEY, OPENROUTER_BASE_URL, OPENROUTER_MODEL
548 # Xiaomi MiMo: XIAOMI_MIMO_API_KEY (or XIAOMI_API_KEY / MIMO_API_KEY), XIAOMI_MIMO_BASE_URL, XIAOMI_MIMO_MODEL
549 # Token Plan: XIAOMI_MIMO_TOKEN_PLAN_API_KEY (or MIMO_TOKEN_PLAN_API_KEY), XIAOMI_MIMO_MODE/MIMO_MODE
550 # Novita: NOVITA_API_KEY, NOVITA_BASE_URL, NOVITA_MODEL
551 # Fireworks: FIREWORKS_API_KEY, FIREWORKS_BASE_URL
552 # SiliconFlow: SILICONFLOW_API_KEY, SILICONFLOW_BASE_URL, SILICONFLOW_MODEL
553 # Arcee: ARCEE_API_KEY, ARCEE_BASE_URL, ARCEE_MODEL
554 # Moonshot/Kimi: MOONSHOT_API_KEY (or KIMI_API_KEY), MOONSHOT_BASE_URL, MOONSHOT_MODEL
555 # SGLang: SGLANG_BASE_URL, SGLANG_MODEL, optional SGLANG_API_KEY
556 # vLLM: VLLM_BASE_URL, VLLM_MODEL, optional VLLM_API_KEY
557 # Ollama: OLLAMA_BASE_URL, OLLAMA_MODEL, optional OLLAMA_API_KEY
558 # Hugging Face: HUGGINGFACE_API_KEY (or HF_TOKEN), HUGGINGFACE_BASE_URL (or HF_BASE_URL),
559 # HUGGINGFACE_MODEL (or HF_MODEL)
560 # Meta Model API: META_MODEL_API_KEY (or MODEL_API_KEY), META_MODEL_API_BASE_URL
561 # (or MODEL_API_BASE_URL), META_MODEL_API_MODEL (or MODEL_API_MODEL)
562 #
563 # Custom DeepSeek-compatible APIs usually do not need a new provider table:
564 # set `provider = "deepseek"` and override [providers.deepseek].base_url/model.
565 # For generic OpenAI-compatible gateways, use `provider = "openai"` and the
566 # [providers.openai] table below. Keep provider/api_key/base_url in user config
567 # or environment variables; project overlays are not allowed to set them.
568 #
569 # Provider is the route/account/endpoint; model is the ID on that route.
570 # Common DeepSeek routes:
571 # provider = "deepseek" model = "deepseek-v4-pro"
572 # provider = "nvidia-nim" model = "deepseek-ai/deepseek-v4-pro"
573 # provider = "openrouter" model = "deepseek/deepseek-v4-pro"
574 # provider = "fireworks" model = "accounts/fireworks/models/deepseek-v4-pro"
575 # provider = "siliconflow" model = "deepseek-ai/DeepSeek-V4-Pro"
576
577 # DeepSeek Platform (https://platform.deepseek.com)
578 [providers.deepseek]
579 # api_key = "YOUR_DEEPSEEK_API_KEY"
580 # base_url = "https://api.deepseek.com/beta"
581 # model = "deepseek-v4-pro"
582 # Custom DeepSeek-compatible example:
583 # base_url = "https://your-provider.example/v1"
584 # model = "deepseek-ai/DeepSeek-V4-Pro"
585 # http_headers = { "X-Model-Provider-Id" = "your-model-provider" } # optional custom request headers
586 # path_suffix = "/chat/completions" # override the API path; skips /v1 versioning when set
587 # reasoning_stream_style = "inline_tags" # route <think>...</think> content into Thinking cells
588
589 # DeepSeek Anthropic-compatible Messages route (opt-in)
590 # [providers.deepseek_anthropic]
591 # api_key = "YOUR_DEEPSEEK_API_KEY"
592 # base_url = "https://api.deepseek.com/anthropic"
593 # model = "deepseek-v4-pro"
594 # [providers.deepseek.auth] # provider-scoped auth source metadata; command execution lands in a follow-up slice
595 # source = "command"
596 # command = ["secret-tool", "lookup", "service", "codewhale-deepseek"]
597 # timeout_ms = 2000
598 # insecure_skip_tls_verify = true # last resort for private gateways; prefer SSL_CERT_FILE
599
600 # NVIDIA NIM-hosted DeepSeek V4 (https://build.nvidia.com)
601 [providers.nvidia_nim]
602 # api_key = "YOUR_NVIDIA_API_KEY"
603 # base_url = "https://integrate.api.nvidia.com/v1"
604 # model = "deepseek-ai/deepseek-v4-pro" # or deepseek-ai/deepseek-v4-flash
605
606 # Generic OpenAI-compatible endpoint. Use the built-in `openai` provider for
607 # third-party gateways; do not invent a custom provider name. For non-local
608 # http:// gateways, launch with DEEPSEEK_ALLOW_INSECURE_HTTP=1 only on a
609 # trusted network.
610 [providers.openai]
611 # api_key = "YOUR_OPENAI_COMPATIBLE_API_KEY"
612 # base_url = "https://api.openai.com/v1"
613 # model = "gpt-4.1"
614 # Gateway example:
615 # base_url = "https://gateway.example/v1"
616 # model = "your-deepseek-compatible-model"
617 # Alibaba Bailian / Model Studio DashScope OpenAI-compatible example:
618 # base_url = "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
619 # model = "qwen-plus"
620 # context_window = 1000000 # set the gateway/model's real total context window
621 # insecure_skip_tls_verify = true # last resort for private gateways; prefer SSL_CERT_FILE
622
623 # AtlasCloud OpenAI-compatible endpoint (https://www.atlascloud.ai/docs/models/llm)
624 [providers.atlascloud]
625 # api_key = "YOUR_ATLASCLOUD_API_KEY"
626 # base_url = "https://api.atlascloud.ai/v1"
627 # model = "deepseek-ai/deepseek-v4-flash"
628
629 # Wanjie Ark / 万界方舟 OpenAI-compatible endpoint
630 [providers.wanjie_ark]
631 # api_key = "YOUR_WANJIE_API_KEY"
632 # base_url = "https://maas-openapi.wanjiedata.com/api/v1"
633 # model = "deepseek-reasoner" # or the exact model ID enabled on your Wanjie account
634
635 # Volcengine / Volcano Engine Ark Coding API
636 [providers.volcengine]
637 # api_key = "YOUR_VOLCENGINE_API_KEY"
638 # base_url = "https://ark.cn-beijing.volces.com/api/coding/v3"
639 # model = "DeepSeek-V4-Pro" # or DeepSeek-V4-Flash
640
641 # OpenRouter — multi-provider gateway (https://openrouter.ai)
642 [providers.openrouter]
643 # vendor = "deepinfra" # exact upstream slug; disables OpenRouter fallbacks
644 # api_key = "YOUR_OPENROUTER_API_KEY"
645 # base_url = "https://openrouter.ai/api/v1"
646 # model = "deepseek/deepseek-v4-pro"
647 # OpenRouter-compatible gateways can reuse this provider so reasoning/cache
648 # parsing stays on the OpenRouter-compatible path instead of generic OpenAI:
649 # base_url = "https://openrouter-compatible.example/v1"
650 # model = "deepseek/deepseek-v4-pro"
651 # Recent large model IDs also accepted here include arcee-ai/trinity-large-thinking,
652 # minimax/minimax-m3, minimax/minimax-m2.7, xiaomi/mimo-v2.5-pro, qwen/qwen3.6-flash,
653 # qwen/qwen3.6-35b-a3b, qwen/qwen3.6-max-preview, qwen/qwen3.6-27b, qwen/qwen3.6-plus,
654 # qwen/qwen3.7-max, google/gemma-4-31b-it, z-ai/glm-5.1, z-ai/glm-5.2,
655 # moonshotai/kimi-k2.6,
656 # nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free, and nvidia/nemotron-3-ultra.
657
658 # OrcaRouter — OpenAI-compatible aggregation gateway (https://www.orcarouter.ai)
659 [providers.orcarouter]
660 # api_key = "YOUR_ORCAROUTER_API_KEY"
661 # base_url = "https://api.orcarouter.ai/v1"
662 # model = "deepseek/deepseek-v4-pro"
663 # Namespaced wire models pass through verbatim (deepseek/deepseek-v4-pro,
664 # deepseek/deepseek-v4-flash); OrcaRouter's own auto-routing model is
665 # selectable as "orcarouter/auto".
666
667 # Xiaomi MiMo OpenAI-compatible endpoint (https://platform.xiaomimimo.com)
668 [providers.xiaomi_mimo]
669 # api_key = "YOUR_XIAOMI_KEY"
670 # base_url = "https://token-plan-sgp.xiaomimimo.com/v1" # Token Plan / tp- keys
671 # # base_url = "https://token-plan-ams.xiaomimimo.com/v1" # Token Plan Europe / Amsterdam
672 # # base_url = "https://api.xiaomimimo.com/v1" # Pay-as-you-go / sk- keys
673 # model = "mimo-v2.5-pro" # chat/reasoning
674 # Chat model IDs: mimo-v2.5-pro, mimo-v2.5-pro-ultraspeed, mimo-v2.5
675 # Token Plan subscriptions use separate tp-* API keys plus api-key auth.
676 # mode = "token-plan-sgp" # default Token Plan endpoint
677 # mode = "token-plan-cn" # China cluster
678 # mode = "token-plan-ams" # Europe cluster
679 # mode = "pay-as-you-go" # standard API / sk- keys
680 # TTS aliases are also accepted by `codewhale speech`: tts, voice-design, voice-clone
681 # TTS model IDs: mimo-v2.5-tts, mimo-v2.5-tts-voicedesign, mimo-v2.5-tts-voiceclone, mimo-v2-tts
682
683 # Novita AI-hosted inference (https://novita.ai)
684 [providers.novita]
685 # api_key = "YOUR_NOVITA_API_KEY"
686 # base_url = "https://api.novita.ai/openai/v1"
687 # model = "deepseek/deepseek-v4-pro" # or deepseek/deepseek-v4-flash
688
689 # Fireworks AI-hosted DeepSeek V4 (https://fireworks.ai)
690 [providers.fireworks]
691 # api_key = "YOUR_FIREWORKS_API_KEY"
692 # base_url = "https://api.fireworks.ai/inference/v1"
693 # model = "accounts/fireworks/models/deepseek-v4-pro"
694
695 # SiliconFlow-hosted DeepSeek V4 (https://siliconflow.com)
696 [providers.siliconflow]
697 # api_key = "YOUR_SILICONFLOW_API_KEY"
698 # base_url = "https://api.siliconflow.com/v1"
699 # model = "deepseek-ai/DeepSeek-V4-Pro" # or deepseek-ai/DeepSeek-V4-Flash
700
701 # SiliconFlow China-hosted DeepSeek V4 (https://siliconflow.cn)
702 # Falls back to [providers.siliconflow] for api_key / base_url / model when unset.
703 [providers.siliconflow-CN]
704 # api_key = "YOUR_SILICONFLOW_API_KEY"
705 # base_url = "https://api.siliconflow.cn/v1"
706 # model = "deepseek-ai/DeepSeek-V4-Pro"
707
708 # Arcee AI direct OpenAI-compatible endpoint (https://docs.arcee.ai)
709 [providers.arcee]
710 # api_key = "YOUR_ARCEE_API_KEY"
711 # base_url = "https://api.arcee.ai/api/v1"
712 # model = "trinity-large-thinking" # or trinity-large-preview
713
714 # Moonshot/Kimi OpenAI-compatible endpoint (https://platform.kimi.ai)
715 [providers.moonshot]
716 # api_key = "YOUR_MOONSHOT_API_KEY" # or KIMI_API_KEY
717 # base_url = "https://api.moonshot.ai/v1" # or KIMI_BASE_URL
718 # model = "kimi-k3" # direct Moonshot K3 wire ID
719 # Direct K3 is always-thinking: off -> low, medium -> high, and max remains max.
720 # The exact route sends top-level reasoning_effort, max_completion_tokens, and
721 # omits temperature/top_p per https://platform.kimi.ai/docs/guide/kimi-k3-quickstart.
722 # Kimi Code membership path (key: https://www.kimi.com/code/console):
723 # api_key = "YOUR_KIMI_CODE_API_KEY"
724 # base_url = "https://api.kimi.com/coding/v1"
725 # model = "k3" # Kimi Code K3 wire ID
726 # K3 membership off -> enabled/low; dispatched auto selects a concrete tier.
727 # Only an omitted reasoning setting leaves the provider default in control.
728 # Moderato plans are capped at 262144; Allegretto and above unlock up to 1048576.
729 # context_window = 262144 # manually cap k3 to the Moderato/256K window
730 # context_window = 1048576 # Allegretto+ only; do not claim an unavailable entitlement
731 # Alternatively use model = "k3-256k" with context_window = 262144 for the fixed 256K route.
732 # Omit context_window to keep Codewhale's safe 262144-token bare-k3 baseline.
733 # `k3[1m]` is a Claude Code-only convention, not an API model ID; Codewhale rejects it.
734 # Kimi Code K2.7 remains available to all members as model = "kimi-for-coding".
735 # Kimi OAuth is not supported. Legacy auth_mode = "kimi_oauth" fails closed
736 # to the API-key guidance above without probing Kimi CLI credential files.
737
738 # Z.AI GLM Coding Plan endpoint (https://docs.z.ai)
739 [providers.zai]
740 # api_key = "YOUR_ZAI_API_KEY" # or Z_AI_API_KEY
741 # base_url = "https://api.z.ai/api/coding/paas/v4"
742 # # General API endpoint, if you are not using the Coding Plan:
743 # # base_url = "https://api.z.ai/api/paas/v4"
744 # model = "GLM-5.3" # default; GLM-5.3-Flash is the fast sibling, GLM-5.2 the previous default, GLM-5.1 the smaller model, GLM-5-Turbo the 5.2 fast sibling
745 # # GLM-5.3 is live on the Z.ai Coding Plan (2026-08-13). Its catalog metadata
746 # # (limits, reasoning options) is inherited from GLM-5.2 until Z.ai publishes
747 # # distinct 5.3 numbers, and it carries no price. GLM-5.3-Flash (2026-08-26)
748 # # is the 1M multimodal picker row (`model = "GLM-5.3-Flash"`). An explicit
749 # # model = "GLM-5.2" keeps sending GLM-5.2; only the default moved. Accounts
750 # # not provisioned for 5.3 can still see a 429 with entitlement code 1311.
751
752 # StepFun / StepFlash direct OpenAI-compatible endpoint (https://platform.stepfun.ai)
753 [providers.stepfun]
754 # api_key = "YOUR_STEPFUN_API_KEY" # or STEP_API_KEY
755 # base_url = "https://api.stepfun.ai/v1" # or STEP_BASE_URL
756 # # Coding Plan endpoint:
757 # # base_url = "https://api.stepfun.ai/step_plan/v1"
758 # model = "step-3.7-flash" # or STEPFUN_MODEL / STEP_MODEL
759
760 # MiniMax direct OpenAI-compatible endpoint (https://platform.minimax.io)
761 [providers.minimax]
762 # api_key = "YOUR_MINIMAX_API_KEY"
763 # base_url = "https://api.minimax.io/v1"
764 # model = "MiniMax-M3" # or MiniMax-M2.7, MiniMax-M2.7-highspeed
765 # # MiniMax also publishes Anthropic-compatible endpoints:
766 # # global https://api.minimax.io/anthropic, China https://api.minimaxi.com/anthropic.
767
768 # Self-hosted SGLang OpenAI-compatible server
769 [providers.sglang]
770 # api_key = "OPTIONAL_SGLANG_TOKEN"
771 # base_url = "http://localhost:30000/v1"
772 # model = "deepseek-ai/DeepSeek-V4-Pro" # or deepseek-ai/DeepSeek-V4-Flash
773
774 # Self-hosted vLLM OpenAI-compatible server
775 [providers.vllm]
776 # api_key = "OPTIONAL_VLLM_TOKEN"
777 # base_url = "http://localhost:8000/v1"
778 # model = "deepseek-ai/DeepSeek-V4-Pro" # or deepseek-ai/DeepSeek-V4-Flash
779
780 # Self-hosted Ollama OpenAI-compatible server
781 [providers.ollama]
782 # api_key = "OPTIONAL_OLLAMA_TOKEN"
783 # base_url = "http://localhost:11434/v1"
784 # model = "deepseek-v4-flash" # or any local Ollama tag
785
786 # Hugging Face Inference Providers (https://huggingface.co/docs/api-inference)
787 # Provider aliases: huggingface, hugging-face, hugging_face, hf
788 # Env var aliases: HUGGINGFACE_API_KEY / HF_TOKEN, HUGGINGFACE_BASE_URL / HF_BASE_URL,
789 # HUGGINGFACE_MODEL / HF_MODEL
790 [providers.huggingface]
791 # api_key = "YOUR_HF_TOKEN"
792 # base_url = "https://router.huggingface.co/v1"
793 # model = "deepseek-ai/DeepSeek-V4-Pro" # or deepseek-ai/DeepSeek-V4-Flash
794
795 # DeepInfra — AI inference cloud (https://deepinfra.com)
796 [providers.deepinfra]
797 # api_key = "YOUR_DEEPINFRA_TOKEN"
798 # base_url = "https://api.deepinfra.com/v1/openai"
799 # model = "deepseek-ai/DeepSeek-V4-Pro" # or deepseek-ai/DeepSeek-V4-Flash
800
801 # ─────────────────────────────────────────────────────────────────────────────────
802 # Sakana AI Fugu Provider (https://api.sakana.ai)
803 # Provider aliases: sakana, sakana-ai, sakana_ai, fugu
804 # Env var aliases: FUGU_API_KEY, SAKANA_API_KEY
805 [providers.sakana]
806 # api_key = "YOUR_FUGU_API_KEY"
807 # base_url = "https://api.sakana.ai/v1"
808 # model = "fugu" # or fugu-ultra-20260615
809
810 # Meituan LongCat Provider (https://longcat.chat/platform)
811 # OpenAI-compatible curated gateway for Meituan's LongCat models.
812 # Provider aliases: longcat, long-cat, meituan-longcat, meituan
813 # Env var aliases: LONGCAT_API_KEY
814 [providers.longcat]
815 # api_key = "YOUR_LONGCAT_API_KEY"
816 # base_url = "https://api.longcat.chat/openai/v1"
817 # model = "LongCat-2.0"
818
819 # OpenCode Go (https://opencode.ai/docs/go/)
820 # Subscription-backed OpenAI-compatible Chat Completions route.
821 # Env vars: OPENCODE_GO_API_KEY, OPENCODE_GO_BASE_URL, OPENCODE_GO_MODEL
822 # Chat Completions models: deepseek-v4-pro, grok-4.5, glm-5.2, glm-5.1,
823 # kimi-k3, kimi-k2.7-code, kimi-k2.6, deepseek-v4-flash, mimo-v2.5,
824 # mimo-v2.5-pro.
825 # Models documented only on OpenCode Go's Anthropic `/messages` endpoint are
826 # intentionally not advertised by this provider yet.
827 [providers.opencode_go]
828 # api_key = "YOUR_OPENCODE_GO_API_KEY"
829 # base_url = "https://opencode.ai/zen/go/v1"
830 # model = "deepseek-v4-pro"
831
832 # OpenCode Zen (https://opencode.ai/docs/zen/)
833 # Model-aware gateway: GPT/Muse Spark use Responses, Claude/Qwen use Anthropic
834 # Messages, and DeepSeek/MiniMax/GLM/Kimi/Grok/free models use Chat Completions.
835 # Gemini uses a Google-specific protocol that Codewhale does not implement and
836 # therefore fails closed instead of being sent with the wrong request shape.
837 # Env vars: OPENCODE_ZEN_API_KEY (preferred), OPENCODE_API_KEY,
838 # OPENCODE_ZEN_BASE_URL, OPENCODE_ZEN_MODEL
839 [providers.opencode_zen]
840 # api_key = "YOUR_OPENCODE_ZEN_API_KEY"
841 # base_url = "https://opencode.ai/zen/v1"
842 # model = "gpt-5.5" # Responses
843 # model = "muse-spark-1.2-contributor-free" # Responses (free tier, auto-routed to Responses — no wire needed)
844 # model = "claude-sonnet-4-6" # Anthropic Messages example
845 # model = "deepseek-v4-pro" # Chat Completions example
846 # Custom gateway equivalent (when not using the opencode_zen provider):
847 # [providers.my_opencode]
848 # kind = "openai-compatible"
849 # base_url = "https://opencode.ai/zen/v1"
850 # model = "muse-spark-1.2-contributor-free"
851 # wire = "responses"
852 # api_key_env = "OPENCODE_ZEN_API_KEY"
853
854 # Meta Model API / Muse Spark (https://developer.meta.com/ai/)
855 # OpenAI-compatible Chat Completions route.
856 # Provider aliases: meta, meta-ai, meta-model-api, muse, muse-spark
857 # Env var aliases: META_MODEL_API_KEY / MODEL_API_KEY,
858 # META_MODEL_API_BASE_URL / MODEL_API_BASE_URL,
859 # META_MODEL_API_MODEL / MODEL_API_MODEL
860 [providers.meta]
861 # api_key = "YOUR_META_MODEL_API_KEY"
862 # base_url = "https://api.meta.ai/v1"
863 # model = "muse-spark-1.1"
864
865 # xAI / Grok Provider (https://console.x.ai/)
866 # OpenAI-compatible Chat Completions route.
867 # Provider aliases: xai, x-ai, x_ai, grok
868 # Env var aliases: XAI_API_KEY, XAI_BASE_URL, XAI_MODEL
869 #
870 # Auth modes:
871 # api_key (default) — console.x.ai pay-per-use key via api_key / XAI_API_KEY / keyring
872 # oauth — `codewhale auth xai-device` uses Codewhale-owned storage.
873 # Reading an existing Grok CLI file requires explicit
874 # `codewhale auth external-consent --provider xai --mode read-only`.
875 [providers.xai]
876 # api_key = "YOUR_XAI_API_KEY"
877 # auth_mode = "oauth" # or "device_code" / "grok_cli"
878 # base_url = "https://api.x.ai/v1"
879 # model = "grok-4.6" # or grok-4.5, grok-4.3, grok-build
880
881 # Mistral AI — la Plateforme (https://console.mistral.ai/)
882 # OpenAI-compatible Chat Completions route.
883 # Provider aliases: mistral, mistral-ai, mistralai, la-plateforme
884 # Env var aliases: MISTRAL_API_KEY, MISTRAL_BASE_URL, MISTRAL_MODEL
885 [providers.mistral]
886 # api_key = "YOUR_MISTRAL_API_KEY"
887 # base_url = "https://api.mistral.ai/v1"
888 # model = "mistral-code-latest" # or mistral-medium-latest, mistral-small-latest, mistral-large-latest
889
890 # Google Gemini — Google AI Studio (https://aistudio.google.com/apikey)
891 # OpenAI-compatible Chat Completions route on the official Gemini endpoint;
892 # this is the supported Gemini path (see docs/PROVIDERS.md).
893 # Provider aliases: google, gemini, google-gemini, ai-studio
894 # Env var aliases: GEMINI_API_KEY, GOOGLE_API_KEY, GEMINI_BASE_URL, GOOGLE_BASE_URL
895 [providers.google]
896 # api_key = "YOUR_GEMINI_API_KEY"
897 # base_url = "https://generativelanguage.googleapis.com/v1beta/openai/"
898 # model = "gemini-3.1-pro-preview"
899
900 # ─────────────────────────────────────────────────────────────────────────────────
901 # Alibaba Cloud Model Studio — Token Plan
902 # (https://bailian.console.aliyun.com/)
903 #
904 # Token Plan Personal and Team share the same AP-Southeast (Singapore) endpoint.
905 # Available text/coding models: qwen3.8-max, qwen3.8-max-preview, qwen3.7-plus,
906 # qwen3.7-max, qwen3.6-flash, deepseek-v4-pro, deepseek-v4-flash-0731, glm-5.2
907 #
908 # Provider aliases: modelstudio-token-plan, modelstudio_token_plan,
909 # alibaba-token-plan, dashscope-token-plan
910 # Env var aliases: MODELSTUDIO_API_KEY (preferred), DASHSCOPE_API_KEY,
911 # MODELSTUDIO_TOKEN_PLAN_BASE_URL, MODELSTUDIO_TOKEN_PLAN_MODEL
912 [providers.modelstudio_token_plan]
913 # api_key = "YOUR_MODELSTUDIO_API_KEY"
914 # base_url = "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1"
915 # model = "qwen3.8-max"
916 # # Anthropic-compatible dialect (same key, /apps/anthropic path):
917 # # provider = "modelstudio-token-plan-anthropic"
918 # # base_url = "https://token-plan.ap-southeast-1.maas.aliyuncs.com/apps/anthropic"
919
920 # ─────────────────────────────────────────────────────────────────────────────────
921 # Alibaba Cloud Model Studio — Coding Plan
922 # (https://bailian.console.aliyun.com/)
923 #
924 # Coding Plan uses a separate international endpoint (coding-intl.dashscope).
925 # Same model catalog as the Token Plan.
926 #
927 # Provider aliases: modelstudio-coding-plan, modelstudio_coding_plan,
928 # alibaba-coding-plan, dashscope-coding-plan
929 # Env var aliases: MODELSTUDIO_API_KEY (preferred), DASHSCOPE_API_KEY,
930 # MODELSTUDIO_CODING_PLAN_BASE_URL, MODELSTUDIO_CODING_PLAN_MODEL
931 [providers.modelstudio_coding_plan]
932 # api_key = "YOUR_MODELSTUDIO_API_KEY"
933 # base_url = "https://coding-intl.dashscope.aliyuncs.com/v1"
934 # model = "qwen3.8-max"
935 # # Anthropic-compatible dialect (same key, /apps/anthropic path):
936 # # provider = "modelstudio-coding-plan-anthropic"
937 # # base_url = "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic"
938
939 # ─────────────────────────────────────────────────────────────────────────────────
940 # Together AI Provider (https://www.together.ai/)
941 # Env var aliases: TOGETHER_API_KEY, TOGETHER_BASE_URL, TOGETHER_MODEL
942 [providers.together]
943 # api_key = "YOUR_TOGETHER_API_KEY"
944 # base_url = "https://api.together.xyz/v1"
945 # model = "deepseek-ai/DeepSeek-V4-Pro" # or deepseek-ai/DeepSeek-V4-Flash
946
947 # ─────────────────────────────────────────────────────────────────────────────────
948 # Baidu Qianfan Provider (https://intl.cloud.baidu.com/product/qianfan.html)
949 # Provider aliases: qianfan, baidu-qianfan, baidu_qianfan, baidu
950 # Env var aliases: QIANFAN_API_KEY / BAIDU_QIANFAN_API_KEY,
951 # QIANFAN_BASE_URL / BAIDU_QIANFAN_BASE_URL,
952 # QIANFAN_MODEL / BAIDU_QIANFAN_MODEL
953 [providers.qianfan]
954 # api_key = "YOUR_QIANFAN_API_KEY"
955 # base_url = "https://api.baiduqianfan.ai/v1"
956 # model = "ernie-4.0-turbo-8k" # or your Qianfan service/model id
957
958 # ─────────────────────────────────────────────────────────────────────────────────
959 # OpenAI Codex (ChatGPT) Provider — EXPERIMENTAL
960 # Run `codex login`, then explicitly grant read-only access to that exact file:
961 # codewhale auth external-consent --provider openai-codex --mode read-only
962 # Codewhale never refreshes or rewrites the Codex CLI file. No API key is
963 # stored here. Talks to the OpenAI Responses API at /codex/responses.
964 # Env var aliases: OPENAI_CODEX_ACCESS_TOKEN / CODEX_ACCESS_TOKEN (token override),
965 # OPENAI_CODEX_BASE_URL / CODEX_BASE_URL, OPENAI_CODEX_MODEL / CODEX_MODEL,
966 # OPENAI_CODEX_ACCOUNT_ID / CODEX_ACCOUNT_ID, OPENAI_CODEX_AUTH_FILE, CODEX_HOME
967 [providers.openai_codex]
968 # base_url = "https://chatgpt.com/backend-api"
969 # model = "gpt-5.5"
970 # The CLI writes this table after informed consent; do not copy it between
971 # providers or machines. Absence means disabled.
972 # [providers.openai_codex.external_credentials]
973 # access = "read_only"
974 # provider = "openai-codex"
975 # source = "codex_cli"
976 # path = "/absolute/path/to/.codex/auth.json"
977 # consent_version = 1
978
979 # ─────────────────────────────────────────────────────────────────────────────────
980 # Anthropic Provider (native Messages API)
981 # Talks to https://api.anthropic.com/v1/messages with x-api-key auth — not an
982 # OpenAI-compatible route. Models: claude-opus-4-8, claude-sonnet-4-6 (default),
983 # claude-haiku-4-5. Env vars: ANTHROPIC_API_KEY, ANTHROPIC_BASE_URL,
984 # ANTHROPIC_MODEL.
985 [providers.anthropic]
986 # api_key = "sk-ant-..."
987 # base_url = "https://api.anthropic.com"
988 # model = "claude-sonnet-4-6"
989
990 # OpenModel Provider (Anthropic-compatible Messages API)
991 # Talks to https://api.openmodel.ai/v1/messages with Bearer auth. OpenModel
992 # routes DeepSeek, DashScope, Xiaomi, Claude, and other models by model id.
993 # Env vars: OPENMODEL_API_KEY, OPENMODEL_BASE_URL, OPENMODEL_MODEL.
994 [providers.openmodel]
995 # api_key = "om-..."
996 # base_url = "https://api.openmodel.ai"
997 # model = "deepseek-v4-flash"
998
999 # Codewhale API (https://app.codewhale.net/settings?section=api)
1000 # Account-backed model access over the provider keys connected to the
1001 # customer's Codewhale account. The account's authenticated `GET /v1/models`
1002 # listing is the catalog authority: each `provider/model` row states its wire
1003 # (`chat-completions` → `/v1/chat/completions`, `anthropic-messages` →
1004 # `/v1/messages`, `responses` → `/v1/responses`). Every protocol authenticates
1005 # the account key with `Authorization: Bearer`, never `x-api-key`.
1006 # Provider aliases: codewhale-api, cw-api, codewhale-cloud
1007 # Env vars: CODEWHALE_API_KEY, CODEWHALE_API_BASE (HTTPS except loopback);
1008 # the generic CODEWHALE_MODEL override also applies.
1009 [providers.codewhale]
1010 # api_key = "cwc_key_..." # or `codewhale account api-keys create --name <name> --use`
1011 # base_url = "https://api.codewhale.net/v1"
1012 # model = "deepseek/deepseek-v4-pro" # provider/model exactly as the account catalog returns it
1013
1014 # ─────────────────────────────────────────────────────────────────────────────────
1015 # Web Search Provider
1016 # ─────────────────────────────────────────────────────────────────────────────────
1017 # Choose which backend the Web tool's search uses. Default is keyless Firecrawl
1018 # (bounded per-IP quota; `[search] api_key` or FIRECRAWL_API_KEY raises it).
1019 # Bing and DuckDuckGo HTML scraping need no API key. Tavily, Bocha, Metaso,
1020 # Baidu, Volcengine, Sofya and Serply need an api_key; SearXNG needs base_url.
1021 # API runtime failures and empty responses visibly degrade through DuckDuckGo
1022 # then Bing. Missing configuration and network-policy denials fail closed.
1023 #
1024 # [search]
1025 # provider = "firecrawl" # firecrawl | bing | duckduckgo | tavily | bocha | metaso | searxng | baidu | volcengine | sofya | serply
1026 # # firecrawl: Firecrawl Cloud search, keyless with a bounded per-IP quota
1027 # # duckduckgo: HTML scrape with Bing fallback
1028 # # bing: HTML scrape, no API key
1029 # # tavily: https://tavily.com — AI search, needs api_key
1030 # # TAVILY_API_KEY set + provider unset auto-selects tavily
1031 # # at runtime; it is never written to config.toml
1032 # # bocha: https://bochaai.com — 博查AI搜索,国内友好,需api_key
1033 # # metaso: https://metaso.cn — 秘塔AI搜索,需 api_key
1034 # # 设置 METASO_API_KEY 或 [search] api_key
1035 # # searxng: https://docs.searxng.org — trusted/self-hosted JSON API,
1036 # # set base_url; no public instance is used by default
1037 # # instance must enable JSON (settings.yml search.formats); HTML-only instances 403
1038 # # baidu: 百度 AI Search via qianfan.baidubce.com,需 api_key
1039 # # volcengine: 火山引擎 Ark web_search (免费 2 万次/月), 需 api_key
1040 # # 也回退到 VOLCENGINE_API_KEY / VOLCENGINE_ARK_API_KEY / ARK_API_KEY 环境变量
1041 # # sofya: https://sofya.co — AI search returning full page
1042 # # content (not snippets), needs api_key (ay_live_...);
1043 # # also falls back to the SOFYA_API_KEY env var
1044 # # serply: https://serply.io Google organic results with
1045 # # snippets, needs api_key;
1046 # # also falls back to the SERPLY_API_KEY env var
1047 # base_url = "https://search.example/" # optional DuckDuckGo-compatible HTML endpoint;
1048 # # required SearXNG root or /search endpoint
1049 # api_key = "YOUR_SEARCH_KEY" # required for tavily, bocha, metaso, baidu, volcengine, sofya, and serply; optional for firecrawl (raises the keyless quota); unused by searxng
1050 # # WARNING: treat config.toml like a secret file when
1051 # # storing API keys. Prefer env vars for local smoke tests.
1052 #
1053 # Env-var overrides:
1054 # CODEWHALE_SEARCH_PROVIDER → search.provider
1055 # DEEPSEEK_SEARCH_PROVIDER → search.provider (legacy alias)
1056 # CODEWHALE_SEARCH_API_KEY → search.api_key
1057 # DEEPSEEK_SEARCH_API_KEY → search.api_key (legacy alias)
1058 # CODEWHALE_SEARCH_BASE_URL → search.base_url
1059 # DEEPSEEK_SEARCH_BASE_URL → search.base_url (legacy alias)
1060 # FIRECRAWL_API_KEY → firecrawl key fallback
1061 # METASO_API_KEY → metaso key fallback
1062 # BAIDU_SEARCH_API_KEY → baidu key fallback
1063 # VOLCENGINE_API_KEY / VOLCENGINE_ARK_API_KEY / ARK_API_KEY → volcengine key fallback
1064 # SOFYA_API_KEY → sofya key fallback
1065 # SERPLY_API_KEY → serply key fallback
1066 # TAVILY_API_KEY → tavily autodetect when provider is unset (explicit
1067 # provider = "..." or CODEWHALE_SEARCH_PROVIDER still
1068 # wins; the autodetect is never persisted)
1069
1070 # ─────────────────────────────────────────────────────────────────────────────────
1071 # Network Policy (#135)
1072 # ─────────────────────────────────────────────────────────────────────────────────
1073 # Per-domain allow/deny rules for outbound network calls made by the TUI's
1074 # tools (`fetch_url`, `web_search`) and the MCP HTTP transport. Stdio MCP
1075 # servers and direct LLM API calls are unaffected.
1076 #
1077 # Precedence: deny wins. A host listed in both `allow` and `deny` is denied.
1078 #
1079 # Host-matching rules:
1080 # - Exact match: `api.deepseek.com` matches only `api.deepseek.com`.
1081 # - Subdomain wildcard: an entry starting with `.` (e.g. `.example.com`)
1082 # matches `api.example.com` and `a.b.example.com` but not the apex
1083 # `example.com`. To cover both, list both. `*.example.com` is also accepted.
1084 #
1085 # Defaults are intentionally conservative: when this section is absent, no
1086 # policy is enforced (mirrors pre-v0.7.0 behavior). To opt in:
1087 #
1088 # [network]
1089 # default = "prompt" # allow | deny | prompt
1090 # allow = ["api.deepseek.com", "github.com", ".githubusercontent.com"]
1091 # deny = []
1092 # proxy = ["github.com", ".githubusercontent.com"]
1093 # proxy_fake_ip_cidrs = ["198.18.0.0/15"] # requires both matching host and address
1094 # audit = true # one line per call to ~/.codewhale/audit.log
1095
1096 # ─────────────────────────────────────────────────────────────────────────────────
1097 # Verifier preview (#2093)
1098 # ─────────────────────────────────────────────────────────────────────────────────
1099 # Enables automatic claim-of-done verifier preview once the runtime trigger is
1100 # active. Manual `run_verifiers` remains available even when this is false.
1101 #
1102 # [verifier]
1103 # enabled = false
1104
1105 # ─────────────────────────────────────────────────────────────────────────────────
1106 # Advisor / Watcher (#3982)
1107 # ─────────────────────────────────────────────────────────────────────────────────
1108 # Optional background watcher that fires after each turn that contains tool
1109 # calls. It reads a bounded slice of recent tool interactions, makes a concise
1110 # LLM advisory call, and emits a brief note into the status area.
1111 #
1112 # Off by default. Enable with `[advisor] enabled = true` or toggle per-session
1113 # with `/advisor on` / `/advisor off`.
1114 #
1115 # Options (the full set — `AdvisorConfigToml` in crates/config/src/lib.rs):
1116 # enabled — master switch (default: false)
1117 # max_tool_calls — number of recent tool call/result pairs to read
1118 # (default: 10, clamped to 1–50)
1119 # rate_limit_secs — minimum seconds between successive advisor notes
1120 # (default: 60, clamped to 5–3600)
1121 # dedup_window_secs — notes whose content hash matches the previous note
1122 # within this window are dropped (default: 300)
1123 # model — model override for the advisory call; when absent the
1124 # advisor reuses the session's current model
1125 #
1126 # Unknown keys under [advisor] are silently ignored, not rejected — a typo
1127 # leaves the default in place with no error.
1128 #
1129 # [advisor]
1130 # enabled = false
1131 # max_tool_calls = 10
1132 # rate_limit_secs = 60
1133 # dedup_window_secs = 300
1134 # model = "deepseek-v4-flash"
1135
1136 # ─────────────────────────────────────────────────────────────────────────────────
1137 # Skills (#140)
1138 # ─────────────────────────────────────────────────────────────────────────────────
1139 # Settings for the `/skill install <spec>` community-skill installer.
1140 # * registry_url — curated index.json that resolves bare names to
1141 # `github:owner/repo` specs. Override to point at
1142 # a private fork or internal mirror.
1143 # * max_install_size_bytes — per-skill uncompressed size cap. Tarballs that
1144 # exceed this limit are rejected during validation.
1145 # Default: 5 MiB.
1146 #
1147 # `/skill install` is gated by `[network]`. Make sure `github.com` and
1148 # `raw.githubusercontent.com` are reachable (default `prompt` is fine — you'll
1149 # be asked once and can persist) before running it.
1150 #
1151 # [skills]
1152 # registry_url = "https://raw.githubusercontent.com/Hmbown/deepseek-skills/main/index.json"
1153 # max_install_size_bytes = 5_242_880
1154 # scan_codewhale_only = false # true: ignore Claude/OpenCode/Cursor/agentskills.io skill dirs
1155
1156 # ─────────────────────────────────────────────────────────────────────────────────
1157 # TUI
1158 # ─────────────────────────────────────────────────────────────────────────────────
1159 [tui]
1160 alternate_screen = "auto" # auto/always start on the alternate screen; never starts inline (keeps terminal scrollback). /fullscreen and /inline switch at runtime
1161 mouse_capture = true # true: TUI-owned mouse selection (payload set by selection_copy_markdown); false: raw terminal selection/copy
1162 selection_copy_markdown = true # true: copy transcript drag selections as Markdown source; false: rendered text
1163 terminal_probe_timeout_ms = 500 # optional startup terminal-mode timeout (100-5000ms)
1164 stream_chunk_timeout_secs = 900 # optional SSE idle timeout per chunk (0 = default, 1-3600)
1165 # Model steps are uncapped by default. Uncomment to install an explicit
1166 # ceiling; omission or 0 means no step limit, positive values clamp to 1-100000.
1167 # max_model_steps = 1000
1168 # Wall-clock and stream budgets remain finite; 0 selects their default.
1169 turn_wall_clock_secs = 3600 # cumulative wall clock for one turn, excluding time
1170 # blocked on a human approval (0 = default, 30-86400)
1171 stream_max_content_mb = 10 # per-step cap on streamed content (0 = default, 1-512)
1172 stream_max_duration_secs = 1800 # per-step cap on one stream's duration (0 = default, 10-86400)
1173 osc8_links = true # emit OSC 8 escapes around URLs (Cmd+click in iTerm2/Ghostty/Kitty/WezTerm/Terminal.app 13+); set false for terminals that misrender
1174 # What the bottom chrome shows. Each key is one thing on screen: `mode` is the
1175 # posture bar's plan/act/operate chip, everything else is a segment of the
1176 # metrics line under it. Omit the key to use the built-in default; set [] to
1177 # strip the metrics line down to the help hint. You can also edit this
1178 # interactively with `/statusline`.
1179 # Supported keys: mode, model, context_percent, cost,
1180 # balance (prepaid providers only: DeepSeek, DeepSeekCN, OpenRouter,
1181 # SiliconFlow), cache, tokens, ttft, output_rate, workspace, git_branch.
1182 # Legacy session_metrics enables both ttft and output_rate.
1183 # Retired in 0.9.13: status, agents, reasoning_replay, prefix_stability,
1184 # last_tool_elapsed, rate_limit — they drove nothing. Old files
1185 # keep loading; the retired keys are ignored.
1186 # status_items = ["mode", "model", "context_percent", "cost", "tokens"]
1187 # Size presets for the two rows themselves (#5950) — composition stays in
1188 # status_items; these only decide how much of a row paints:
1189 # posture_bar = "full" # full | compact | hidden (default full)
1190 # # compact keeps the posture chips (and the cap
1191 # # warning) and drops the clocks, counts and hint;
1192 # # hidden gives the row to the transcript.
1193 # metrics_line = "compact" # full | compact | hidden (default compact)
1194 # # compact keeps the route, context reading, cost
1195 # # and balance plus selected TTFT/rate when they fit.
1196 # # It drops secondary counts and help; hidden gives the row to the transcript.
1197 # # Also settable at runtime: /config posture_bar compact
1198 # notification_condition = "unfocused" # unfocused | always | never
1199 # "unfocused" = notify only after this terminal has been
1200 # in the background for two seconds (default);
1201 # "always" = allow configured notifications while focused;
1202 # successful turns skip the threshold;
1203 # "never" = suppress all operator notifications.
1204 # locale = "auto" # UI chrome language: auto | en | ja | zh-Hans | zh-Hant | pt-BR | es-419
1205 # # | vi | ko | ca | de | fr | id | hi | ru | uk
1206 # # "auto" reads LC_ALL → LC_MESSAGES → LANG; falls back to English.
1207 # # Override: `locale = "zh-Hans"` for Simplified Chinese regardless of OS locale.
1208 # # Also settable at runtime: /config locale zh-Hans
1209 # # Note: this only affects TUI labels/chrome — it does NOT change model output language.
1210 # mention_menu_behavior = "fuzzy" # fuzzy | browser; browser lists immediate directory children for @-mentions.
1211
1212 # ─────────────────────────────────────────────────────────────────────────────────
1213 # Transcript
1214 # ─────────────────────────────────────────────────────────────────────────────────
1215 # Prose — user messages, assistant answers, and reasoning/thinking — fills the
1216 # full content width, consistent with tool/status cells and the wide-frame
1217 # decision in #5322 (#5436). Owners who want a bounded reading measure on
1218 # ultrawide terminals can cap it in columns.
1219 [transcript]
1220 # prose_measure = 120 # positive integer: cap prose wrap at N columns.
1221 # 0 or absent = full content width (default).
1222 # Must be a positive whole number; tool, diff, and
1223 # status cells always keep the full content width.
1224
1225 # ─────────────────────────────────────────────────────────────────────────────────
1226 # Feature Flags
1227 # ─────────────────────────────────────────────────────────────────────────────────
1228 [features]
1229 shell_tool = true
1230 subagents = true
1231 web_search = true # enables canonical web.run plus the compatibility web_search alias
1232 apply_patch = true
1233 mcp = true
1234 exec_policy = true
1235 # vision_model = false # enable vision model for image_analyze tool
1236 # verify_tool = false # disable the agent-callable `verify` self-critique tool
1237 # (#4196). On by default; the agent decides when to spend
1238 # the extra reasoning, so cost is only incurred on demand.
1239 # Set false to remove it from the model's tool catalog.
1240
1241 # ─────────────────────────────────────────────────────────────────────────────────
1242 # Vision Model Configuration (optional)
1243 # ─────────────────────────────────────────────────────────────────────────────────
1244 # Uses an OpenAI-compatible vision model API for the `image_analyze` tool.
1245 # api_key inherits from the main config if not specified.
1246 #
1247 # [vision_model]
1248 # model = "gemini-3.1-flash-lite-preview" # Required: vision-capable model ID
1249 # api_key = "YOUR_API_KEY" # Optional: defaults to main api_key
1250 # base_url = "https://generativelanguage.googleapis.com/v1beta/openai/" # Optional
1251 #
1252 # Xiaomi MiMo image understanding can be configured through the same tool:
1253 # model = "mimo-v2.5"
1254 # api_key = "YOUR_XIAOMI_KEY"
1255 # base_url = "https://token-plan-sgp.xiaomimimo.com/v1" # Token Plan / tp- keys
1256
1257 # ─────────────────────────────────────────────────────────────────────────────────
1258 # Retry Configuration
1259 # ─────────────────────────────────────────────────────────────────────────────────
1260 [retry]
1261 enabled = true
1262 max_retries = 3
1263 initial_delay = 1.0
1264 max_delay = 60.0
1265 exponential_base = 2.0
1266
1267 # ─────────────────────────────────────────────────────────────────────────────────
1268 # Goal loop (`[goal]`) — operate-mode persistent goals
1269 # ─────────────────────────────────────────────────────────────────────────────────
1270 # Operate-mode goals run to their completion gate with no default token, time,
1271 # or continuation ceiling. Token/time budgets, when supplied, are telemetry
1272 # only and do not stop a goal. The keys below are the opt-in circuit breakers.
1273 # [goal]
1274 # Optional safety backstop on automatic goal continuation passes.
1275 # Default: 0 (unlimited). Set a positive value to opt into a ceiling.
1276 # max_continuations = 100
1277 # Optional cancellable quiet period between successful turns, useful for
1278 # coordinator goals that poll on a cadence instead of keeping one provider
1279 # turn open. Default: 0 (continue immediately). Cap: 86400 (24h).
1280 # continuation_delay_seconds = 300
1281 # Per-turn step allowance while a goal is active (#5994): larger but still
1282 # finite. Default: 1000 (0/absent resolves to 1000). Range: 1..=100,000.
1283 # Bounds each turn, never the number of continuation passes; explicit
1284 # per-invocation ceilings (exec --max-turns, worker caps) always win.
1285 # max_steps = 1000
1286
1287 # ─────────────────────────────────────────────────────────────────────────────────
1288 # Context Compaction
1289 # ─────────────────────────────────────────────────────────────────────────────────
1290 # Auto-compaction is a saved UI setting edited with `/config` (`auto_compact`).
1291 # The optional saved threshold setting is `auto_compact_threshold_percent`
1292 # (default 80). There is no config-file
1293 # `[compaction]` table yet; runtime compaction budgets are chosen by the TUI
1294 # from the active model/context window.
1295
1296 # [context] supports `enabled` (default false) and `project_pack` (#4781).
1297 # The old seam-manager keys (verbatim_window_turns, l1/l2/l3_threshold,
1298 # seam_model) are parsed but ignored — seam_manager.rs no longer exists.
1299
1300 # ─────────────────────────────────────────────────────────────────────────────────
1301 # Workshop / Large-Output Routing (#548)
1302 # ─────────────────────────────────────────────────────────────────────────────────
1303 # Tool outputs exceeding `large_output_threshold_tokens` are routed through a
1304 # V4-Flash synthesis sub-agent. Only the synthesis reaches the parent context;
1305 # the raw text is stored in the workshop variable `last_tool_result` so the
1306 # parent can call `promote_to_context` later if it needs the full content.
1307 #
1308 # Per-tool overrides let high-volume tools (e.g. Bash) use tighter
1309 # thresholds without changing the global default. Name tools by the identifiers
1310 # the model actually sees — `exec_shell`, `grep_files`, and `web_search` were
1311 # retired and an override keyed to them matches nothing.
1312 #
1313 # There is no per-call `raw = true` escape. It was documented here, but
1314 # `raw = true` on a tool call does NOT bypass routing: the adaptive router
1315 # ignores it, and it is honoured only under the legacy
1316 # `CODEWHALE_CLASSIC_OUTPUT_ROUTING` switch. No tool advertises it.
1317 #
1318 # [workshop]
1319 # large_output_threshold_tokens = 4096
1320 # # Optional model-visible byte ceilings (#5367). Absent keeps the
1321 # # compile-time defaults (read 50KiB / read_file 16KiB, then the
1322 # # compact 12K-char floor). Values raise the floor; they never lower
1323 # # it. Hard cap is 2MiB.
1324 # # read_result_max_bytes = 102400
1325 # # tool_result_max_bytes = 102400
1326 # [workshop.per_tool_thresholds]
1327 # Bash = 2048 # shell output synthesised aggressively
1328 # Web = 8192 # web results can be large; give them more room
1329
1330 # ─────────────────────────────────────────────────────────────────────────────────
1331 # Profile Example (for multiple environments)
1332 # ─────────────────────────────────────────────────────────────────────────────────
1333 # Select a profile with `deepseek --profile <name>` or `DEEPSEEK_PROFILE=<name>`.
1334 [profiles.work]
1335 api_key = "WORK_DEEPSEEK_API_KEY"
1336 base_url = "https://api.deepseek.com/beta"
1337
1338 [profiles.dev]
1339 api_key = "DEV_DEEPSEEK_API_KEY"
1340 allow_shell = true
1341
1342 [profiles.nvidia-nim]
1343 provider = "nvidia-nim"
1344 api_key = "YOUR_NVIDIA_API_KEY"
1345 base_url = "https://integrate.api.nvidia.com/v1"
1346 default_text_model = "deepseek-ai/deepseek-v4-pro"
1347
1348 # ─────────────────────────────────────────────────────────────────────────────────
1349 # Desktop Notifications (OSC 9 / BEL on long agent-turn completion)
1350 # ─────────────────────────────────────────────────────────────────────────────────
1351 # Emits an escape sequence to the terminal when a turn **completes successfully**
1352 # and took longer than `threshold_secs`. Failed or cancelled turns are
1353 # intentionally silent. Useful when you tab away from the TUI and want an alert
1354 # for "your task is ready".
1355 #
1356 # method = "auto" # auto | osc9 | kitty | ghostty | bel | off
1357 # auto: native/terminal banner for known terminals.
1358 # On macOS, the native banner is silent; on Linux,
1359 # unknown terminals stay silent instead of ringing BEL.
1360 # osc9: \x1b]9;<msg>\x07 (iTerm2-style; shows macOS notification)
1361 # bel: explicit \x07 beep (MessageBeep on Windows)
1362 # off: disable banners; an explicit completion sound is independent
1363 # threshold_secs = 30 # only notify when the turn took >= this many seconds
1364 # include_summary = false # include elapsed time + cost in the notification body
1365 # subagent_completion = "final-only" # always | final-only | off — per-subagent
1366 # notifications during fleet/workflow runs. final-only
1367 # (default) stays quiet mid-run and fires once when the
1368 # batch drains; off silences them entirely.
1369 # completion_sound = "off" # off | beep | bell | file — opt-in turn-completion sound
1370 # sound_file = "E:\\google\\downloads\\notify.wav" # WAV used when completion_sound = "file" (Windows)
1371 [notifications]
1372 # method = "auto"
1373 # threshold_secs = 30
1374 # include_summary = false
1375 # subagent_completion = "final-only"
1376 # completion_sound = "off"
1377 # sound_file = "E:\\google\\downloads\\notify.wav"
1378
1379 # Lifecycle event outbox: opt-in JSONL stream of session/turn/subagent
1380 # lifecycle events for supervisors and automation harnesses. One JSON line
1381 # per event (RuntimeEventEnvelope schema), appended and flushed on every
1382 # emit; seq is monotonic per file and recovers from the last line on open.
1383 # UNCOMMENT `path` TO ENABLE — unset/empty = OFF = behavior unchanged.
1384 # Fires for interactive TUI sessions AND headless `codewhale exec` runs.
1385 # See docs/CONFIGURATION.md → Lifecycle Outbox for the file contract.
1386 # [lifecycle_outbox]
1387 # path = "~/.codewhale/notifications/outbox.jsonl"
1388 # webhook_url = "https://example.com/hooks/codewhale" # optional: POST {"at", "event"} JSON per event
1389 # webhook_token = "" # optional: sent as `Authorization: Bearer <token>`
1390 # # delivery is best-effort: failures are logged and dropped
1391
1392 # Opt-in per-event sound cues (#4817): deterministic, terminal-bell level
1393 # (BEL bytes only — functional signals, platform-safe no-op when the terminal
1394 # ignores BEL). Off by default. When completion_sound is active, turn-complete
1395 # is left to that channel so the two never double-ding.
1396 # [notifications.event_sound]
1397 # enabled = false # master switch, default off
1398 # events = ["turn-complete", "approval-needed"] # allow-list; unknown names ignored
1399 # min_interval_ms = 2000 # per-event rate limit
1400 # quiet = false # true silences all event sounds
1401
1402 # ─────────────────────────────────────────────────────────────────────────────────
1403 # Workspace Snapshots (#137)
1404 # ─────────────────────────────────────────────────────────────────────────────────
1405 # Each turn the TUI takes a `pre-turn:<seq>` and `post-turn:<seq>` snapshot of
1406 # your workspace into a side-git repo at:
1407 #
1408 # ~/.codewhale/snapshots/<project_hash>/<worktree_hash>/.git
1409 #
1410 # Your own `.git` is never touched — `--git-dir` and `--work-tree` are always
1411 # set together when shelling out to git. Use `/restore N` (slash command) or
1412 # the `revert_turn` tool to roll the working tree back. Conversation history
1413 # is unaffected.
1414 #
1415 # Disk footprint: ~1-2 GB worst case for a 100 MB workspace × 12 turns/day,
1416 # typically far less thanks to git's content-addressed storage. The session
1417 # boot prunes anything older than `max_age_days` (default 7).
1418 #
1419 # [snapshots]
1420 # enabled = true # Snapshot workspace pre/post each turn for /restore
1421 # max_age_days = 7 # Older snapshots pruned at session start
1422 # max_workspace_gb = 2 # Snapshots self-disable on first init when the
1423 # # non-excluded workspace exceeds this size in GB
1424 # # (v0.8.32). Default 2 GB protects against running
1425 # # codewhale in directories with hundreds of GB
1426 # # of datasets / model weights / docker dumps where
1427 # # `git add -A` would hang the TUI for hours. Set
1428 # # to 0 to disable the cap (v0.8.31 behaviour);
1429 # # raise to a higher number for legitimate large
1430 # # monorepos.
1431
1432 # ─────────────────────────────────────────────────────────────────────────────────
1433 # LSP Diagnostics (post-edit) (#136)
1434 # ─────────────────────────────────────────────────────────────────────────────────
1435 # After every successful file edit (`edit_file`, `apply_patch`, `write_file`),
1436 # the engine asks an LSP server for diagnostics on the file and injects them
1437 # as a synthetic system message before the next API call. This lets the agent
1438 # see compile breaks immediately without round-tripping through the user.
1439 #
1440 # Enabled by default. Failure modes are non-blocking: a missing LSP binary,
1441 # a crashed server, or a timeout simply skips the post-edit hook for that
1442 # turn — the agent's work is never blocked.
1443 #
1444 # Built-in language → server defaults:
1445 # rust → rust-analyzer
1446 # go → gopls serve
1447 # python → pyright-langserver --stdio
1448 # typescript → typescript-language-server --stdio
1449 # java → jdtls
1450 # php → intelephense --stdio
1451 # vue → vue-language-server --stdio
1452 # c, cpp → clangd
1453 #
1454 # Java support uses Eclipse JDT LS via the `jdtls` command. IntelliJ IDEA is
1455 # not required, and installing IntelliJ IDEA alone does not install `jdtls`.
1456 #
1457 # Override the defaults via the `servers` table below.
1458 #
1459 # For languages not in the built-in list (Ruby, C#, Swift, etc.), use
1460 # `[lsp.custom.<ext>]` to register a language server:
1461 #
1462 # [lsp.custom.rb]
1463 # command = "ruby-lsp"
1464 # args = ["--stdio"]
1465 # language_id = "ruby"
1466 #
1467 # [lsp.custom.cs]
1468 # command = "csharp-ls"
1469 # args = []
1470 # language_id = "csharp"
1471 #
1472 # [lsp.custom.swift]
1473 # command = "sourcekit-lsp"
1474 # language_id = "swift"
1475 [lsp]
1476 # enabled = true
1477 # poll_after_edit_ms = 5000
1478 # max_diagnostics_per_file = 20
1479 # include_warnings = false
1480 # [lsp.servers]
1481 # rust = ["rust-analyzer"]
1482 # go = ["gopls", "serve"]
1483 # java = ["jdtls"]
1484 # php = ["intelephense", "--stdio"]
1485 # vue = ["vue-language-server", "--stdio"]
1486
1487 # ─────────────────────────────────────────────────────────────────────────────────
1488 # Hooks (optional)
1489 # ─────────────────────────────────────────────────────────────────────────────────
1490 # Hooks run shell commands on lifecycle events (session start/end, tool calls, etc.).
1491 # Configure as `[[hooks.hooks]]` under a `[hooks]` table.
1492 #
1493 # SCOPE: hooks are a TUI runtime feature. They fire from the interactive TUI
1494 # and the engine turn loop it drives. `codewhale exec`, the CLI subcommands,
1495 # the app-server / ACP surfaces, and the `workflow` tool do NOT fire them.
1496 #
1497 # Available events (all 11): session_start, session_end, message_submit,
1498 # tool_call_before, tool_call_after, mode_change, on_error, turn_end,
1499 # subagent_spawn, subagent_complete, shell_env.
1500 # See docs/HOOKS.md for the per-event payload, env var, and steering contract.
1501 #
1502 # `message_submit`, `tool_call_before`, and `shell_env` are the only events
1503 # whose result can change what Codewhale does. The rest are observer-only —
1504 # which means their RESULT is ignored, not that the command is harmless. Every
1505 # hook is an arbitrary shell command running with your credentials.
1506 #
1507 # Note: `default_timeout_secs` below OVERRIDES each hook's own `timeout_secs`.
1508 # Leave it unset if you want per-hook timeouts to apply. `/hooks list` shows the
1509 # effective value and names the override. `default_timeout_secs = 0` is
1510 # REJECTED at load — it would expire every hook immediately — and per-hook
1511 # `timeout_secs` applies instead. The timeout applies to background hooks too:
1512 # on expiry the whole process group is killed and then reaped, best-effort,
1513 # with a bounded reap wait (see docs/HOOKS.md → Timeouts).
1514 #
1515 # `background = true` means submitted and never awaited. The hook still gets
1516 # the documented stdin payload, environment, and timeout — it just has no exit
1517 # code, so it cannot steer. `shell_env` ignores the flag and always runs in the
1518 # foreground because its stdout is the contract.
1519 #
1520 # A condition that references context its event never carries is REJECTED at
1521 # load, logged, and shown by `/hooks list` — for example an `exit_code`
1522 # condition outside `tool_call_after` / `on_error`, or a `mode` condition on
1523 # `shell_env`. Rejection is per entry: a broken hook never drops another one
1524 # that happens to share its name. `on_error` fires for tool failures with the
1525 # tool name, call id, and reported exit code attached, so tool-scoped and
1526 # exit-code-scoped `on_error` hooks are supported.
1527 #
1528 # `shell_env` (#456) is special: the hook runs immediately before each
1529 # `exec_shell` invocation and its stdout is parsed as `KEY=VALUE\n` lines.
1530 # Those vars are applied on top of `exec_shell`'s environment. For LOCAL
1531 # execution that environment is built from a fixed allowlist of parent
1532 # variables (PATH, HOME, LANG, TERM, …) — an ambient secret exported in your
1533 # terminal is NOT forwarded to `exec_shell` by itself, so this hook is the
1534 # supported way to supply one. If an external sandbox backend is configured,
1535 # that allowlist does NOT apply: the backend owns its base environment and your
1536 # `shell_env` values are TRANSMITTED to it. Later hooks override
1537 # earlier ones. Use this for ephemeral credentials, per-skill PATH adjustments,
1538 # or short-lived tokens. The resolved KEY names (NEVER values) are written to
1539 # `~/.codewhale/audit.log` so each session can be reconciled later. Hook
1540 # failure / timeout simply contributes no vars — it does not abort the shell
1541 # call.
1542 #
1543 # [hooks]
1544 # enabled = true
1545 # default_timeout_secs = 30
1546 #
1547 # [[hooks.hooks]]
1548 # event = "session_start"
1549 # command = "echo 'Codewhale session started'"
1550 #
1551 # # Inject ephemeral creds into every shell call. Output one
1552 # # KEY=VALUE per line on stdout (export prefix optional).
1553 # [[hooks.hooks]]
1554 # name = "aws-creds"
1555 # event = "shell_env"
1556 # command = "aws-vault export my-profile --format=env"
1557 # # Optionally limit to specific tool names / categories:
1558 # # condition = { type = "tool_category", category = "shell" }
1559 #
1560 # # Observe sub-agent lifecycle events. These hooks receive bounded JSON
1561 # # metadata on stdin and are warn-only: failures do not affect sub-agent
1562 # # scheduling, prompts, or results. continue_on_error has no effect for
1563 # # these observer events; later matching hooks always continue.
1564 # [[hooks.hooks]]
1565 # name = "subagent-audit"
1566 # event = "subagent_complete"
1567 # command = "~/.codewhale/hooks/subagent-audit.sh"
1568
1569 # ─────────────────────────────────────────────────────────────────────────────────
1570 # Runtime API (`deepseek serve --http`) (#561)
1571 # ─────────────────────────────────────────────────────────────────────────────────
1572 # Tuning knobs for the local HTTP/SSE daemon. The server binds to 127.0.0.1
1573 # by default and is intended for local UIs (whalescale-desktop, dashboards,
1574 # automation scripts). Today this section only controls the CORS allow-list;
1575 # host/port/workers stay on `--host`, `--port`, and `--workers` flags.
1576 #
1577 # Built-in defaults always include:
1578 # http://localhost:3000 http://127.0.0.1:3000
1579 # http://localhost:1420 http://127.0.0.1:1420
1580 # tauri://localhost
1581 #
1582 # Use `cors_origins` to add extra dev origins (e.g. Vite's default `:5173`).
1583 # User entries STACK on top of the defaults — they do not replace them. The
1584 # CLI flag `--cors-origin URL` (repeatable) and env var
1585 # `DEEPSEEK_CORS_ORIGINS=url1,url2` resolve to the same merged list.
1586 #
1587 # [runtime_api]
1588 # cors_origins = ["http://localhost:5173", "http://127.0.0.1:5173"]
1589
1590 # ─────────────────────────────────────────────────────────────────────────────────
1591 # Tool Overrides & Plugins ([tools])
1592 # ─────────────────────────────────────────────────────────────────────────────────
1593 # The `[tools]` table lets you replace any built-in tool with a custom
1594 # implementation (script or command) or disable it entirely — without
1595 # forking or recompiling the binary.
1596 #
1597 # Plugin scripts dropped in the plugin directory are auto-discovered and
1598 # registered as model-visible tools alongside the built-in ones.
1599 #
1600 # Scripts receive the tool's JSON input on **stdin** and must return a
1601 # JSON `ToolResult` (`{"content": "...", "success": true}`) on **stdout**.
1602 #
1603 # [tools]
1604 # # Custom plugin directory (defaults to `~/.codewhale/tools/`)
1605 # plugin_dir = "~/.codewhale/tools"
1606 #
1607 # [tools.overrides]
1608 # # Disable a tool entirely — removes it from the model-visible catalog.
1609 # "code_execution" = { type = "disabled" }
1610 #
1611 # # Replace a tool with a script. Relative paths resolve against plugin_dir.
1612 # "exec_shell" = { type = "script", path = "audit-exec-shell.sh" }
1613 #
1614 # # Replace a tool with a command (binary on PATH or absolute path).
1615 # "read_file" = { type = "command", command = "bat", args = ["--paging=never"] }
1616 #
1617 # # Scripts can also accept static arguments before the JSON input:
1618 # "fetch_url" = { type = "script", path = "cached-fetch.sh", args = ["--ttl", "300"] }
1619
1620 # ──────────── Enterprise example: audit-logging exec_shell wrapper ──────────────
1621 # Drop `audit-exec-shell.sh` in `~/.codewhale/tools/` and enable with:
1622 #
1623 # [tools.overrides]
1624 # "exec_shell" = { type = "script", path = "audit-exec-shell.sh" }
1625 #
1626 # The wrapper logs every request to `~/.codewhale/audit/exec_shell.log`, then
1627 # delegates to your own approved shell executor. Do not pipe the raw JSON
1628 # request into `sh -s`; parse the command field and enforce your policy first.
1629 #
1630 # ```sh
1631 # #!/usr/bin/env sh
1632 # # name: exec_shell
1633 # # description: Audit-logging wrapper for exec_shell
1634 # # approval: required
1635 # LOGDIR="${HOME}/.codewhale/audit"
1636 # mkdir -p "$LOGDIR"
1637 # LOGFILE="$LOGDIR/exec_shell.log"
1638 # input=$(cat)
1639 # echo "[$(date -Iseconds)] $input" >> "$LOGFILE"
1640 # printf '%s\n' '{"content":"audit wrapper dry run: configure an executor","success":false}'
1641 # ```
1642
1643 # ─────────────────────────────────────────────────────────────────────────────────
1644 # Workflow automatic launch, approval, isolation, and activity (#4128)
1645 # ─────────────────────────────────────────────────────────────────────────────────
1646 # First-class knobs for automatic Workflow orchestration. When the table is
1647 # omitted entirely, the runtime uses these product defaults. Later launch,
1648 # approval, and activity-persistence paths all read through this one model.
1649 # [workflow]
1650 # # Allow the parent agent to auto-launch Workflow for multi-agent work.
1651 # # Set false to require an explicit `/workflow` opt-in.
1652 # automatic = true
1653 # # Auto-start read-only plans without an approval card when automatic is on.
1654 # auto_start_read_only = true
1655 # # Require an approval card before write/shell/network/high-budget launches.
1656 # require_approval_for_writes = true
1657 # # Hard ceiling on agents in one Workflow run (matches VM lifetime cap).
1658 # max_children = 1000
1659 # # Maximum concurrently live agents inside one run (others wait for a slot).
1660 # max_concurrent = 16
1661 # # Maximum structural nesting depth accepted for Workflow IR (default 5).
1662 # # This is separate from Runtime child delegation below.
1663 # max_depth = 5
1664 # # Default shared token budget for a Workflow run and its children.
1665 # default_token_budget = 120000
1666
1667 # ─────────────────────────────────────────────────────────────────────────────────
1668 # Agent Fleet roster, role registry, and execution requests (#3165, #3167)
1669 # ─────────────────────────────────────────────────────────────────────────────────
1670 # [fleet]
1671 # # Fleet stores member identity and route intent. Project trust, host identity,
1672 # # secrets, approvals, sandboxing, filesystem/network reach, and tool authority
1673 # # are Runtime policy and are deliberately not configured here. Pre-0.9.11
1674 # # trust keys are accepted only as ignored migration input.
1675 #
1676 # # Headless worker execution hardening (#3027)
1677 # [fleet.exec]
1678 # # Tools always allowed regardless of role
1679 # allowed_tools = []
1680 # # Tools always disallowed (overrides role and task spec)
1681 # disallowed_tools = ["exec_shell"]
1682 # # Optional hard ceiling on worker steps (tool calls + model turns).
1683 # # Omit or set 0 for the unbounded default; use a positive value to opt in.
1684 # max_turns = 500
1685 # # Runtime child-agent depth for fleet workers. Shares ONE recursion axis with
1686 # # standalone sub-agents (a fleet worker IS a headless sub-agent). This is an
1687 # # execution request, not Fleet member identity. 0 blocks child agents (the
1688 # # root worker still runs); 3 is the default and 8 is the opt-in hard ceiling.
1689 # max_spawn_depth = 3
1690 # # Extra system prompt injected into every headless worker
1691 # append_system_prompt = "Never modify .git/config or change remotes."
1692 # # Output format: "text" (default) or "stream-json" for ndjson events
1693 # output_format = "text"
1694 #
1695 # # Fleet profiles define named agent configurations the roster can dispatch.
1696 # # Built-in profiles are always available: manager, operator, scout, builder,
1697 # # reviewer, verifier, synthesizer, general. User-defined profiles under
1698 # # [fleet.profiles] override or extend the built-in set by id. Precedence
1699 # # is Workspace (.codewhale/agents/*.toml) > Config ([fleet.profiles]) > BuiltIn.
1700 # # See /fleet setup for an in-app profile-authoring wizard.
1701 # [fleet.profiles.ci-linter]
1702 # slot = "verifier"
1703 # loadout = "fast"
1704 # model = "deepseek-v4-pro"
1705 #
1706 # [fleet.profiles.ci-linter.role]
1707 # name = "CI Linter"
1708 # description = "Runs linters and formatters"
1709 # instructions = "Run cargo fmt --check and cargo clippy; never apply fixes."
1710 #
1711 # [fleet.profiles.pr-reviewer]
1712 # slot = "reviewer"
1713 # loadout = "inherit"
1714 #
1715 # [fleet.profiles.pr-reviewer.role]
1716 # name = "PR Reviewer"
1717 # description = "Reviews PRs with GitHub access"
1718 # instructions = "Review diffs for correctness, regressions, and missing tests."
1719
1720 # ─────────────────────────────────────────────────────────────────────────────────
1721 # Named fleets live in files, not inline tables (0.9.14).
1722 #
1723 # Inline [fleets.<name>] tables were removed: define named fleets as
1724 # `fleets/<name>.toml` under $CODEWHALE_HOME or the workspace instead
1725 # (see docs/FLEET.md). Old inline tables still parse but are ignored.
1726 # ─────────────────────────────────────────────────────────────────────────────────
1727
1728 # ─────────────────────────────────────────────────────────────────────────────────
1729 # Requirements (admin constraints) example file
1730 # ─────────────────────────────────────────────────────────────────────────────────
1731 # allowed_approval_policies = ["on-request", "untrusted", "never"]
1732 # allowed_sandbox_modes = ["read-only", "workspace-write"]
1733
1733 lines TOML