Commit Graph

5754 Commits

Author SHA1 Message Date
Felipe Coury
2ddac81e43 feat(tui): make reasoning shortcuts configurable 2026-04-23 10:35:35 -03:00
Felipe Coury
4b478b593f fix(tui): resolve keymap rebase fallout 2026-04-23 10:35:35 -03:00
Felipe Coury
0be4b70d15 fix(tui): return keymap edits to main picker
Return successful shortcut edits and removals to the main `keymap`
picker with the edited action selected. This keeps the list flow stable
after save instead of reopening the focused binding menu.
2026-04-23 10:35:35 -03:00
Felipe Coury
9baecb04e5 refactor(tui): move keymap details to footer
Keep shortcut rows focused on section, status, action, and binding. Move selected action descriptions into the footer note and add a compact legend for custom and unbound shortcut markers.
2026-04-23 10:35:35 -03:00
Felipe Coury
7aac2616b4 refactor(tui): simplify keymap row status indicators
Show custom and unbound keymap states with compact indicators in the shortcut picker. This removes the default-state prefix so ordinary shortcuts scan by section while changes and missing bindings remain visible.
2026-04-23 10:35:35 -03:00
Felipe Coury
fb459feb5a feat(tui): add common keymap picker category
Add a curated Common tab to `/keymap` for the shortcuts most
likely to be rebound, while keeping All as the initial view.

Demote onboarding from the top-level picker categories without
removing its keymap actions from All or search.
2026-04-23 10:35:35 -03:00
Felipe Coury
b9a5f3c773 feat(tui): support alternate keymap bindings
Adds explicit replace-all, replace-one, and add-alternate flows to the `/keymap` action menu so multi-binding actions can be edited without losing the full shortcut set.

Persists one binding as a scalar and multiple bindings as an array under `tui.keymap`, matching the existing config shape while preserving default-derived alternates.
2026-04-23 10:35:35 -03:00
Felipe Coury
b7b6119c29 feat(tui): overhaul keymap picker
Move `/keymap` to a tabbed, single-line picker inspired by the
`/plugins` command so shortcut browsing is denser and less repetitive.
The picker now groups shortcuts by source and context, with compact row
status markers for default, custom, and unbound actions.

Move selected-action details into the edit menu header and remove the
old side panel so the picker stays focused on scanning and searching.
2026-04-23 10:35:35 -03:00
Felipe Coury
f4fd3a6c7f fix(tui): repair list selection test after rebase
Pass the default list keymap into the plugin-toggle selection test
so the rebased test code matches the runtime-keymap constructor.
2026-04-23 10:35:35 -03:00
Felipe Coury
88923c8419 fix(tui): enable slash command shortcut in vim normal mode
Allow an empty Vim normal-mode composer to treat / as the command
shortcut by seeding the slash prefix and returning to insert mode.

This keeps popup behavior consistent with the footer hint while
preserving normal-mode editing for non-empty drafts.
2026-04-23 10:35:34 -03:00
Felipe Coury
699989af76 fix(tui): return to keymap menu after capture
Keep the keymap action menu on the bottom-pane stack while capturing or clearing a shortcut so users return to the previous menu instead of the composer.

Refresh the active action menu after keymap updates so the displayed binding and clear state match the saved config.
2026-04-23 10:35:34 -03:00
Felipe Coury
ca773db697 fix(tui): resolve keymap rebase fallout
Remove the duplicate slash-command dispatcher left by the rebase and wire the vim/keymap commands into the extracted dispatcher.

Update affected keymap test callsites and footer snapshot so the rebased TUI builds against the current list keymap API.
2026-04-23 10:35:34 -03:00
Felipe Coury
d6a09dddcd test(tui): update keymap copy snapshots
Use a non-copy chord in the queue shadowing regression test so it still reaches the intended composer conflict after `global.copy` claimed `ctrl-o`.

Accept the keymap picker snapshots now that Copy is part of the configurable global keymap actions.
2026-04-23 10:35:34 -03:00
Felipe Coury
2e15fb0666 fix(tui): annotate opaque tui literals
Add missing argument comments around positional boolean and numeric
literals that the TUI keymap work now reaches.

This keeps callsites self-documenting and satisfies the
argument-comment lint in CI.
2026-04-23 10:35:34 -03:00
Felipe Coury
2228d45325 test(tui): initialize copy binding in helper
Set the manual `ChatWidget` test helper to use the default copy
shortcut binding so helper-built widgets match normal construction.

This fixes the CI compile failure from the new remappable copy field.
2026-04-23 10:35:34 -03:00
Felipe Coury
62922b712b feat(tui): expose copy shortcut in keymap config
Add `global.copy` to the TUI keymap schema, runtime keymap,
and interactive remap action list so the Copy shortcut can be
remapped or unbound like other global shortcuts.

Move the existing `ctrl-o` default into a new `v3` preset while
preserving older presets, and update docs plus coverage for the
remapped copy behavior.
2026-04-23 10:35:34 -03:00
Felipe Coury
7ff22c969e fix(tui): annotate keymap capture dimensions
Adds argument-name comments to the keymap capture snapshot helper so the argument-comment lint accepts the test literals.
2026-04-23 10:35:34 -03:00
Felipe Coury
61aa440047 feat(tui): add guided keymap remapping
Adds a `/keymap` flow for discovering actions, replacing root-level shortcut bindings, and removing custom bindings when users want to return to defaults.

The picker uses responsive layouts so wide terminals show richer action details while narrow terminals keep the guidance compact.
2026-04-23 10:35:34 -03:00
Felipe Coury
e5c668871d docs(tui): format keymap planning notes
Apply the pinned Prettier layout to the keymap handoff and rollout notes
so the root `pnpm run format` check accepts the documentation.
2026-04-23 10:35:34 -03:00
Felipe Coury
231ffafdbe fix(tui): satisfy keymap ci checks
Regenerate the config schema after adding keymap rustdoc so the
fixture matches the generated `config.toml` schema.

Add the required argument comments for opaque literals in the vim and
cursor tests so the branch passes the argument-comment lint.
2026-04-23 10:35:34 -03:00
Felipe Coury
4f8d37cbf8 fix(tui): route empty vim insert escape to composer
Let Vim Insert handle plain `Esc` before the chat edit-previous and backtrack shortcut layers so an empty composer can still return to Normal mode.

Keep existing empty-composer backtrack behavior after Vim has reached Normal mode, and cover both composer-local and app-level routing.
2026-04-23 10:35:34 -03:00
Felipe Coury
ebc6522276 feat(tui): show bar cursor in vim insert mode
Thread cursor style through the custom terminal and render tree so the chat composer can request an insert-like cursor while Vim mode is in Insert.

Restore the terminal default cursor style on Normal/non-Vim frames and TUI teardown, and cover the escape sequences plus composer mode transitions.
2026-04-23 10:35:33 -03:00
Felipe Coury
704225a300 docs(tui): config and keybinding docs 2026-04-23 10:35:33 -03:00
Felipe Coury
942fb20e00 fix(tui): resolve keymap rebase regressions
Preserve mainline submit/queue semantics, update approval tests for
`SubmitThreadOp`, and keep permission-deny shortcuts from shadowing
`Esc` cancellation.

Add a `v2` keymap preset so `latest` can restore `alt-d`
`delete_forward_word` without mutating frozen `v1` defaults, and refresh
the generated config schema and docs.
2026-04-23 10:35:33 -03:00
Felipe Coury
006fb5ae5b docs(tui): document shifted-letter keybinding compatibility
Clarify that shifted letter bindings are matched compatibly when
terminals emit uppercase letters without an explicit SHIFT modifier.

Update config guidance, the default keymap template, and the action
matrix with examples for shift-i, shift-a, and shift-o mapping to I, A,
and O.
2026-04-23 10:35:17 -03:00
Felipe Coury
eb2275a64a feat(tui): support shift-letter vim bindings across terminal variants
Make shifted letter bindings robust when terminals report uppercase
letters without explicit SHIFT modifiers.

This updates keybinding matching so bindings like `shift-i`, `shift-a`,
and `shift-o` also match `I`, `A`, and `O` event forms, and adds
regression tests covering Vim normal-mode actions for line-start insert,
line-end append, and open-line-above with shift-only bindings.
2026-04-23 10:35:17 -03:00
Felipe Coury
f0b70188a7 fix(tui): align Vim esc cursor behavior with normal mode
Update `tui/src/bottom_pane/textarea.rs` so `Esc` in Vim insert mode
moves the cursor to the previous atomic boundary before switching to
normal mode. This matches Vim behavior at end-of-line and avoids
leaving the cursor on a virtual trailing cell.

Add regression coverage in `tui/src/bottom_pane/textarea.rs` and
`tui/src/bottom_pane/chat_composer.rs`, plus a
`vim_escape_cursor_position` snapshot to lock rendered cursor
placement after `Esc`.
2026-04-23 10:35:17 -03:00
Felipe Coury
cb3e5baa73 feat(tui): add configurable Vim keymap and startup default
Expose Vim bindings through the TUI keymap config and add a startup
toggle so Vim mode can be enabled by default.

Wire runtime keymap handling for Vim actions, add
`tui.vim_mode_default` to config parsing/runtime config, apply the
default in ChatWidget startup paths, and update docs/default
keymap/schema accordingly.
2026-04-23 10:35:17 -03:00
Felipe Coury
d49e77a7c2 fix(core): reject unknown fields in TuiKeymap config at parse time
Actions placed directly under [tui.keymap] instead of a context
sub-table (e.g. [tui.keymap.global]) were silently ignored because
deny_unknown_fields was only on the schemars attribute, not serde.
2026-04-23 10:35:17 -03:00
Josh McKinney
86374facf2 feat(tui): surface keymap customization hints
Add a dedicated footer line pointing to `[tui.keymap]` in
`~/.codex/config.toml` so users can find where to rebind shortcuts.

Refresh tooltips and snapshots to mention the config entry and
the keymap template URL.
2026-04-23 10:35:17 -03:00
Josh McKinney
1655c2e37f feat(tui): wire runtime keymap into event handling 2026-04-23 10:35:16 -03:00
Josh McKinney
ef07eb7efd test(tui): add runtime keymap resolver characterization suite
Introduce the TUI runtime keymap resolver and keybinding matching helpers
with a dedicated unit-test suite. This commit is additive only: it adds
resolution logic, conflict validation, parser coverage, and documented macros
without wiring input handlers to the new runtime map yet.
2026-04-23 10:34:37 -03:00
Josh McKinney
289ccad03c feat(core): add keymap config schema and types
Introduce keymap configuration types and schema support in core without
wiring runtime key handling yet. This keeps behavior unchanged while
adding the configuration surface needed by later commits.
2026-04-23 10:34:37 -03:00
Josh McKinney
1202598434 docs(keymap): establish long-term keymap documentation
Publish keymap system documentation first so the implementation stack can
be reviewed against explicit behavior and invariants.

This commit adds the keymap system guide, action matrix, default keymap
template, and config/example documentation updates, plus a rollout plan
used to stage the additive refactor and validation work.
2026-04-23 10:32:12 -03:00
jif-oai
d3b044938d Reject agents.max_threads with multi_agent_v2 (#19129)
## Why

`multi_agent_v2` uses the v2 agent lifecycle, so accepting the legacy
`agents.max_threads` limit alongside it creates conflicting
configuration semantics. Config load should fail early with a clear
error instead of allowing both knobs to be set.

## What Changed

- During config load, detect when the effective `multi_agent_v2` feature
is enabled and `agents.max_threads` is explicitly set.
- Return an `InvalidInput` error: `agents.max_threads cannot be set when
multi_agent_v2 is enabled`.

## Verification

- `cargo test -p codex-core multi_agent_v2_rejects_agents_max_threads`
passed locally with a temporary focused test for this behavior.
- `cargo test -p codex-core` was also run; the new focused path passed,
but the crate suite has unrelated pre-existing failures in managed
config/proxy/request-permissions tests.
2026-04-23 13:31:54 +02:00
Won Park
17ae906048 Fix auto-review config compatibility across protocol and SDK (#19113)
## Why

This keeps the partial Guardian subagent -> Auto-review rename
forward-compatible across mixed Codex installations. Newer binaries need
to understand the new `auto_review` spelling, but they cannot write it
to shared `~/.codex/config.toml` yet because older CLI/app-server
bundles only know `user` and `guardian_subagent` and can fail during
config load before recovering.

The Python SDK had the opposite compatibility gap: app-server responses
can contain `approvalsReviewer: "auto_review"`, but the checked-in
generated SDK enum did not accept that value.

## What Changed

- Keep `ApprovalsReviewer::AutoReview` readable from both
`guardian_subagent` and `auto_review`, while serializing it as
`guardian_subagent` in both protocol crates.
- Update TUI Auto-review persistence tests so enabling Auto-review
writes `approvals_reviewer = "guardian_subagent"` while UI copy still
says Auto-review.
- Map managed/cloud `feature_requirements.auto_review` to the existing
`Feature::GuardianApproval` gate without adding a broad local
`[features].auto_review` key or changing config writes.
- Add `auto_review` to the Python SDK `ApprovalsReviewer` enum and cover
`ThreadResumeResponse` validation.

## Testing

- `cargo test -p codex-protocol approvals_reviewer`
- `cargo test -p codex-app-server-protocol approvals_reviewer`
- `cargo test -p codex-tui
update_feature_flags_enabling_guardian_selects_auto_review`
- `cargo test -p codex-tui
update_feature_flags_enabling_guardian_in_profile_sets_profile_auto_review_policy`
- `cargo test -p codex-core
feature_requirements_auto_review_disables_guardian_approval`
- `pytest
sdk/python/tests/test_client_rpc_methods.py::test_thread_resume_response_accepts_auto_review_reviewer`
- `git diff --check`
2026-04-23 03:12:56 -07:00
Abhinav
305825abd9 Support MCP tools in hooks (#18385)
## Summary

Lifecycle hooks currently treat `PreToolUse`, `PostToolUse`, and
`PermissionRequest` as Bash-only flows
- hook schema constrains `tool_name` to `Bash`
- hook input assumes a command-shaped `tool_input`
- core hook dispatch path passes only shell command strings

That means hooks cannot target MCP tools even though MCP tool names are
model-visible and stable

This change generalizes those hook paths so they can match and receive
payloads for MCP tools while preserving the existing Bash behavior.

## Reviewer Notes

I think these are the key files
- `codex-rs/core/src/tools/handlers/mcp.rs`
- `codex-rs/core/src/mcp_tool_call.rs`

Otherwise the changes across apply_patch, shell, and unified_exec are
mainly to rewire everything to be `tool_input` based instead of just
`command` so that it'll make sense for MCP tools.

## Changes

- Allow `PreToolUse`, `PostToolUse`, and `PermissionRequest` hook inputs
to carry arbitrary `tool_name` and `tool_input` values instead of
hard-coding `Bash` and command-only payloads.
- Add MCP hook payload support through `McpHandler`, using the
model-visible tool name from `ToolInvocation` and the raw MCP arguments
as `tool_input`.
- Include MCP tool responses in `PostToolUse` by serializing
`McpToolOutput` into the hook response payload.
- Run `PermissionRequest` hooks for MCP approval requests after
remembered approval checks and before falling back to user-facing MCP
elicitation.
- Preserve exact matching for literal hook matchers like `Bash` and
`mcp__memory__create_entities`, while keeping regex matcher support for
patterns like `mcp__memory__.*` and `mcp__.*__write.*`.

---------

Co-authored-by: Andrei Eternal <eternal@openai.com>
Co-authored-by: Codex <noreply@openai.com>
2026-04-23 07:33:57 +00:00
Michael Bolin
8bc667b07b app-server: include filesystem entries in permission requests (#19086)
## Why

`item/permissions/requestApproval` sends a requested permission profile
to app-server clients. The core profile already stores filesystem
permissions as `entries`, but the v2 compatibility conversion used the
legacy `read`/`write` projection whenever possible and left `entries`
unset.

That made the request ambiguous for clients that consume the canonical
v2 shape: `permissions.fileSystem.entries` was missing even though
filesystem access was being requested. A client that rendered or echoed
grants from `entries` could treat the request as having no filesystem
permission entries, then return an empty or incomplete grant. The
app-server intersects responses with the original request, so omitted
filesystem permissions are denied.

## What Changed

- Populate `AdditionalFileSystemPermissions.entries` when converting
legacy read/write roots for request permission payloads, while
preserving `read` and `write` for compatibility.
- Mark `read` and `write` as transitional schema fields in the generated
app-server schema.
- Add regression coverage for the v2 conversion, the app-server
`item/permissions/requestApproval` round trip, and TUI app-server
approval conversion expectations.
- Refresh generated JSON and TypeScript schema fixtures.

## Verification

- `just fmt`
- `cargo test -p codex-app-server-protocol`
- `cargo test -p codex-app-server request_permissions_round_trip`
- `cargo test -p codex-tui
converts_request_permissions_into_granted_permissions`
- `cargo test -p codex-tui
resolves_permissions_and_user_input_through_app_server_request_id`
2026-04-23 00:21:59 -07:00
Shijie Rao
993e3f407e Persist target default reasoning on model upgrade (#19085)
## Why

When the TUI upgrade flow moves a user to a newer model, the accepted
migration should also persist the target model's default reasoning
effort. That keeps the upgraded model and reasoning setting aligned
instead of carrying forward a stale previously saved effort from the old
model.

## What changed

- The accepted model migration path now updates in-memory config, TUI
state, and persisted model selection with the target preset's
`default_reasoning_effort`.
- The upgrade destructuring keeps `reasoning_effort_mapping` explicitly
unused because mappings are no longer consulted on accepted migrations.
- Added a catalog test that starts with a pre-existing saved reasoning
effort and verifies the accepted upgrade overwrites it with the target
model default and emits the expected persistence events.
- Rebasing onto current `main` also updates a TUI thread-session test
helper for the latest `permission_profile` field and
`ApprovalsReviewer::AutoReview` rename so CI compiles on the new base.

## Verification

- `cargo test -p codex-tui model_catalog`
- `cargo test -p codex-tui
permission_settings_sync_updates_active_snapshot_without_rewriting_side_thread`
2026-04-22 23:36:15 -07:00
Gav Verma
2ef2d675d6 Clarify cloud requirements error messages (#19078)
## Why
The current cloud-requirements failures say `workspace-managed config`,
which is ambiguous and can read like it refers to local managed config
such as `managed_config.toml`.

This code path only applies to cloud requirements, so the user-facing
message should name that source directly.

## What changed
- Updated the load failure in
[`codex-rs/cloud-requirements/src/lib.rs`](46e704d1f9/codex-rs/cloud-requirements/src/lib.rs)
to say `failed to load cloud requirements (workspace-managed policies)`.
- Updated the parse failure in the same file to use the same `cloud
requirements (workspace-managed policies)` terminology.
- Kept `workspace-managed` hyphenated because it is used as a compound
modifier.
- Updated the matching assertion in
[`codex-rs/app-server/src/codex_message_processor.rs`](46e704d1f9/codex-rs/app-server/src/codex_message_processor.rs).
- Reused `CLOUD_REQUIREMENTS_LOAD_FAILED_MESSAGE` in the
`codex-cloud-requirements` test where the test is asserting that
crate-local contract directly.

## Testing
`cargo test -p codex-cloud-requirements`
2026-04-22 23:07:08 -07:00
xl-openai
951be1a8a1 feat: Warn and continue on unknown feature requirements (#19038)
Requirements feature flags now fail open like config feature flags, but
with a startup warning.

<img width="443" height="68" alt="image"
src="https://github.com/user-attachments/assets/76767fa7-8ce8-4fc7-8a09-902fcdda6298"
/>
2026-04-22 22:50:44 -07:00
xl-openai
fb6308cf64 Use remote plugin IDs for detail reads and enlarge list pages (#19079)
1. For remote plugin use plugin id (plugin name) directly for read
plugin details;
2. Request up to 200 remote plugins per directory list page.
2026-04-22 22:50:20 -07:00
Leo Shimonaka
7730fb3ab8 Add computer_use feature requirement key (#19071)
## Summary
- add the `computer_use` requirements-only feature key
- include it in generated config schema output
- cover the new key in feature metadata tests

## Testing
- `cargo test -p codex-features`
- `just write-config-schema`
- `just fmt`
- `just fix -p codex-features`

cc @xl-openai

---------

Co-authored-by: Dylan Hurd <dylan.hurd@openai.com>
2026-04-22 22:49:26 -07:00
Eric Traut
08b5e96678 TUI: preserve permission state after side conversations (#18924)
Addresses #18854

## Why

The `/permissions` selector updates the active TUI session state, but
the cached session snapshot used when replaying a thread could still
contain the old approval or sandbox settings. After opening and leaving
`/side`, the main thread replay could restore those stale settings into
the `ChatWidget`, so the UI and the next submitted turn could fall back
to the old permission mode.

## What

- Sync the active thread's cached `ThreadSessionState` whenever approval
policy, sandbox policy, or approval reviewer changes.

## Verification

Confirmed bug prior to fix and correct behavior after fix.
2026-04-22 22:40:35 -07:00
Abhinav
23afa173f4 Mark codex_hooks stable (#19012)
# Why

Hooks are ready to graduate to GA in the next release!

# What

- Moves `Feature::CodexHooks` into the stable feature group.
- Marks the `codex_hooks` feature spec as `Stage::Stable` and
default-enabled.
2026-04-23 05:34:05 +00:00
Michael Bolin
9d824cf4b4 app-server: accept command permission profiles (#18283)
## Why

`command/exec` is another app-server entry point that can run under
caller-provided permissions. It needs to accept `PermissionProfile`
directly so command execution is not left behind on `SandboxPolicy`
while thread APIs move forward.

Command-level profiles also need to preserve the semantics clients
expect from profile-relative paths. `:cwd` and cwd-relative deny globs
should be anchored to the resolved command cwd for a command-specific
profile, while configured deny-read restrictions such as `**/*.env =
none` still need to be enforced because they can come from config or
requirements rather than the command override itself.

## What Changed

This adds `permissionProfile` to `CommandExecParams`, rejects requests
that combine it with `sandboxPolicy`, and converts accepted profiles
into the runtime filesystem/network permissions used for command
execution.

When a command supplies a profile, the app-server resolves that profile
against the command cwd instead of the thread/server cwd. It also
preserves configured deny-read entries and `globScanMaxDepth` on the
effective filesystem policy so one-off command overrides cannot drop
those read protections. The PR also updates app-server docs/schema
fixtures and adds command-exec coverage for accepted, rejected,
cwd-scoped, and deny-read-preserving profile paths.

## Verification

- `cargo test -p codex-app-server
command_exec_permission_profile_cwd_uses_command_cwd`
- `cargo test -p codex-app-server
command_profile_preserves_configured_deny_read_restrictions`
- `cargo test -p codex-app-server
command_exec_accepts_permission_profile`
- `cargo test -p codex-app-server
command_exec_rejects_sandbox_policy_with_permission_profile`
- `just fix -p codex-app-server`

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/18283).
* #18288
* #18287
* #18286
* #18285
* #18284
* __->__ #18283
2026-04-22 22:33:16 -07:00
Eric Traut
bbff4ee61a Add safety check notification and error handling (#19055)
Adds a new app-server notification that fires when a user account has
been flagged for potential safety reasons.
2026-04-22 22:24:12 -07:00
Shijie Rao
02170996e6 Default Fast service tier for eligible ChatGPT plans (#19053)
## Why

Enterprise and business-like ChatGPT plans should get Codex's Fast
service tier by default when the user or caller has not made an explicit
service-tier choice. At the same time, callers need a durable way to
choose standard routing without adding a new persisted `standard`
service tier value. This keeps existing config compatibility while
letting core own the managed default policy.

## What changed

- Resolve the effective service tier in core at session creation:
explicit `fast` or `flex` wins, explicit null/clear or
`[notice].fast_default_opt_out = true` resolves to standard routing, and
otherwise eligible ChatGPT plans resolve to Fast when FastMode is
enabled.
- Add `[notice].fast_default_opt_out` as the persisted opt-out marker
for managed Fast defaults.
- Treat app-server/TUI `service_tier: null` as an explicit
standard/clear choice by preserving that intent through config loading.
- Update TUI rendering to use core's effective service tier for startup
and status surfaces while still keeping `config.service_tier` as the
explicit configured choice.
- Update `/fast off` to clear `service_tier`, persist the opt-out
marker, and send explicit standard for subsequent turns.

## Verification

- Added unit coverage for config override/notice handling, service-tier
resolution, runtime null clearing, and `/fast off` turn propagation.
- `cargo build -p codex-cli`

Full test suite was not run locally per author request.
2026-04-22 21:54:44 -07:00
Michael Bolin
082fc4f632 protocol: report session permission profiles (#18282)
## Why

Clients that observe `SessionConfigured` need the same canonical
permission view that app-server thread responses provide. Reporting the
profile in protocol events lets clients keep their local state
synchronized without reinterpreting legacy sandbox fields.

## What changed

This adds `permission_profile` to `SessionConfigured` and propagates it
through core, exec JSON output, MCP server messages, and TUI
history/widget handling.

## Verification

- `cargo test -p codex-tui permissions -- --nocapture`
- `cargo test -p codex-core --test all permissions_messages --
--nocapture`















































---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/18282).
* #18288
* #18287
* #18286
* #18285
* #18284
* #18283
* __->__ #18282
2026-04-22 21:29:32 -07:00
Andrei Eternal
2b2de3f38b codex: support hooks in config.toml and requirements.toml (#18893)
## Summary

Support the existing hooks schema in inline TOML so hooks can be
configured from both `config.toml` and enterprise-managed
`requirements.toml` without requiring a separate `hooks.json` payload.

This gives enterprise admins a way to ship managed hook policy through
the existing requirements channel while still leaving script delivery to
MDM or other device-management tooling, and it keeps `hooks.json`
working unchanged for existing users.

This also lays the groundwork for follow-on managed filtering work such
as #15937, while continuing to respect project trust gating from #14718.
It does **not** implement `allow_managed_hooks_only` itself.

NOTE: yes, it's a bit unfortunate that the toml isn't formatted as
closely as normal to our default styling. This is because we're trying
to stay compatible with the spec for plugins/hooks that we'll need to
support & the main usecase here is embedding into requirements.toml

## What changed

- moved the shared hook serde model out of `codex-rs/hooks` into
`codex-rs/config` so the same schema can power `hooks.json`, inline
`config.toml` hooks, and managed `requirements.toml` hooks
- added `hooks` support to both `ConfigToml` and
`ConfigRequirementsToml`, including requirements-side `managed_dir` /
`windows_managed_dir`
- treated requirements-managed hooks as one constrained value via
`Constrained`, so managed hook policy is merged atomically and cannot
drift across requirement sources
- updated hook discovery to load requirements-managed hooks first, then
per-layer `hooks.json`, then per-layer inline TOML hooks, with a warning
when a single layer defines both representations
- threaded managed hook metadata through discovered handlers and exposed
requirements hooks in app-server responses, generated schemas, and
`/debug-config`
- added hook/config coverage in `codex-rs/config`, `codex-rs/hooks`,
`codex-rs/core/src/config_loader/tests.rs`, and
`codex-rs/core/tests/suite/hooks.rs`

## Testing

- `cargo test -p codex-config`
- `cargo test -p codex-hooks`
- `cargo test -p codex-app-server config_api`

## Documentation

Companion updates are needed in the developers website repo for:

- the hooks guide
- the config reference, sample, basic, and advanced pages
- the enterprise managed configuration guide

---------

Co-authored-by: Michael Bolin <mbolin@openai.com>
2026-04-22 21:20:09 -07:00