Commit Graph
11680 Commits
Author SHA1 Message Date
Michael Bolin 2c3f574d58 merge commit for archive created by Sapling 2026-04-17 10:08:12 -07:00
Michael Bolin 7f6b8d83fc tests: isolate approval fixtures from host rules 2026-04-17 10:07:49 -07:00
Michael Bolin 37139694a7 shell-escalation: carry resolved permission profiles 2026-04-17 10:07:49 -07:00
Michael Bolin b9c66f7e39 mcp: include permission profiles in sandbox state 2026-04-17 10:07:49 -07:00
Michael Bolin 9e5832b1e6 tui: carry permission profiles on user turns 2026-04-17 10:07:49 -07:00
Michael Bolin ffac8a64f6 tui: sync session permission profiles 2026-04-17 10:07:49 -07:00
Michael Bolin d4e0e5d619 app-server: accept command permission profiles 2026-04-17 10:07:49 -07:00
Michael Bolin 41ebc4d85a protocol: report session permission profiles 2026-04-17 10:07:49 -07:00
Michael Bolin 702cfbd778 rollout: persist turn permission profiles 2026-04-17 10:07:49 -07:00
Michael Bolin c69a1d8163 clients: send permission profiles to app-server 2026-04-17 10:07:49 -07:00
Michael Bolin f1bf9089df app-server: accept permission profile overrides 2026-04-17 10:07:49 -07:00
Michael Bolin 25420fc5ee app-server: expose thread permission profiles 2026-04-17 10:07:49 -07:00
Michael Bolin 98c469c0a7 core: derive active permission profiles 2026-04-17 10:07:49 -07:00
Michael Bolin 9c660d21cb exec-server: carry filesystem sandbox profiles 2026-04-17 10:07:49 -07:00
Michael Bolin 17f71a2e37 sandboxing: intersect permission profiles semantically 2026-04-17 10:07:49 -07:00
Michael Bolin d7e7f53234 protocol: canonicalize file system permissions 2026-04-17 10:07:49 -07:00
Michael Bolin da79a2011f Merge 3be462a6dd into sapling-pr-archive-bolinfest 2026-04-17 09:27:52 -07:00
Michael Bolin 3be462a6dd fix: remove unused imports that got past CI 2026-04-17 09:27:45 -07:00
Michael Bolin d0eff70383 Fix config-loader tests after filesystem abstraction race (#18351)
## Why

`origin/main` picked up two changes that crossed in flight:

- #18209 refactored config loading to read through `ExecutorFileSystem`,
changing `load_requirements_toml` to take a filesystem handle and an
`AbsolutePathBuf`.
- #17740 added managed `deny_read` requirements tests that still called
`load_requirements_toml` with the previous two-argument signature.

Once both landed, `just clippy` failed because the new tests no longer
matched the current helper API.

## What

- Updates the two managed `deny_read` requirements tests to convert the
fixture path to `AbsolutePathBuf` before loading.
- Passes `LOCAL_FS.as_ref()` into `load_requirements_toml` so these
tests follow the filesystem abstraction introduced by #18209.

## Verification

- `just clippy`
- `cargo test -p codex-core load_requirements_toml_resolves_deny_read`
- `cargo test -p codex-core --test all
unified_exec_enforces_glob_deny_read_policy`
2026-04-17 09:20:39 -07:00
pakrym-oai 71e4c6fa17 Move codex module under session (#18249)
## Summary
- rename the core codex module root to session/mod.rs without using
#[path]
- move the codex module directory and tests under core/src/session
- remove session/mod.rs reexports so call sites use explicit child
module paths

## Testing
- cargo test -p codex-core --lib
- cargo check -p codex-core --tests
- just fmt
- just fix -p codex-core
- git diff --check
2026-04-17 16:18:53 +00:00
Michael Bolin 44b233968d Merge e9052bce30 into sapling-pr-archive-bolinfest 2026-04-17 09:05:27 -07:00
Michael Bolin e9052bce30 fix build breakage resulting from merge race 2026-04-17 09:05:20 -07:00
Michael Bolin 48d30e7e74 merge commit for archive created by Sapling 2026-04-17 08:50:01 -07:00
Michael Bolin 67a4a63a82 fix: remove unused imports that got past CI 2026-04-17 08:49:47 -07:00
viyatb-oaiandCodex dae0608c06 feat(config): support managed deny-read requirements (#17740)
## Summary
- adds managed requirements support for deny-read filesystem entries
- constrains config layers so managed deny-read requirements cannot be
widened by user-controlled config
- surfaces managed deny-read requirements through debug/config plumbing

This PR lets managed requirements inject deny-read filesystem
constraints into the effective filesystem sandbox policy.
User-controlled config can still choose the surrounding permission
profile, but it cannot remove or weaken the managed deny-read entries.

## Managed deny-read shape
A managed requirements file can declare exact paths and glob patterns
under `[permissions.filesystem]`:

```toml
# /etc/codex/requirements.toml
[permissions.filesystem]
deny_read = [
  "/Users/alice/.gitconfig",
  "/Users/alice/.ssh",
  "./managed-private/**/*.env",
]
```

Those entries are compiled into the effective filesystem policy as
`access = none` rules, equivalent in shape to filesystem permission
entries like:

```toml
[permissions.workspace.filesystem]
"/Users/alice/.gitconfig" = "none"
"/Users/alice/.ssh" = "none"
"/absolute/path/to/managed-private/**/*.env" = "none"
```

The important difference is that the managed entries come from
requirements, so lower-precedence user config cannot remove them or make
those paths readable again.

Relative managed `deny_read` entries are resolved relative to the
directory containing the managed requirements file. Glob entries keep
their glob suffix after the non-glob prefix is normalized.

## Runtime behavior
- Managed `deny_read` entries are appended to the effective
`FileSystemSandboxPolicy` after the selected permission profile is
resolved.
- Exact paths become `FileSystemPath::Path { access: None }`; glob
patterns become `FileSystemPath::GlobPattern { access: None }`.
- When managed deny-read entries are present, `sandbox_mode` is
constrained to `read-only` or `workspace-write`; `danger-full-access`
and `external-sandbox` cannot silently bypass the managed read-deny
policy.
- On Windows, the managed deny-read policy is enforced for direct file
tools, but shell subprocess reads are not sandboxed yet, so startup
emits a warning for that platform.
- `/debug-config` shows the effective managed requirement as
`permissions.filesystem.deny_read` with its source.

## Stack
1. #15979 - glob deny-read policy/config/direct-tool support
2. #18096 - macOS and Linux sandbox enforcement
3. This PR - managed deny-read requirements

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-17 08:40:09 -07:00
Eric Traut 2dd6734dd3 fix(tui): use BEL for terminal title updates (#18261)
## Summary

Fixes #18160.

iTerm2 can append the current foreground process to tab titles, and
Codex's terminal-title updates were causing that decoration to appear as
`(codex")` with a stray trailing quote. Codex was writing OSC 0 title
sequences terminated with ST (`ESC \`). Some terminal title integrations
appear to accept that title update but still expose the ST terminator in
their own process/title decoration.

## Changes

- Update `codex-rs/tui/src/terminal_title.rs` to terminate OSC 0 title
updates with BEL instead of ST.
- Update the focused terminal-title encoding test to assert the
BEL-terminated sequence.

## Compatibility

This should be low risk: the title payload and update timing are
unchanged, and BEL is the form already emitted by
`crossterm::terminal::SetTitle` in the crossterm version used by this
repository. BEL is also the widely supported xterm-family title
terminator used by common terminals and multiplexers. The main
theoretical risk would be a very old or unusual terminal that accepted
only ST and not BEL for OSC title termination, but that is unlikely
compared with the observed iTerm2 issue.

## Verification

- `cargo test -p codex-tui terminal_title`
- `cargo test -p codex-tui`
2026-04-17 08:39:37 -07:00
Eric Traut c3ecb557d3 Support Ctrl+P/Ctrl+N in resume picker (#18267)
Fixes #18179.

## Why
The fullscreen `/resume` picker accepted Up/Down navigation but ignored
Ctrl+P/Ctrl+N, which made it inconsistent with other TUI selection flows
such as `ListSelectionView`-backed pickers and composer navigation.

## What Changed
Updated `codex-rs/tui/src/resume_picker.rs` so the resume picker treats
Ctrl+P/Ctrl+N as aliases for Up/Down, including the raw `^P`/`^N`
control-character events some terminals emit without a CONTROL modifier.
2026-04-17 08:38:47 -07:00
jif-oai 3421a107e0 nit: phase 2 ephemeral (#18338) 2026-04-17 16:10:58 +01:00
AbhinavandCodex 8494e5bd7b Add PermissionRequest hooks support (#17563)
## Why

We need `PermissionRequest` hook support!

Also addresses:
- https://github.com/openai/codex/issues/16301
- run a script on Hook to do things like play a sound to draw attention
but actually no-op so user can still approve
- can omit the `decision` object from output or just have the script
exit 0 and print nothing
- https://github.com/openai/codex/issues/15311
  - let the script approve/deny on its own
  - external UI what will run on Hook and relay decision back to codex


## Reviewer Note

There's a lot of plumbing for the new hook, key files to review are:
- New hook added in `codex-rs/hooks/src/events/permission_request.rs`
- Wiring for network approvals
`codex-rs/core/src/tools/network_approval.rs`
- Wiring for tool orchestrator `codex-rs/core/src/tools/orchestrator.rs`
- Wiring for execve
`codex-rs/core/src/tools/runtimes/shell/unix_escalation.rs`

## What

- Wires shell, unified exec, and network approval prompts into the
`PermissionRequest` hook flow.
- Lets hooks allow or deny approval prompts; quiet or invalid hooks fall
back to the normal approval path.
- Uses `tool_input.description` for user-facing context when it helps:
  - shell / `exec_command`: the request justification, when present
  - network approvals: `network-access <domain>`
- Uses `tool_name: Bash` for shell, unified exec, and network approval
permission-request hooks.
- For network approvals, passes the originating command in
`tool_input.command` when there is a single owning call; otherwise falls
back to the synthetic `network-access ...` command.

<details>
<summary>Example `PermissionRequest` hook input for a shell
approval</summary>

```json
{
  "session_id": "<session-id>",
  "turn_id": "<turn-id>",
  "transcript_path": "/path/to/transcript.jsonl",
  "cwd": "/path/to/cwd",
  "hook_event_name": "PermissionRequest",
  "model": "gpt-5",
  "permission_mode": "default",
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -f /tmp/example"
  }
}
```

</details>

<details>
<summary>Example `PermissionRequest` hook input for an escalated
`exec_command` request</summary>

```json
{
  "session_id": "<session-id>",
  "turn_id": "<turn-id>",
  "transcript_path": "/path/to/transcript.jsonl",
  "cwd": "/path/to/cwd",
  "hook_event_name": "PermissionRequest",
  "model": "gpt-5",
  "permission_mode": "default",
  "tool_name": "Bash",
  "tool_input": {
    "command": "cp /tmp/source.json /Users/alice/export/source.json",
    "description": "Need to copy a generated file outside the workspace"
  }
}
```

</details>

<details>
<summary>Example `PermissionRequest` hook input for a network
approval</summary>

```json
{
  "session_id": "<session-id>",
  "turn_id": "<turn-id>",
  "transcript_path": "/path/to/transcript.jsonl",
  "cwd": "/path/to/cwd",
  "hook_event_name": "PermissionRequest",
  "model": "gpt-5",
  "permission_mode": "default",
  "tool_name": "Bash",
  "tool_input": {
    "command": "curl http://codex-network-test.invalid",
    "description": "network-access http://codex-network-test.invalid"
  }
}
```

</details>

## Follow-ups

- Implement the `PermissionRequest` semantics for `updatedInput`,
`updatedPermissions`, `interrupt`, and suggestions /
`permission_suggestions`
- Add `PermissionRequest` support for the `request_permissions` tool
path

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-17 14:45:47 +00:00
sayan-oai d0047de7cb add token-based tool deferral behind feature flag (#18097)
add new `tool_search_always_defer_mcp_tools` feature flag that always
defers all mcp tools rather than deferring once > 100 deferrable tools.

add new tests, also move `mcp_exposure` tests into dedicated file rather
than polluting `codex_tests`.
2026-04-17 18:34:06 +08:00
alexsong-oai 20b4b80426 Sync local plugin imports, async remote imports, refresh caches after… (#18246)
… import

## Why

`externalAgentConfig/import` used to spawn plugin imports in the
background and return immediately. That meant local marketplace imports
could still be in flight when the caller refreshed plugin state, so
newly imported plugins would not show up right away.

This change makes local marketplace imports complete before the RPC
returns, while keeping remote marketplace imports asynchronous so we do
not block on remote fetches.

## What changed

- split plugin migration details into local and remote marketplace
imports based on the external config source
- import local marketplaces synchronously during
`externalAgentConfig/import`
- return pending remote plugin imports to the app-server so it can
finish them in the background
- clear the plugin and skills caches before responding to plugin
imports, and again after background remote imports complete, so the next
`plugin/list` reloads fresh state
- keep marketplace source parsing encapsulated behind
`is_local_marketplace_source(...)` instead of re-exporting the internal
enum
- add core and app-server coverage for the synchronous local import path
and the pending remote import path

## Verification

- `cargo test -p codex-app-server-protocol`
- `cargo test -p codex-core` (currently fails an existing unrelated
test:
`config_loader::tests::cli_override_can_update_project_local_mcp_server_when_project_is_trusted`)
- `cargo test` (currently fails existing `codex-app-server` integration
tests in MCP/skills/thread-start areas, plus the unrelated `codex-core`
failure above)
2026-04-17 09:34:55 +00:00
jif-oai 64177aaa22 fix: reduce writable root (#17947) 2026-04-17 09:33:12 +01:00
Eric Traut 2e038e6d38 Fix Windows exec policy test flake (#18304)
## Summary

This fixes a Windows-only failure in the exec policy multi-segment shell
test. The test was meant to verify that a compound shell command only
bypasses sandboxing when every parsed segment has an explicit exec
policy allow rule.

On Windows, the read-only sandbox setup is intentionally treated as
lacking sandbox protection, so the old fixture could take the approval
path before reaching the intended bypass assertion. The test now uses
the workspace-write sandbox policy, keeping the focus on the per-segment
bypass rule while preserving the expected bypass_sandbox false result
when only cat is explicitly allowed.
2026-04-17 00:43:49 -07:00
sashank-oaiandCodex 22f7ef1cb7 [codex] Revoke ChatGPT tokens on logout (#17825)
## Summary

This changes Codex logout so managed ChatGPT auth is revoked against
AuthAPI before local auth state is removed. CLI logout, TUI `/logout`,
and the app-server account logout path now use the token-revoking logout
flow instead of only deleting `auth.json` / credential store state.

## Root Cause

Logout previously cleared only local auth storage. That removed Codex's
local credentials but did not ask the backend to invalidate the
refresh/access token state associated with a managed ChatGPT login.

## Behavior

For managed ChatGPT auth, logout sends the stored refresh token to
`https://auth.openai.com/oauth/revoke` with `token_type_hint:
refresh_token` and the Codex OAuth client id, then deletes all local
auth stores after revocation succeeds. If only an access token is
available, it falls back to revoking that access token. API key auth and
externally supplied `chatgptAuthTokens` are still only cleared locally
because Codex does not own a refresh token for those modes.

Revocation failures are fail-closed: if Codex cannot load stored auth or
the backend revoke call fails, logout returns an error and leaves local
auth in place so the user can retry instead of silently clearing local
state while backend tokens remain valid.

## Validation
ran local version of `codex-cli` with staging overrides/harness for auth

ran `codex login` then `codex logout`:

saw auth.json clear and  backend revocation endpoints were called

```
POST /oauth/revoke
status: 200

revoking access token
should clear auth session
clearing auth session due to token revocation
successfully revoked session and access token
CANONICAL-API-LINE Response: status='200' method='POST' path='/oauth/revoke
```

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-16 22:51:21 -07:00
Michael Bolin b8935f1e2d Merge 153c846a0a into sapling-pr-archive-bolinfest 2026-04-16 22:32:50 -07:00
Michael Bolin 153c846a0a tests: isolate approval fixtures from host rules 2026-04-16 22:32:35 -07:00
Michael Bolin 177e1f03da shell-escalation: carry resolved permission profiles 2026-04-16 22:32:35 -07:00
Michael Bolin 435f0d65e9 mcp: include permission profiles in sandbox state 2026-04-16 22:32:35 -07:00
Michael Bolin 37759580a5 tui: carry permission profiles on user turns 2026-04-16 22:32:35 -07:00
Michael Bolin dc15de86e3 tui: sync session permission profiles 2026-04-16 22:32:35 -07:00
Michael Bolin fa7535b8ad app-server: accept command permission profiles 2026-04-16 22:32:35 -07:00
Michael Bolin bc7e46376e protocol: report session permission profiles 2026-04-16 22:32:35 -07:00
Michael Bolin 449f772a40 rollout: persist turn permission profiles 2026-04-16 22:32:35 -07:00
Michael Bolin f819f6214a clients: send permission profiles to app-server 2026-04-16 22:32:35 -07:00
Michael Bolin af1dbc447b app-server: accept permission profile overrides 2026-04-16 22:32:35 -07:00
Michael Bolin 4874afe1a6 app-server: expose thread permission profiles 2026-04-16 22:32:35 -07:00
Michael Bolin da38f0dbaa core: derive active permission profiles 2026-04-16 22:32:35 -07:00
Michael Bolin 950809add0 exec-server: carry filesystem sandbox profiles 2026-04-16 22:32:35 -07:00
Michael Bolin 4be1ed7e8d sandboxing: intersect permission profiles semantically 2026-04-16 22:32:35 -07:00
Michael Bolin d1b30474a9 protocol: canonicalize file system permissions 2026-04-16 22:32:35 -07:00