1pub const PI_CORE_TOML: &str = r#"# built-in preset: pi-core — the §1 core with pi's exact defaults, plus pi's four kept extras.
37schema_version = 1
38
39[core]
40effort = "medium" # pi default thinking level (pi§3, src:core/defaults.ts:3)
41max_tool_output_bytes = 51200 # pi's shared truncation policy: 50KB / 2000 lines (pi§1, truncate.ts)
42project_context = true # AGENTS.md/CLAUDE.md global + ancestor walk (pi§2 "Context files")
43env_context = true # pi appends Current date + cwd to the prompt (pi§2, system-prompt.ts:88-170)
44
45[core.retry] # pi agent-level auto-retry (pi§3)
46enabled = true
47max_retries = 3
48base_delay_ms = 2000
49
50[core.tools]
51enabled = ["read_file", "bash", "edit_file", "write_file"] # pi's default-ACTIVE four (pi§1, src:core/sdk.ts:245)
52schema_tier = "full"
53[core.tools.read_file]
54multimodal = true # pi read returns images as attachments (pi§1, read.ts)
55
56[core.skills]
57enabled = true # agentskills.io discovery + progressive disclosure (pi§2; D-7 met by read_file)
58
59[core.compaction]
60enabled = true
61after_messages = 0 # pi's trigger is token pressure, never message count (pi§2)
62reserve_tokens = 16384 # compaction.reserveTokens default (pi§2, pi§6)
63keep_recent_tokens = 20000 # compaction.keepRecentTokens default (pi§2)
64summarize = true # structured Goal/Constraints/Progress/… summary (pi§2, compaction.md)
65
66[core.steering]
67steering_mode = "one-at-a-time" # pi delivery-mode defaults (pi§3 "Message queue", pi§6)
68follow_up_mode = "one-at-a-time"
69
70# ---- modules ON (each is on pi's kept-list, pi§10 closing) ----
71[capabilities.trust]
72enabled = true
73default = "ask"
74# pi's ONE built-in gate (pi§4, trust-manager.ts; defaultProjectTrust "ask")
75[capabilities.session_tree]
76enabled = true
77branch_summaries = true
78labels = true
79# THE core pi feature (pi§10; D5)
80[capabilities.session_share]
81enabled = true
82# /share gist public link (pi§8); human /export HTML is core now (§1.6, S6) and on regardless
83[capabilities.server]
84enabled = true
85# --mode json / --mode rpc embedding ladder (pi§8, pi§10)
86[capabilities.plugins]
87enabled = true
88# everything-is-an-extension (pi§7; D-10 dep satisfied by trust above)
89[capabilities.tui]
90enabled = true
91# §1.9 recorded deviation, default-on in parity presets
92
93# ---- notable OFFs (each a pi FIRST-PARTY omission, pi§10 / catalog §3) ----
94[capabilities.tools_search]
95enabled = false
96# grep/find/ls exist but are OPT-IN even in pi (pi§1 "--tools"); one line re-enables
97[capabilities.mcp]
98enabled = false
99# "intentionally does not include built-in MCP" (pi§7)
100[capabilities.subagents]
101enabled = false
102# example extension only (pi§3 "NO subagents")
103[capabilities.permissions]
104enabled = false
105approval = "never"
106sandbox = "danger_full_access"
107 # pi has NO popups/rules/sandbox (pi§4; README "Permissions & containerization").
108 # C3 fires its MANDATORY warning here BY DESIGN — pi's own docs say containers, not trust in the harness.
109[capabilities.plan_mode]
110enabled = false
111# example ext only (pi§10)
112[capabilities.todos]
113enabled = false
114# example ext only (pi§10)
115[capabilities.tools_background]
116enabled = false
117# "tmux instead" (pi§10)
118[capabilities.tools_web]
119enabled = false
120# web search ships as a SKILL in pi (pi§10)
121[capabilities.checkpoint]
122enabled = false
123# git-checkpoint example ext only (pi§5)
124[capabilities.memory]
125enabled = false
126# no memory subsystem (pi§2)
127[capabilities.hooks]
128enabled = false
129# pi's "hooks" are code extensions, not config-registered commands
130[capabilities.deferred_tools]
131enabled = false
132[capabilities.cache]
133enabled = false
134[capabilities.reduction]
135enabled = false
136# supercode-only OPTIONAL policies off; A7 truncation + rehydrate stay always-on core regardless (§1.13, S1/S7 — no longer a D-8/D-8-error risk)
137[capabilities.model_catalog]
138enabled = false
139[capabilities.model_oauth]
140enabled = false
141# pi HAS /login OAuth (pi§9) — deferred module 27, recorded gap
142"#;
143
144pub const CC_PARITY_TOML: &str = r#"# built-in preset: cc-parity — Claude Code's default surface, composed.
146schema_version = 1
147
148[core]
149model = "anthropic/claude-opus-4-8" # CC account-default Opus 4.8 (cc§9 "Account-type defaults")
150effort = "medium"
151env_context = true # CC startup context: cwd/git status (cc§2 "Startup context")
152project_context = true # CLAUDE.md tiers + directory walk (cc§2); global tier included (§1.4)
153nested_instructions = true # S6/S12 home: subdir CLAUDE.md loaded on demand, CC default (catalog:84) — closes a gap-ledger row
154instruction_imports = true # S6/S12 home: `@path` imports, depth 4, CC default (catalog:85) — closes a gap-ledger row
155
156[core.tools]
157enabled = ["read_file", "bash", "edit_file", "write_file"]
158schema_tier = "full"
159[core.tools.read_file]
160multimodal = true # CC Read renders images/PDFs/notebooks (cc§1 Read)
161[core.tools.edit_file]
162require_read_before_edit = true # S6/S12 home: CC Edit refuses unless the file was read this conversation (catalog:32) — closes the cc-parity gap-ledger row
163notebook_aware = true # S6/S12 home: NotebookEdit cell-level replace/insert/delete (catalog:40) — closes the cc-parity gap-ledger row
164[core.tools.bash]
165timeout_secs = 120 # CC default 2 min, model-raisable (cc§1 Bash)
166
167[core.skills]
168enabled = true # SKILL.md dirs + commands, descriptions-only until invoked (cc§7 Skills)
169
170[core.compaction]
171enabled = true
172summarize = true # CC auto-compaction near limit + /compact [instructions] (cc§2)
173reserve_tokens = 16384 # CC's threshold is pct-based (CLAUDE_CODE_AUTOCOMPACT_PCT_OVERRIDE, cc§2); reserve is our §1.5 equivalent
174
175# ---- modules ON ----
176[capabilities.tools_search]
177enabled = true
178glob = true
179content_search = true
180list_dir = false
181 # Glob + Grep, promptless read-class (cc§1); list_dir OFF (S15 fix) — CC lists dirs via Bash/Glob, Read rejects directories (catalog:33 fn²), so the module's dir-listing sub-tool would be a capability CC users never see
182[capabilities.tools_web]
183enabled = true
184fetch = true
185search = true
186# WebFetch + WebSearch (cc§1)
187[capabilities.tools_question]
188enabled = true
189# AskUserQuestion (cc§1)
190[capabilities.todos]
191enabled = true
192persist = true
193# Task*/TodoWrite checklist, persists across compaction (cc§1, cc§3)
194[capabilities.plan_mode]
195enabled = true
196# Shift+Tab / EnterPlanMode read-only mode (cc§3); dep met by permissions.rules below
197[capabilities.subagents]
198enabled = true
199max_depth = 2
200background = true
201background_prompts = "parent"
202 # Agent tool; background-by-default v2.1.198+, nested allowed (cc§1, cc§3)
203 # C6 resolved via the schema key (S2 fix, not prose): background_prompts = "parent" — background children surface prompts in the parent session (cc§3, claude-code.md:98)
204[capabilities.tools_background]
205enabled = true
206# run_in_background + Ctrl+B (cc§1, cc§8); C6: same parent-surfaced queue as subagents.background_prompts above
207
208[capabilities.permissions]
209enabled = true
210approval = "untrusted" # CC tiered default: read-only never prompts, Bash/edits prompt first-use (cc§4 "Tiered defaults")
211 # No C3: approval != never.
212auto_approved_tools = ["read_file", "glob", "search"] # CC read-only tier (cc§4); `list_dir` removed (S15 — module tool is off above)
213# module 12 in table form (S5): OS sandbox OFF (CC's `/sandbox` is opt-in, cc§4), fs tier unconfined.
214# One key `permissions.sandbox` — the table form, not the bare-scalar shorthand, so no collision.
215[capabilities.permissions.sandbox]
216enabled = false
217tier = "danger_full_access"
218# flip enabled=true + set network.*/escalation for `/sandbox` parity later
219[capabilities.permissions.rules]
220enabled = true # deny→ask→allow first-match IS the CC algebra — native, no translation (C5 decision; cc§4 "Rule sets & evaluation")
221deny = []
222ask = []
223allow = [] # CC ships empty rule sets; "don't ask again" persists into allow at runtime (cc§4)
224[capabilities.permissions.protected_paths]
225enabled = true # never-auto-approved set (cc§4 "Protected paths")
226# Rule-layer floor: file-tools + bash redirect targets + apply_patch + known
227# argv-writers (tee/dd/cp/mv/install/sed -i/truncate/ln); an opaque or
228# dynamic bash write is forced to Ask. Complete OS-level write confinement
229# is `permissions.sandbox`'s job (module 10), not this table's — see
230# `crate::permissions` module doc / `Config::permissions_protected_paths`.
231paths = [".git/**", ".env*", ".claude/**", ".vscode/**", ".idea/**", "~/.claude/settings*"]
232
233[capabilities.trust]
234enabled = true
235default = "ask"
236# workspace trust gates project allow-rules (cc§4)
237[capabilities.mcp]
238enabled = true
239# stdio/HTTP/OAuth, resources, prompts-as-commands (cc§7)
240[capabilities.deferred_tools]
241enabled = true
242core = ["read_file", "bash", "edit_file", "write_file", "glob", "search", "update_plan"]
243 # CC defers MCP tool definitions BY DEFAULT behind ToolSearch (cc§7 "Tool search"); builtins stay eager
244[capabilities.hooks]
245enabled = true
246# config-registered lifecycle hooks (cc§7: 30 events; module ships the CC-compatible subset first)
247[capabilities.memory]
248enabled = true
249# auto memory MEMORY.md + topic files (cc§2); D-9 dep → model_catalog below
250[capabilities.checkpoint]
251enabled = true
252# per-prompt file-history-snapshot → /rewind (cc§5)
253[capabilities.session_tree]
254enabled = true
255branch_summaries = false
256labels = false
257 # CC has the tree DATA MODEL (uuid/parentUuid, cc§5) + /rewind; summaries/labels are pi-isms
258[capabilities.model_catalog]
259enabled = true
260small_model = "anthropic/claude-haiku-4-5"
261fallback = []
262 # aliases + ANTHROPIC_SMALL_FAST_MODEL + fallback chains (cc§9)
263[capabilities.tui]
264enabled = true
265
266# ---- notable OFFs ----
267[capabilities.tools_apply_patch]
268enabled = false
269# CC is edit-only (C1; catalog §5 conflict 1)
270[capabilities.tools_persistent_shell]
271enabled = false
272[capabilities.lsp]
273enabled = false
274# CC's LSP is inactive until a plugin installs it (cc§1) — off matches default
275[capabilities.formatters]
276enabled = false
277[capabilities.session_share]
278enabled = false
279# no PUBLIC share links in CC (D5 OC+PI-only row); `/export`+`/copy` are core now (§1.6 `export_format`, S6) and stay on regardless
280[capabilities.server]
281enabled = false
282# CC has no local HTTP server surface; SDK is in-process
283[capabilities.reduction]
284enabled = false
285[capabilities.cache]
286enabled = false
287# CC caching is provider-automatic (cc§2); plan machinery is a token-saver concern
288[capabilities.structured_output]
289enabled = false
290# --json-schema is headless-only surface; enable per-run
291[capabilities.model_oauth]
292enabled = false
293# recorded gap: CC's DEFAULT auth is subscription OAuth (cc§9) — module 27 deferred
294"#;
295
296pub const CX_PARITY_TOML: &str = r#"# built-in preset: cx-parity — Codex's default surface, composed.
298schema_version = 1
299
300[core]
301effort = "medium" # model_reasoning_effort default tier (cx§6, cx§9)
302env_context = true # <environment_context> block: cwd/sandbox/approval (cx§2)
303project_context = true # AGENTS.md hierarchy, root-down concat, 32KiB cap (cx§2)
304# P2 placement fix: §4.3's own TOML block places `shell_env_snapshot` under
305# `[core.tools]`, but §3.1's schema (the "annotated, exhaustive" canonical
306# definition, line ~598) defines it as a direct `[core]` scalar, not a
307# `core.tools.*` key — `CoreToolsConfig` has no such field, so a literal
308# under-`[core.tools]` placement would silently parse-and-drop it. Moved
309# here to match §3.1 (the schema doc doesn't have this key twice with two
310# different homes; §4.3 is corrected to agree with it).
311shell_env_snapshot = true # S6/S12 home: shell-env snapshotting, cx stable-on feature (catalog:338) — closes a gap-ledger row
312# C4 (catalog §5 conflict 4): Codex's base prompt VARIES BY APPROVAL MODE (cx§2: "proactively run
313# tests only under never"). This preset pins prompt + approval together; when CONTINUING an
314# imported rollout, the emulate path replays the rollout's own persisted base_instructions
315# verbatim (session_meta carries them — cx§2:101; supercode SessionMeta.system_prompt), which is
316# exact prompt parity by construction rather than imitation.
317
318[core.tools]
319enabled = ["bash", "view_image"] # Codex has NO read/write/edit/glob/grep function tools:
320 # reads via shell (cat, rg), writes via apply_patch (cx§1 "File reads/writes"; D1 footnote ¹).
321 # Disabling edit/write advertising is ALSO the C1 resolution.
322 # `view_image` (S6/S12 home, catalog:28) closes the gap-ledger row: with `read_file` off, cx-parity
323 # would otherwise have NO image-input pathway at all, unlike stock Codex's dedicated tool.
324schema_tier = "full"
325
326[core.skills]
327enabled = true # SKILL.md discovery, $skill mentions (cx§7 Skills).
328 # D-7 (S3-amended, no longer a judgment call): the read pathway is bash (`cat`) in the codex
329 # shape — §2.1's D-7 now names read_file|bash explicitly; the resolver warns, doesn't error.
330
331[core.compaction]
332enabled = true
333summarize = true # /compact + auto-compaction at model_auto_compact_token_limit (cx§2)
334
335# ---- modules ON ----
336[capabilities.tools_persistent_shell]
337enabled = true
338# exec_command/write_stdin PTY unified exec (cx§1); supercode has it (builtins.rs:981-984)
339[capabilities.tools_apply_patch]
340enabled = true
341per_model = true
342 # freeform envelope, default write path (cx§1); per_model honors C1 via model_catalog bits (cx§9)
343[capabilities.todos]
344enabled = true
345persist = true
346# update_plan is ALWAYS registered (cx§1); goals (S6/S12 home, catalog:138, cx `/goal`) is the persistent-objective variant of this same module — closes a gap-ledger row
347[capabilities.tools_web]
348enabled = true
349fetch = false
350search = true
351 # Codex has hosted web_search but NO web-fetch tool (cx§1); cached mode default
352[capabilities.tools_background]
353enabled = true
354# background terminals, /ps //stop (cx§1); C6 (S8-corrected defense): under `model_requested`, tools run sandboxed WITHOUT prompting unless the model itself escalates — from a background task's perspective that's an auto-run default, satisfying C6's auto-policy requirement without needing a separate allow-list
355[capabilities.subagents]
356enabled = true
357max_depth = 1
358background = false
359 # multi_agent default-on, agents.max_depth default 1 (cx§1, cx§6)
360[capabilities.deferred_tools]
361enabled = true
362core = ["bash", "shell", "apply_patch", "update_plan"]
363 # ToolExposure::Deferred + native tool_search is Codex's own mechanism (cx§1)
364[capabilities.structured_output]
365enabled = true
366# --output-schema final-response contract (cx§8); module 33, Config.response_format
367
368[capabilities.permissions]
369enabled = true
370approval = "model_requested" # S8 fix: Codex `on-request` default is "the MODEL decides when to ask" (cx§4, protocol.rs:921-924) — NOT supercode's `OnRequest` (client-side allowlist check, config.rs:39-40, 299-302); using the wrong enum value would prompt on every non-allowlisted tool call where stock Codex prompts almost never. `model_requested` is the NEW distinct mode (§3.2) the module must re-implement escalation-initiated-by-the-model for.
371sandbox = "workspace_write" # writes in cwd + tmp, no network (cx§4); RECOMMENDED POSTURE (S16 fix), not upstream's labeled default — codex.md names no sandbox mode "(default)" (unlike approval); this is upstream's own steered guidance ("prefer --sandbox workspace-write", the deprecated --full-auto warning)
372[capabilities.permissions.rules]
373enabled = true # execpolicy .rules allow/prompt/forbidden → translated into deny→ask→allow (C5)
374deny = []
375ask = []
376allow = []
377[capabilities.permissions.protected_paths]
378enabled = true
379# Rule-layer floor (file-tools + bash redirect targets + apply_patch + known
380# argv-writers; opaque/dynamic bash writes forced to Ask) — NOT the same as
381# cx's `workspace_write` OS sandbox read-only mount above; see
382# `crate::permissions` module doc for exactly what is/isn't covered here.
383paths = [".git/**", ".codex/**"] # read-only even inside writable roots (cx§4 workspace-write)
384
385[capabilities.trust]
386enabled = true
387default = "ask"
388# [projects] trust_level gate + hook hash-trust (cx§4:153, cx§7)
389[capabilities.mcp]
390enabled = true
391serve = true
392# full client stack (cx§7); serve = codex mcp-server analog (module 16)
393[capabilities.hooks]
394enabled = true
395# CC-compatible 10-event shape, hash-trusted (cx§7 "Lifecycle hooks")
396[capabilities.model_catalog]
397enabled = true
398# capability bits (apply_patch_tool_type, supports_search_tool) drive
399 # per-model tool swaps — the C1 resolution machinery (cx§9 "Model catalog")
400[capabilities.tui]
401enabled = true
402
403# ---- notable OFFs ----
404[capabilities.tools_search]
405enabled = false
406# no glob/grep tools; "prefer rg" via shell is prompt guidance (cx§2)
407[capabilities.tools_question]
408enabled = false
409# request_user_input is experimental-gated at the pin (cx§1)
410[capabilities.plan_mode]
411enabled = false
412# /plan is effort-tier steering, not a CC/OC restriction mode (cx§6; catalog D1 CC+OC)
413[capabilities.memory]
414enabled = false
415# [features].memories = false default (cx§6, cx§7)
416[capabilities.checkpoint]
417enabled = false
418# no shadow-git; ghost_snapshot is a legacy no-op (cx§6)
419[capabilities.session_tree]
420enabled = false
421# rollout is STRICTLY LINEAR (C7); fork = truncate+copy (D5 footnote ¹³)
422[capabilities.session_share]
423enabled = false
424[capabilities.lsp]
425enabled = false
426[capabilities.formatters]
427enabled = false
428[capabilities.server]
429enabled = false
430# app-server parity is out of preset scope — see gaps
431[capabilities.reduction]
432enabled = false
433[capabilities.cache]
434enabled = false
435[capabilities.model_oauth]
436enabled = false
437# ChatGPT-subscription login (cx§9) — module 27 deferred
438"#;
439
440pub const OC_PARITY_TOML: &str = r#"# built-in preset: oc-parity — opencode's default surface, composed.
442schema_version = 1
443
444[core]
445env_context = true
446project_context = true # AGENTS.md + instructions[] concat (oc§6)
447nested_instructions = true # S6/S12 home: nested AGENTS.md auto-attached only for touched-file dirs, oc default (catalog:84; opencode.md:34 "nested-AGENTS.md") — closes an oc-parity gap-ledger row
448instruction_imports = true # S6/S12 home: `instructions[]` config imports, oc default (catalog:85; opencode.md:367) — closes an oc-parity gap-ledger row
449max_tool_output_bytes = 51200 # tool_output.max_bytes default 51200 / 2000 lines (oc§1 Truncate service)
450
451[core.session]
452auto_title = true # S6/S12 home: hidden title+summary agents (deny-all utility agents) on small_model, oc default (catalog:150; opencode.md:169-170,235) — closes an oc-parity gap-ledger row; small_model is "" below so this falls back to the main model per D-9 until a cheap model is configured
453
454[core.tools]
455enabled = ["read_file", "bash", "edit_file", "write_file"] # oc registry core (oc§1; read subsumes ls)
456schema_tier = "full"
457[core.tools.read_file]
458multimodal = true # images/PDFs as attachments (oc§1 read)
459[core.tools.bash]
460timeout_secs = 120 # flags.bashDefaultTimeoutMs default 120000 (oc§1 bash)
461
462[core.skills]
463enabled = true # skill tool + .opencode/skills + remote registries (oc§7)
464
465[core.compaction]
466enabled = true
467summarize = true # compaction{auto,prune,…} (oc§6)
468
469# ---- modules ON ----
470[capabilities.tools_search]
471enabled = true
472# glob + grep via ripgrep (oc§1)
473[capabilities.todos]
474enabled = true
475persist = true
476# todowrite → SQLite todo table (oc§1)
477[capabilities.tools_web]
478enabled = true
479fetch = true
480search = false
481 # webfetch is default; websearch only under the Zen provider / exa flags (oc§1 "webSearchEnabled")
482[capabilities.subagents]
483enabled = true
484max_depth = 2
485background = false
486 # task tool → child session via parentID, resumable task_id (oc§1); background is env-gated experimental → off
487[capabilities.tools_apply_patch]
488enabled = true
489per_model = true
490 # THE C1 precedent: swapped in (edit/write out) for gpt-* models (oc§1 apply_patch; catalog §5 conflict 1)
491[capabilities.plan_mode]
492enabled = false
493# S18 fix (flipped from `true`): opencode's plan_enter/plan_exit TOOLS — exactly what this module is defined by (§2 module 8) — are DENY-BY-DEFAULT at the pin (opencode.md:251), and this preset's own translated rule set below denies them. What oc actually runs by default is the LEGACY generation: the plan agent is a permission-ruleset agent (edit denied) — already expressible as an agent-scoped `permissions.rules` restriction, not the tool-based `plan_mode` module. Enabling `plan_mode` here would contradict oc's own deny-default; off is the honest reading.
494
495[capabilities.permissions]
496enabled = true
497approval = "on_request" # ask-flow with once|always|reject replies (oc§4 "Ask/approve flow")
498sandbox = "danger_full_access" # opencode has NO OS sandbox (catalog D4: sandbox is CC+CX only)
499[capabilities.permissions.rules]
500enabled = true
501# opencode's default policy, TRANSLATED per the C5 decision (last-match-wins → deny→ask→allow
502# first-match). Source policy (oc§4 "Default policy"): {"*": allow} with carve-outs
503# doom_loop: ask, external_directory: ask, question: deny, plan_enter/plan_exit: deny,
504# read {*.env: ask, *.env.*: ask, *.env.example: allow}.
505#
506# S4 fix — this is NOT "the same fixed point" as oc's last-match algebra, and is recorded honestly
507# as THREE NAMED DEVIATIONS rather than claimed as exact parity:
508# 1. `.env.example` → ASK here, not ALLOW. Under first-match deny→ask→allow, a read of
509# `.env.example` matches the ask-rule `read_file(*.env.*)` (glob matches) BEFORE the allow
510# list is ever consulted, so it asks where stock opencode allows. The engine's rule grammar
511# has no specificity/negation to express "ask unless a more-specific allow" — fixing this
512# would require adding that to the grammar (not done here); the deviation is in the SAFE
513# direction (stricter) and is named, not hidden.
514# 2. `doom_loop` is NOT a rule-language pattern at all — it's a repetition TRIGGER (same tool
515# call repeated), not a tool/path match. Routed instead to its actual mechanism: the P4
516# doom-loop breaker (a call-repetition counter + PreToolHook default, §5.2 P4) — no rule
517# entry for it below.
518# 3. `external_directory` is an oc PERMISSION CATEGORY (any tool touching paths outside the
519# worktree), not a tool name — routed instead to its actual permission category: `[core]
520# additional_dirs` (Config.additional_dirs, config.rs:169) governs which extra roots are
521# writable at all; paths outside cwd AND outside `additional_dirs` are simply not reachable,
522# which is a stricter (not equivalent) reading of oc's ask-by-default.
523deny = ["plan_enter", "plan_exit"] # matches module 8's off-by-default above (S18) and oc's own "plan_enter/plan_exit: deny"
524ask = ["read_file(*.env)", "read_file(*.env.*)"] # includes .env.example per deviation 1 above (glob matches before any allow)
525allow = ["*"]
526[capabilities.permissions.protected_paths]
527enabled = false
528# oc does .env protection through rules (above), not a path module
529
530[capabilities.trust]
531enabled = true
532default = "ask"
533# DELIBERATE SAFETY DEVIATION: opencode LACKS a project trust gate (catalog §3 closing) yet loads
534# .opencode/ plugins/tools/commands from the repo. Our resolver treats plugins→trust as a HARD dep
535# (D-10: "config-borne code execution without a trust gate is an injection hole") — so oc-parity
536# ships the gate ON. This only NARROWS behavior (§3.3 monotonic-tightening spirit); recorded, not hidden.
537
538[capabilities.mcp]
539enabled = true
540# local/remote/OAuth servers (oc§7)
541[capabilities.plugins]
542enabled = true
543# .opencode/plugin + npm specs (oc§7); dep on trust satisfied above
544[capabilities.lsp]
545enabled = true
546# 38 auto-spawned servers; diagnostics into edit/write results (oc§7, oc§10)
547[capabilities.formatters]
548enabled = true
549diff_back = true
550# ~27 format-on-write formatters; diff_back honors C10 (oc§7; oc§10; catalog §5 conflict 10)
551[capabilities.checkpoint]
552enabled = true
553# shadow-git snapshots + revert/unrevert (oc§4 "Snapshots"/"Revert")
554[capabilities.session_share]
555enabled = true
556# share manual|auto|disabled, default manual (oc§5, oc§6)
557[capabilities.server]
558enabled = true
559# the client/server split: every frontend is an HTTP client (oc§8)
560[capabilities.model_catalog]
561enabled = true
562small_model = ""
563# models.dev catalog + small_model config key (oc§6, oc§9)
564[capabilities.tui]
565enabled = true
566
567# ---- notable OFFs ----
568[capabilities.tools_question]
569enabled = false
570# question tool is DENY-by-default outside build/plan agents (oc§1, oc§4)
571[capabilities.tools_background]
572enabled = false
573# background subagents are env-gated experimental at the pin (oc§1)
574[capabilities.session_tree]
575enabled = false
576# oc sessions are parent/child linear, no in-place tree (D5; C7)
577[capabilities.memory]
578enabled = false
579[capabilities.hooks]
580enabled = false
581# no config-registered hooks; the plugin API is the interception layer (oc§7)
582[capabilities.deferred_tools]
583enabled = false
584# opencode advertises eagerly (D1: deferred is CC+CX)
585[capabilities.cache]
586enabled = false
587[capabilities.reduction]
588enabled = false
589# oc "prune" is the LOSSY analog (catalog §1 UNIQUE OC note); ours stays off to match, mechanism on per §1.13
590[capabilities.structured_output]
591enabled = false
592[capabilities.model_oauth]
593enabled = false
594# provider /login flows (oc§9) — module 27 deferred
595"#;
596
597pub const TOKEN_SAVER_TOML: &str = r#"# built-in preset: token-saver — the reduction spine over the minimal core.
599schema_version = 1
600extends = "pi-core" # smallest surface = cheapest surface; every knob below overrides it
601
602[core.tools]
603schema_tier = "minimal" # TR-8/T5 schema tiering (config.rs:219-225)
604# C9 (catalog §5 conflict 9): a GLOBAL minimal tier is a footgun for models trained on exact
605# schemas. Per-tool override survives the global — pin any load-bearing tool back:
606[core.tools.edit_file]
607schema_tier = "full" # exact-string edit is the least forgiving schema; keep it verbatim
608
609[core.compaction]
610enabled = true
611reserve_tokens = 24576 # trigger earlier than pi's 16384 — spend the summary, save the window
612keep_recent_tokens = 10000 # aggressive: half of pi's keep budget (recall traded — see caveats)
613summarize = true # SpanSummary side-call (reduce.rs:274-289) → small_model below (D-9)
614
615[capabilities.reduction] # module 23 — ALL genuinely-optional passes on (≡ CLI reduce=true, userconfig.rs:33-38)
616enabled = true
617# NOTE (S7): no `truncation` key here — A7 ToolOutputTruncated (reduce.rs:95-103) is always-on core
618# plumbing (§1.13), never a `[capabilities.reduction]` toggle, in token-saver same as every other preset.
619stale_reads = true # A8 FileReadElided (reduce.rs:104-111)
620diff_reads = true # TR-3 FileReadDiffed (reduce.rs:202-217)
621duplicates = true # TR-2 DuplicateOutput (reduce.rs:228-234)
622supersede = true # TR-6 Superseded (reduce/supersede.rs)
623tool_input_elision = true # TR-10 ToolInputElided (reduce.rs:148-169)
624normalize_output = true # T30 OutputNormalized (reduce/normalize.rs)
625image_redaction = true # A9 ImageRedacted (reduce.rs:112-116) — ON here, off everywhere else
626span_summaries = true # TR-7 (reduce/summarize.rs; D-9)
627handoff = true # reduce/handoff.rs — smallest-faithful-context model handoff
628
629[capabilities.deferred_tools] # module 24 — the FLAGSHIP lever (SPEC.md B6)
630enabled = true
631core = ["read_file", "bash", "edit_file", "write_file"] # builtins stay eager; everything else behind tool_search
632
633[capabilities.cache] # module 25 — the C2 referee
634enabled = true
635plan = "imported_prefix" # CachePlan::ImportedPrefix (config.rs:88-96)
636warnings = true # cache_warnings (config.rs:227-239): every prefix-churning feature must answer to this
637
638[capabilities.model_catalog] # module 26 — D-9 consumer
639enabled = true
640small_model = "anthropic/claude-haiku-4-5" # compaction summaries + span summaries route here, not the main model
641"#;
642
643pub const SUPERCODE_DEFAULT_TOML: &str = r#"# built-in preset: supercode-default — pi-core MINUS {trust, session_tree,
645# session_share, server, plugins}, PLUS the six extra with_builtins() builtins
646# ON, notify available, reduction off (design §4 intro paragraph, S10 fix: NOT
647# "pi-core plus extras" — pi-core itself turns those five modules ON to match
648# pi's kept-list, so this preset is pi-core's core knobs UNCHANGED with a
649# capability delta). This is what `supercode` resolves to with NO config file
650# at all — "today's defaults, named and warned" rather than implicit
651# (design:958-960).
652schema_version = 1
653extends = "pi-core"
654
655# core knobs identical to pi-core (§4.1's [core]/[core.retry]/[core.tools]/
656# [core.skills]/[core.compaction]/[core.steering] blocks) — unchanged, nothing
657# to override here; inherited verbatim via `extends`.
658
659# ---- the S10 delta over pi-core: OFF (today's CLI has none of these — "—"
660# across the board in §2's Today column) ----
661[capabilities.trust]
662enabled = false
663[capabilities.session_tree]
664enabled = false
665[capabilities.session_share]
666enabled = false
667[capabilities.server]
668enabled = false
669[capabilities.plugins]
670enabled = false
671
672# ---- the six extra with_builtins() builtins (tools/mod.rs:179-192), ON ----
673# list_dir/glob/search -> tools_search; apply_patch -> tools_apply_patch;
674# persistent_shell -> tools_persistent_shell; update_plan -> todos.
675[capabilities.tools_search]
676enabled = true
677[capabilities.tools_apply_patch]
678enabled = true
679# NOT per_model: with_builtins() registers every tool struct unconditionally
680# with no per-model filtering at all (§4.6 "faithful to today's actual
681# unfiltered default stack") — this is what makes C1's warning fire here,
682# honestly, rather than suppressing it with a `per_model` bit today's CLI
683# doesn't actually have.
684[capabilities.tools_persistent_shell]
685enabled = true
686[capabilities.todos]
687enabled = true
688
689# notify available (today's CLI already ships full notify support end to end
690# — userconfig.rs:61-71 — unlike pi-core, which doesn't mention it at all).
691[capabilities.notify]
692enabled = true
693
694# reduction off (policies only; A7 truncation + rehydrate stay always-on core
695# regardless, §1.13) — already off by inheritance from pi-core; restated for
696# clarity per the design intro's explicit "reduction off" callout.
697[capabilities.reduction]
698enabled = false
699
700# permissions stays off too (approval = never, sandbox = danger_full_access)
701# — identical to pi-core's own values (config.rs:36-38, tools/mod.rs:40-42);
702# restated verbatim so the C3 mandatory warning fires here by the same
703# mechanism as pi-core's, naming today's actual default stack rather than
704# leaving it implicit (design:971-974).
705[capabilities.permissions]
706enabled = false
707approval = "never"
708sandbox = "danger_full_access"
709"#;
710
711pub const RESERVED_PRESET_NAMES: &[&str] = &[
713 "pi-core",
714 "cc-parity",
715 "cx-parity",
716 "oc-parity",
717 "token-saver",
718 "supercode-default",
719];
720
721pub fn lookup(name: &str) -> Option<&'static str> {
726 match name {
727 "pi-core" => Some(PI_CORE_TOML),
728 "cc-parity" => Some(CC_PARITY_TOML),
729 "cx-parity" => Some(CX_PARITY_TOML),
730 "oc-parity" => Some(OC_PARITY_TOML),
731 "token-saver" => Some(TOKEN_SAVER_TOML),
732 "supercode-default" => Some(SUPERCODE_DEFAULT_TOML),
733 _ => None,
734 }
735}
736
737#[cfg(test)]
738mod tests {
739 use super::*;
740 use crate::configfile::HarnessConfig;
741
742 #[test]
748 fn every_reserved_preset_parses() {
749 for name in RESERVED_PRESET_NAMES {
750 let toml = lookup(name).unwrap_or_else(|| panic!("no TOML for preset `{name}`"));
751 HarnessConfig::from_toml_str(toml)
752 .unwrap_or_else(|e| panic!("preset `{name}` failed to parse: {e}"));
753 }
754 }
755
756 #[test]
759 fn lookup_returns_none_for_non_preset_names() {
760 assert!(lookup("not-a-real-preset").is_none());
761 assert!(lookup("./some/path.toml").is_none());
762 assert!(lookup("").is_none());
763 }
764
765 #[test]
770 fn token_saver_and_supercode_default_extend_pi_core() {
771 let ts = HarnessConfig::from_toml_str(TOKEN_SAVER_TOML).unwrap();
772 assert_eq!(ts.extends.as_deref(), Some("pi-core"));
773 let sd = HarnessConfig::from_toml_str(SUPERCODE_DEFAULT_TOML).unwrap();
774 assert_eq!(sd.extends.as_deref(), Some("pi-core"));
775 let pc = HarnessConfig::from_toml_str(PI_CORE_TOML).unwrap();
776 assert_eq!(pc.extends, None);
777 }
778}