Skip to content

feat(codex): pass per-agent config overrides via codexConfig#278

Open
vprudnikoff wants to merge 3 commits into
awslabs:mainfrom
vprudnikoff:feat/codex-agent-config
Open

feat(codex): pass per-agent config overrides via codexConfig#278
vprudnikoff wants to merge 3 commits into
awslabs:mainfrom
vprudnikoff:feat/codex-agent-config

Conversation

@vprudnikoff

Copy link
Copy Markdown
Contributor

Summary

There is no way to set a per-agent Codex setting — reasoning effort, service tier, fast mode, model, and so on — from an agent profile. Today you must either edit the global ~/.codex/config.toml (which affects every agent and the user's own Codex) or maintain named profile files referenced by codexProfile. This PR adds an optional codexConfig map to the agent profile whose entries are passed straight to Codex as -c key=value overrides at launch — the same mechanism CAO already uses for developer_instructions and mcpServers.

What changed

  • models/agent_profile — new optional codexConfig: Dict[str, Any].
  • providers/codex_toml_scalar serializes each value to a TOML literal (strings quoted; bools/ints/floats bare; bool is checked before int, since bool subclasses int), and _build_codex_command emits -c <key>=<value> for every entry, in both the default --yolo path and the --profile <codexProfile> path. Keys may be dotted config paths (e.g. features.fast_mode); overrides are emitted last so they win on key conflicts.
  • docscodex-cli.md (new "Inline Codex Config Overrides" section) and agent-profile.md (field reference).
  • tests_toml_scalar serialization, codexConfig command building (yolo path, --profile composition, model/MCP composition), and frontmatter parsing.

Example

---
name: backend-developer
provider: codex
role: developer
codexConfig:
  model_reasoning_effort: "xhigh"
  service_tier: "fast"
  features.fast_mode: true
---

launches:

codex --yolo … -c model_reasoning_effort="xhigh" -c service_tier="fast" -c features.fast_mode=true

so the effort / fast-mode settings apply to that agent only — no global ~/.codex/config.toml edit and no named profile file.

Why

This mirrors the existing per-session, no-global-mutation philosophy of the MCP overrides. It composes with codexProfile (which governs sandbox/approvals) rather than replacing it, and unblocks per-agent reasoning effort without forcing the agent into a restricted-tools --profile launch.

Testing

  • pytest — full suite green.
  • black / isort — clean.
  • Verified live on codex-cli 0.137: codex exec --strict-config -c model_reasoning_effort=xhigh -c service_tier=fast -c features.fast_mode=true starts clean and the session banner reports reasoning effort: xhigh (--strict-config proves every key is recognized by this Codex version).

Add an optional `codexConfig` map to the agent profile. The codex provider
serializes each entry to a TOML scalar and passes it as `-c key=value` at
launch, in both the default --yolo path and the --profile <codexProfile>
path. Keys may be dotted config paths (e.g. features.fast_mode); strings are
quoted, bools/numbers emitted bare.

This lets a profile set per-agent Codex knobs — reasoning effort, service
tier, fast mode, etc. — without editing the global ~/.codex/config.toml or
maintaining named profile files, mirroring how the mcpServers overrides
already work.
@vprudnikoff vprudnikoff force-pushed the feat/codex-agent-config branch from 294f3dc to 0b5e33c Compare June 5, 2026 12:44
@haofeif haofeif requested a review from Copilot June 6, 2026 10:02
@haofeif haofeif added the enhancement New feature or request label Jun 6, 2026
@codecov-commenter

codecov-commenter commented Jun 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@1224cd0). Learn more about missing BASE report.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #278   +/-   ##
=======================================
  Coverage        ?   92.16%           
=======================================
  Files           ?       70           
  Lines           ?     7119           
  Branches        ?        0           
=======================================
  Hits            ?     6561           
  Misses          ?      558           
  Partials        ?        0           
Flag Coverage Δ
unittests 92.16% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds per-agent Codex configuration overrides via a new optional codexConfig map on agent profiles, allowing CAO to launch Codex with -c key=value flags so settings (e.g., reasoning effort, service tier, fast mode) can be scoped to a single agent/session without mutating the user’s global ~/.codex/config.toml or requiring named Codex profiles.

Changes:

  • Introduces codexConfig: Optional[Dict[str, Any]] on AgentProfile and parses it from profile frontmatter.
  • Updates the Codex provider to serialize codexConfig values and append -c overrides to the launch command (both --yolo and --profile paths).
  • Adds documentation and unit tests covering parsing, serialization, and command composition.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
test/utils/test_agent_profiles.py Adds tests ensuring codexConfig frontmatter parses into AgentProfile.
test/providers/test_codex_provider_unit.py Adds unit tests for _toml_scalar and for codexConfig -c emission/composition.
src/cli_agent_orchestrator/providers/codex.py Adds _toml_scalar and appends codexConfig overrides to the Codex command.
src/cli_agent_orchestrator/models/agent_profile.py Adds the new codexConfig field to the profile model.
docs/codex-cli.md Documents inline Codex config overrides via codexConfig.
docs/agent-profile.md Documents codexProfile and codexConfig in the profile field reference.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +121 to +135
def _toml_scalar(value: Any) -> str:
"""Serialize a Python scalar to a TOML literal for a ``-c key=<value>`` override.

Strings become quoted TOML basic strings (backslash/quote/newline escaped so
tmux ``send_keys`` keeps the launch command on one line); bools become
``true``/``false``; ints and floats are emitted bare. ``bool`` is checked
before ``int`` because ``bool`` is a subclass of ``int`` in Python, so the
order here is load-bearing — a flipped order would render ``True`` as ``1``.
"""
if isinstance(value, bool):
return "true" if value else "false"
if isinstance(value, (int, float)):
return str(value)
escaped = str(value).replace("\\", "\\\\").replace('"', '\\"').replace("\n", "\\n")
return f'"{escaped}"'

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partially agreed. The valuable part is real: silently accepting non-scalars (dict/list/None) would serialize a Python repr and fail confusingly in Codex, so _toml_scalar now raises TypeError for non-scalar values so a misconfigured profile fails fast. I also added \t/\r escaping for completeness.

That said, in practice codexConfig values are author-controlled config scalars (effort/service-tier strings, bool flags) rather than untrusted input, so the control-character path is more defensive than a real vector here.

_toml_scalar now raises TypeError for non-scalar values (dict/list/None) so a
misconfigured profile fails with a clear message instead of silently
serializing a Python repr that Codex cannot parse. Tab and carriage return are
escaped too, keeping the emitted TOML basic string valid.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Comment on lines +292 to +294
if profile.codexConfig:
for key, value in profile.codexConfig.items():
command_parts.extend(["-c", f"{key}={_toml_scalar(value)}"])

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 9f66945. Two parts here:

  • Values were already escaped and validated by _toml_scalar (backslash/quote/\t/\r/\n escaped, non-scalars rejected with TypeError), and the whole command goes through shlex.join, so a value can't split the tmux payload or inject.
  • Keys were the gap — they were interpolated raw. Added _toml_override(), which allowlists keys to a safe dotted-path syntax ([A-Za-z0-9_.-]) and rejects spaces / = / quotes / control chars with a clear ValueError naming the key, so a misconfigured profile fails fast instead of producing a malformed -c override. Value-serialization errors are also re-raised with the offending key for context.

Comment on lines +50 to +53
# Codex-only. Inline Codex config overrides passed as `-c key=value` at
# launch (e.g. {"model_reasoning_effort": "xhigh", "service_tier": "fast",
# "features.fast_mode": true}). Keys may be dotted paths into Codex's
# config.toml schema; values are serialized to TOML scalars (strings are

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 9f66945 — the example is Python (a dict literal), so it now uses True instead of the YAML-style true.

codexConfig keys were interpolated raw into the `-c key=value` override.
Add _toml_override(), which rejects keys outside a safe dotted-path syntax
([A-Za-z0-9_.-]) so spaces, `=`, quotes, or control characters fail fast
instead of producing a malformed override or a multi-line tmux payload, and
re-raises value-serialization errors with the offending key. Values were
already escaped/validated by _toml_scalar.

Also fix the AgentProfile.codexConfig doc example to use Python `True`
instead of YAML `true`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants