Commit Graph

128 Commits

Author SHA1 Message Date
pakrym-oai
53b1570367 Update image outputs to default to high detail (#18386)
Do not assume the default `detail`.
2026-04-18 11:01:12 -07:00
richardopenai
6b39d0c657 [codex] Add owner nudge app-server API (#18220)
## Summary

Second PR in the split from #17956. Stacked on #18227.

- adds app-server v2 protocol/schema support for
`account/sendAddCreditsNudgeEmail`
- adds the backend-client `send_add_credits_nudge_email` request and
request body mapping
- handles the app-server request with auth checks, backend call, and
cooldown mapping
- adds the disabled `workspace_owner_usage_nudge` feature flag and
focused app-server/backend tests

## Validation

- `cargo test -p codex-backend-client`
- `cargo test -p codex-app-server-protocol`
- `cargo test -p codex-app-server rate_limits`
- `cargo test -p codex-tui workspace_`
- `cargo test -p codex-tui status_`
- `just fmt`
- `just fix -p codex-backend-client`
- `just fix -p codex-app-server-protocol`
- `just fix -p codex-app-server`
- `just fix -p codex-tui`
2026-04-17 21:41:57 -07:00
xl-openai
3f7222ec76 feat: Budget skill metadata and surface trimming as a warning (#18298)
Cap the model-visible skills section to a small share of the context
window, with a fallback character budget, and keep only as many implicit
skills as fit within that budget.

Emit a non-fatal warning when enabled skills are omitted, and add a new
app-server warning notification

Record thread-start skill metrics for total enabled skills, kept skills,
and whether truncation happened

---------

Co-authored-by: Matthew Zeng <mzeng@openai.com>
Co-authored-by: Codex <noreply@openai.com>
2026-04-17 18:11:47 -07:00
xl-openai
26d9894a27 feat: Add remote plugin fields to plugin API (#17277)
## Summary
Update the plugin API for the new remote plugin model.

The mental model is no longer “keep local plugin state in sync with
remote.” Instead, local and remote plugins are becoming separate
sources. Remote catalog entries can be shown directly from the remote
API before installation; after installation they are still downloaded
into the local cache for execution, but remote installed state will come
from the API and be held in memory rather than being read from config.

• ## API changes
- Remove `forceRemoteSync` from `plugin/list`, `plugin/install`, and
`plugin/uninstall`.
  - Remove `remoteSyncError` from `plugin/list`.
  - Add remote-capable metadata to `plugin/list` / `plugin/read`:
    - nullable `marketplaces[].path`
    - `source: { type: "remote", downloadUrl }`
    - URL asset fields alongside local path fields:
  `composerIconUrl`, `logoUrl`, `screenshotUrls`
  - Make `plugin/read` and `plugin/install` source-compatible:
    - `marketplacePath?: AbsolutePathBuf | null`
    - `remoteMarketplaceName?: string | null`
    - exactly one source is required at runtime
2026-04-17 16:47:58 -07:00
xli-oai
0e111e08d0 [codex] Add cross-repo plugin sources to marketplace manifests (#18017)
## Summary
- add first-class marketplace support for git-backed plugin sources
- keep the newer marketplace parsing behavior from `main`, including
alternate manifest locations and string local sources
- materialize remote plugin sources during install, detail reads, and
non-curated cache refresh
- expose git plugin source metadata through the app-server protocol

## Details
This teaches the marketplace parser to accept all of the following:
- local string sources such as `"source": "./plugins/foo"`
- local object sources such as
`{"source":"local","path":"./plugins/foo"}`
- remote repo-root sources such as
`{"source":"url","url":"https://github.com/org/repo.git"}`
- remote subdir sources such as
`{"source":"git-subdir","url":"owner/repo","path":"plugins/foo","ref":"main","sha":"..."}`

It also preserves the newer tolerant behavior from `main`: invalid or
unsupported plugin entries are skipped instead of breaking the whole
marketplace.

## Validation
- `cargo test -p codex-core plugins::marketplace::tests`
- `just fix -p codex-core`
- `just fmt`

## Notes
- A full `cargo test -p codex-core` run still hit unrelated existing
failures in agent and multi-agent tests during this session; the
marketplace-focused suite passed after the rebase resolution.
2026-04-17 15:11:42 -07:00
richardopenai
139fa8b8f2 [codex] Propagate rate limit reached type (#18227)
## Summary

First PR in the split from #17956.

- adds the core/app-server `RateLimitReachedType` shape
- maps backend `rate_limit_reached_type` into Codex rate-limit snapshots
- carries the field through app-server notifications/responses and
generated schemas
- updates existing constructors/tests for the new optional field

## Validation

- `cargo test -p codex-backend-client`
- `cargo test -p codex-app-server-protocol`
- `cargo test -p codex-app-server rate_limits`
- `cargo test -p codex-tui workspace_`
- `cargo test -p codex-tui status_`
- `just fmt`
- `just fix -p codex-backend-client`
- `just fix -p codex-app-server-protocol`
- `just fix -p codex-app-server`
- `just fix -p codex-tui`
2026-04-17 13:37:25 -07:00
David de Regt
eaf78e43f2 Add sorting/backwardsCursor to thread/list and new thread/turns/list api (#17305)
To improve performance of UI loads from the app, add two main
improvements:
1. The `thread/list` api now gets a `sortDirection` request field and a
`backwardsCursor` to the response, which lets you paginate forwards and
backwards from a window. This lets you fetch the first few items to
display immediately while you paginate to fill in history, then can
paginate "backwards" on future loads to catch up with any changes since
the last UI load without a full reload of the entire data set.
2. Added a new `thread/turns/list` api which also has sortDirection and
backwardsCursor for the same behavior as `thread/list`, allowing you the
same small-fetch for immediate display followed by background fill-in
and resync catchup.
2026-04-17 11:49:02 -07:00
Won Park
af7b8d551c Guardian -> Auto-Review (#18021)
This PR is a user-facing change for our rebranding of guardian to
auto-review.
2026-04-17 09:56:24 -07:00
Abhinav
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
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
Abhinav
8720b7bdce Add codex_hook_run analytics event (#17996)
# Why
Add product analytics for hook handler executions so we can understand
which hooks are running, where they came from, and whether they
completed, failed, stopped, or blocked work.

# What
- add the new `codex_hook_run` analytics event and payload plumbing in
`codex-rs/analytics`
- emit hook-run analytics from the shared hook completion path in
`codex-rs/core`
- classify hook source from the loaded hook path as `system`, `user`,
`project`, or `unknown`

```
{
  "event_type": "codex_hook_run",
  "event_params": {
    "thread_id": "string",
    "turn_id": "string",
    "model_slug": "string",
    "hook_name": "string, // any HookEventName
    "hook_source": "system | user | project | unknown",
    "status": "completed | failed | stopped | blocked"
  }
}
```

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-16 19:43:16 +00:00
Matthew Zeng
224dad41ac [codex][mcp] Add resource uri meta to tool call item. (#17831)
- [x] Add resource uri meta to tool call item so that the app-server
client can start prefetching resources immediately without loading mcp
server status.
2026-04-16 05:09:17 +00:00
alexsong-oai
ca650561d6 support plugins in external agent config migration (#17855) 2026-04-14 19:39:10 -07:00
pakrym-oai
dd1321d11b Spread AbsolutePathBuf (#17792)
Mechanical change to promote absolute paths through code.
2026-04-14 14:26:10 -07:00
viyatb-oai
81c0bcc921 fix: Revert danger-full-access denylist-only mode (#17732)
## Summary

- Reverts openai/codex#16946 and removes the danger-full-access
denylist-only network mode.
- Removes the corresponding config requirements, app-server
protocol/schema, config API, TUI debug output, and network proxy
behavior.
- Drops stale tests that depended on the reverted mode while preserving
newer managed allowlist-only coverage.

## Verification

- `just write-app-server-schema`
- `just fmt`
- `cargo test -p codex-config network_requirements`
- `cargo test -p codex-core network_proxy_spec`
- `cargo test -p codex-core
managed_network_proxy_decider_survives_full_access_start`
- `cargo test -p codex-app-server map_requirements_toml_to_api`
- `cargo test -p codex-tui debug_config_output`
- `cargo test -p codex-app-server-protocol`
- `just fix -p codex-config -p codex-core -p codex-app-server-protocol
-p codex-app-server -p codex-tui`
- `git diff --cached --check`

Not run: full workspace `cargo test` (repo instructions ask for
confirmation before that broader run).
2026-04-14 09:50:14 -07:00
Ahmed Ibrahim
2f6fc7c137 Add realtime output modality and transcript events (#17701)
- Add outputModality to thread/realtime/start and wire text/audio output
selection through app-server, core, API, and TUI.\n- Rename the realtime
transcript delta notification and add a separate transcript done
notification that forwards final text from item done without correlating
it with deltas.
2026-04-14 00:13:13 -07:00
xli-oai
ff584c5a4b [codex] Refactor marketplace add into shared core flow (#17717)
## Summary

Move `codex marketplace add` onto a shared core implementation so the
CLI and app-server path can use one source of truth.

This change:
- adds shared marketplace-add orchestration in `codex-core`
- switches the CLI command to call that shared implementation
- removes duplicated CLI-only marketplace add helpers
- preserves focused parser and add-path coverage while moving the shared
behavior into core tests

## Why

The new `marketplace/add` RPC should reuse the same underlying
marketplace-add flow as the CLI. This refactor lands that consolidation
first so the follow-up app-server PR can be mostly protocol and handler
wiring.

## Validation

- `cargo test -p codex-core marketplace_add`
- `cargo test -p codex-cli marketplace_cmd`
- `just fix -p codex-core`
- `just fix -p codex-cli`
- `just fmt`
2026-04-13 20:37:11 -07:00
pakrym-oai
f3cbe3d385 [codex] Add symlink flag to fs metadata (#17719)
Add `is_symlink` to FsMetadata struct.
2026-04-13 17:46:56 -07:00
pakrym-oai
d4be06adea Add turn item injection API (#17703)
## Summary
- Add `turn/inject_items` app-server v2 request support for appending
raw Responses API items to a loaded thread history without starting a
turn.
- Generate JSON schema and TypeScript protocol artifacts for the new
params and empty response.
- Document the new endpoint and include a request/response example.
- Preserve compatibility with the typo alias `turn/injet_items` while
returning the canonical method name.

## Testing
- Not run (not requested)
2026-04-13 16:11:05 -07:00
jif-oai
46a266cd6a feat: disable memory endpoint (#17626) 2026-04-13 18:29:49 +01:00
pakrym-oai
ac82443d07 Use AbsolutePathBuf in skill loading and codex_home (#17407)
Helps with FS migration later
2026-04-13 10:26:51 -07:00
Eric Traut
46ab9974dc Expose instruction sources (AGENTS.md) via app server (#17506)
Addresses #17498

Problem: The TUI derived /status instruction source paths from the local
client environment, which could show stale <none> output or incorrect
paths when connected to a remote app server.

Solution: Add an app-server v2 instructionSources snapshot to thread
start/resume/fork responses, default it to an empty list when older
servers omit it, and render TUI /status from that server-provided
session data.

Additional context: The app-server field is intentionally named
instructionSources rather than AGENTS.md-specific terminology because
the loaded instruction sources can include global instructions, project
AGENTS.md files, AGENTS.override.md, user-defined instruction files, and
future dynamic sources.
2026-04-12 15:50:12 -07:00
Eric Traut
3b948d9dd8 Support prolite plan type (#17419)
Addresses #17353

Problem: Codex rate-limit fetching failed when the backend returned the
new `prolite` subscription plan type.

Solution: Add `prolite` to the backend/account/auth plan mappings, keep
unknown WHAM plan values decodable, and regenerate app-server plan
schemas.
2026-04-11 13:58:16 -07:00
ningyi-oai
be13f03c39 Pass turn id with feedback uploads (#17314)
## Summary
- Add an optional `tags` dictionary to feedback upload params.
- Capture the active app-server turn id in the TUI and submit it as
`tags.turn_id` with `/feedback` uploads.
- Merge client-provided feedback tags into Sentry feedback tags while
preserving reserved system fields like `thread_id`, `classification`,
`cli_version`, `session_source`, and `reason`.

## Behavior / impact
Existing feedback upload callers remain compatible because `tags` is
optional and nullable. The wire shape is still a normal JSON object /
TypeScript dictionary, so adding future feedback metadata will not
require a new top-level protocol field each time. This change only adds
feedback metadata for Codex CLI/TUI uploads; it does not affect existing
pipelines, DAGs, exports, or downstream consumers unless they choose to
read the new `turn_id` feedback tag.

## Tests
- `cargo fmt -- --config imports_granularity=Item` passed; stable
rustfmt warned that `imports_granularity` is nightly-only.
- `cargo run -p codex-app-server-protocol --bin write_schema_fixtures`
- `cargo test -p codex-feedback
upload_tags_include_client_tags_and_preserve_reserved_fields`
- `cargo test -p codex-app-server-protocol
schema_fixtures_match_generated`
- `cargo test -p codex-tui build_feedback_upload_params`
- `cargo test -p codex-tui
live_app_server_turn_started_sets_feedback_turn_id`
- `cargo check -p codex-app-server --tests`
- `git diff --check`

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-11 00:23:50 -07:00
Matthew Zeng
b7139a7e8f [mcp] Support MCP Apps part 3 - Add mcp tool call support. (#17364)
- [x] Add a new app-server method so that MCP Apps can call their own
MCP server directly.
2026-04-11 04:39:19 +00:00
Won Park
37aac89a6d representing guardian review timeouts in protocol types (#17381)
## Summary

- Add `TimedOut` to Guardian/review carrier types:
  - `ReviewDecision::TimedOut`
  - `GuardianAssessmentStatus::TimedOut`
  - app-server v2 `GuardianApprovalReviewStatus::TimedOut`
- Regenerate app-server JSON/TypeScript schemas for the new wire shape.
- Wire the new status through core/app-server/TUI mappings with
conservative fail-closed handling.
- Keep `TimedOut` non-user-selectable in the approval UI.

**Does not change runtime behavior yet; emitting `TimeOut` and
parent-model timeout messaging will come in followup PRs**
2026-04-10 20:02:33 -07:00
Shijie Rao
930e5adb7e Revert "Option to Notify Workspace Owner When Usage Limit is Reached" (#17391)
Reverts openai/codex#16969

#sev3-2026-04-10-accountscheckversion-500s-for-openai-workspace-7300
2026-04-10 23:33:13 +00:00
Owen Lin
a3be74143a fix(guardian, app-server): introduce guardian review ids (#17298)
## Description

This PR introduces `review_id` as the stable identifier for guardian
reviews and exposes it in app-server `item/autoApprovalReview/started`
and `item/autoApprovalReview/completed` events.

Internally, guardian rejection state is now keyed by `review_id` instead
of the reviewed tool item ID. `target_item_id` is still included when a
review maps to a concrete thread item, but it is no longer overloaded as
the review lifecycle identifier.

## Motivation

We'd like to give users the ability to preempt a guardian review while
it's running (approve or decline).

However, we can't implement the API that allows the user to override a
running guardian review because we didn't have a unique `review_id` per
guardian review. Using `target_item_id` is not correct since:
- with execve reviews, there can be multiple execve calls (and therefore
guardian reviews) per shell command
- with network policy reviews, there is no target item ID

The PR that actually implements user overrides will use `review_id` as
the stable identifier.
2026-04-10 16:21:02 -07:00
Abhinav
7999b0f60f Support clear SessionStart source (#17073)
## Motivation

The `SessionStart` hook already receives `startup` and `resume` sources,
but sessions created from `/clear` previously looked like normal startup
sessions. This makes it impossible for hook authors to distinguish
between these with the matcher.

## Summary

- Add `InitialHistory::Cleared` so `/clear`-created sessions can be
distinguished from ordinary startup sessions.
- Add `SessionStartSource::Clear` and wire it through core, app-server
thread start params, and TUI clear-session flow.
- Update app-server protocol schemas, generated TypeScript, docs, and
related tests.


https://github.com/user-attachments/assets/9cae3cb4-41c7-4d06-b34f-966252442e5c
2026-04-10 16:05:21 -07:00
richardopenai
9f2a585153 Option to Notify Workspace Owner When Usage Limit is Reached (#16969)
## Summary
- Replace the manual `/notify-owner` flow with an inline confirmation
prompt when a usage-based workspace member hits a credits-depleted
limit.
- Fetch the current workspace role from the live ChatGPT
`accounts/check/v4-2023-04-27` endpoint so owner/member behavior matches
the desktop and web clients.
- Keep owner, member, and spend-cap messaging distinct so we only offer
the owner nudge when the workspace is actually out of credits.

## What Changed
- `backend-client`
- Added a typed fetch for the current account role from
`accounts/check`.
  - Mapped backend role values into a Rust workspace-role enum.
- `app-server` and protocol
  - Added `workspaceRole` to `account/read` and `account/updated`.
- Derived `isWorkspaceOwner` from the live role, with a fallback to the
cached token claim when the role fetch is unavailable.
- `tui`
  - Removed the explicit `/notify-owner` slash command.
- When a member is blocked because the workspace is out of credits, the
error now prompts:
- `Your workspace is out of credits. Request more from your workspace
owner? [y/N]`
  - Choosing `y` sends the existing owner-notification request.
- Choosing `n`, pressing `Esc`, or accepting the default selection
dismisses the prompt without sending anything.
- Selection popups now honor explicit item shortcuts, which is how the
`y` / `n` interaction is wired.

## Reviewer Notes
- The main behavior change is scoped to usage-based workspace members
whose workspace credits are depleted.
- Spend-cap reached should not show the owner-notification prompt.
- Owners and admins should continue to see `/usage` guidance instead of
the member prompt.
- The live role fetch is best-effort; if it fails, we fall back to the
existing token-derived ownership signal.

## Testing
- Manual verification
  - Workspace owner does not see the member prompt.
- Workspace member with depleted credits sees the confirmation prompt
and can send the nudge with `y`.
- Workspace member with spend cap reached does not see the
owner-notification prompt.

### Workspace member out of usage

https://github.com/user-attachments/assets/341ac396-eff4-4a7f-bf0c-60660becbea1

### Workspace owner
<img width="1728" height="1086" alt="Screenshot 2026-04-09 at 11 48
22 AM"
src="https://github.com/user-attachments/assets/06262a45-e3fc-4cc4-8326-1cbedad46ed6"
/>
2026-04-09 21:15:17 -07:00
Ahmed Ibrahim
2f9090be62 Add realtime voice selection (#17176)
- Add realtime voice selection for realtime/start.
- Expose the supported v1/v2 voice lists and cover explicit, configured,
default, and invalid voice paths.
2026-04-08 20:19:15 -07:00
maja-openai
dcbc91fd39 Update guardian output schema (#17061)
## Summary
- Update guardian output schema to separate risk, authorization,
outcome, and rationale.
- Feed guardian rationale into rejection messages.
- Split the guardian policy into template and tenant-config sections.

## Validation
- `cargo test -p codex-core mcp_tool_call`
- `env -u CODEX_SANDBOX_NETWORK_DISABLED INSTA_UPDATE=always cargo test
-p codex-core guardian::`

---------

Co-authored-by: Owen Lin <owen@openai.com>
2026-04-08 15:47:29 -07:00
pash-openai
80ebc80be5 Use model metadata for Fast Mode status (#16949)
Fast Mode status was still tied to one model name in the TUI and
model-list plumbing. This changes the model metadata shape so a model
can advertise additional speed tiers, carries that field through the
app-server model list, and uses it to decide when to show Fast Mode
status.

For people using Codex, the behavior is intended to stay the same for
existing models. Fast Mode still requires the existing signed-in /
feature-gated path; the difference is that the UI can now recognize any
model the model list marks as Fast-capable, instead of requiring a new
client-side slug check.
2026-04-07 17:55:40 -07:00
Ahmed Ibrahim
fb3dcfde1d Add WebRTC transport to realtime start (#16960)
Adds WebRTC startup to the experimental app-server
`thread/realtime/start` method with an optional transport enum. The
websocket path remains the default; WebRTC offers create the realtime
session through the shared start flow and emit the answer SDP via
`thread/realtime/sdp`.

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-07 15:43:38 -07:00
Ruslan Nigmatullin
8a13f82204 app-server: Move watch_id to request of fs/watch (#17026)
It's easier for clients to maintain watchers if they define the watch
id, so move it into the request.
It's not used yet, so should be a safe change.
2026-04-07 11:22:28 -07:00
Matthew Zeng
252d79f5eb [mcp] Support MCP Apps part 2 - Add meta to mcp tool call result. (#16465)
- [x] Add meta to mcp tool call result.
2026-04-07 11:10:21 -07:00
viyatb-oai
9d13d29acd [codex] Add danger-full-access denylist-only network mode (#16946)
## Summary

This adds `experimental_network.danger_full_access_denylist_only` for
orgs that want yolo / danger-full-access sessions to keep full network
access while still enforcing centrally managed deny rules.

When the flag is true and the session sandbox is `danger-full-access`,
the network proxy starts with:

- domain allowlist set to `*`
- managed domain `deny` entries enforced
- upstream proxy use allowed
- all Unix sockets allowed
- local/private binding allowed

Caveat: the denylist is best effort only. In yolo / danger-full-access
mode, Codex or the model can use an allowed socket or other
local/private network path to bypass the proxy denylist, so this should
not be treated as a hard security boundary.

The flag is intentionally scoped to `SandboxPolicy::DangerFullAccess`.
Read-only and workspace-write modes keep the existing managed/user
allowlist, denylist, Unix socket, and local-binding behavior. This does
not enable the non-loopback proxy listener setting; that still requires
its own explicit config.

This also threads the new field through config requirements parsing,
app-server protocol/schema output, config API mapping, and the TUI debug
config output.

## How to use

Add the flag under `[experimental_network]` in the network policy config
that is delivered to Codex. The setting is not under `[permissions]`.

```toml
[experimental_network]
enabled = true
danger_full_access_denylist_only = true

[experimental_network.domains]
"blocked.example.com" = "deny"
"*.blocked.example.com" = "deny"
```

With that configuration, yolo / danger-full-access sessions get broad
network access except for the managed denied domains above. The denylist
remains a best-effort proxy policy because the session may still use
allowed sockets to bypass it. Other sandbox modes do not get the
wildcard domain allowlist or the socket/local-binding relaxations from
this flag.

## Verification

- `cargo test -p codex-config network_requirements`
- `cargo test -p codex-core network_proxy_spec`
- `cargo test -p codex-app-server map_requirements_toml_to_api`
- `cargo test -p codex-tui debug_config_output`
- `cargo test -p codex-app-server-protocol`
- `just write-app-server-schema`
- `just fmt`
- `just fix -p codex-config -p codex-core -p codex-app-server-protocol
-p codex-app-server -p codex-tui`
- `just fix -p codex-core -p codex-config`
- `git diff --check`
- `cargo clean`
2026-04-06 19:38:51 -07:00
Matthew Zeng
5fe9ef06ce [mcp] Support MCP Apps part 1. (#16082)
- [x] Add `mcpResource/read` method to read mcp resource.
2026-04-06 19:17:14 -07:00
Eric Traut
9f737c28dd Speed up /mcp inventory listing (#16831)
Addresses #16244

This was a performance regression introduced when we moved the TUI on
top of the app server API.

Problem: `/mcp` rebuilt a full MCP inventory through
`mcpServerStatus/list`, including resources and resource templates that
made the TUI wait on slow inventory probes.

Solution: add a lightweight `detail` mode to `mcpServerStatus/list`,
have `/mcp` request tools-and-auth only, and cover the fast path with
app-server and TUI tests.

Testing: Confirmed slow (multi-second) response prior to change and
immediate response after change.

I considered two options:
1. Change the existing `mcpServerStatus/list` API to accept an optional
"details" parameter so callers can request only a subset of the
information.
2. Add a separate `mcpServer/list` API that returns only the servers,
tools, and auth but omits the resources.

I chose option 1, but option 2 is also a reasonable approach.
2026-04-06 16:27:02 -07:00
rhan-oai
756c45ec61 [codex-analytics] add protocol-native turn timestamps (#16638)
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/16638).
* #16870
* #16706
* #16659
* #16641
* #16640
* __->__ #16638
2026-04-06 16:22:59 -07:00
Eric Traut
9bb7f0a694 Fix fork source display in /status (expose forked_from_id in app server) (#16596)
Addresses #16560

Problem: `/status` stopped showing the source thread id in forked TUI
sessions after the app-server migration.

Solution: Carry fork source ids through app-server v2 thread data and
the TUI session adapter, and update TUI fixtures so `/status` matches
the old TUI behavior.
2026-04-02 14:05:29 -07:00
Owen Lin
30f6786d62 fix(guardian): make GuardianAssessmentEvent.action strongly typed (#16448)
## Description

Previously the `action` field on `EventMsg::GuardianAssessment`, which
describes what Guardian is reviewing, was typed as an arbitrary JSON
blob. This PR cleans it up and defines a sum type representing all the
various actions that Guardian can review.

This is a breaking change (on purpose), which is fine because:
- the Codex app / VSCE does not actually use `action` at the moment
- the TUI code that consumes `action` is updated in this PR as well
- rollout files that serialized old `EventMsg::GuardianAssessment` will
just silently drop these guardian events
- the contract is defined as unstable, so other clients have a fair
warning :)

This will make things much easier for followup Guardian work.

## Why

The old guardian review payloads worked, but they pushed too much shape
knowledge into downstream consumers. The TUI had custom JSON parsing
logic for commands, patches, network requests, and MCP calls, and the
app-server protocol was effectively just passing through an opaque blob.

Typing this at the protocol boundary makes the contract clearer.
2026-04-01 15:42:18 -07:00
bwanner-oai
82e8031338 Add usage-based business plan types (#15934)
## Summary
- add `self_serve_business_usage_based` and `enterprise_cbp_usage_based`
to the public/internal plan enums and regenerate the app-server + Python
SDK artifacts
- map both plans through JWT login and backend rate-limit payloads, then
bucket them with the existing Team/Business entitlement behavior in
cloud requirements, usage-limit copy, tooltips, and status display
- keep the earlier display-label remap commit on this branch so the new
Team-like and Business-like plans render consistently in the UI

## Testing
- `just write-app-server-schema`
- `uv run --project sdk/python python
sdk/python/scripts/update_sdk_artifacts.py generate-types`
- `just fix -p codex-protocol -p codex-login -p codex-core -p
codex-backend-client -p codex-cloud-requirements -p codex-tui -p
codex-tui-app-server -p codex-backend-openapi-models`
- `just fmt`
- `just argument-comment-lint`
- `cargo test -p codex-protocol
usage_based_plan_types_use_expected_wire_names`
- `cargo test -p codex-login usage_based`
- `cargo test -p codex-backend-client usage_based`
- `cargo test -p codex-cloud-requirements usage_based`
- `cargo test -p codex-core usage_limit_reached_error_formats_`
- `cargo test -p codex-tui plan_type_display_name_remaps_display_labels`
- `cargo test -p codex-tui remapped`
- `cargo test -p codex-tui-app-server
plan_type_display_name_remaps_display_labels`
- `cargo test -p codex-tui-app-server remapped`
- `cargo test -p codex-tui-app-server
preserves_usage_based_plan_type_wire_name`

## Notes
- a broader multi-crate `cargo test` run still hits unrelated existing
guardian-approval config failures in
`codex-rs/core/src/config/config_tests.rs`
2026-03-27 14:25:13 -07:00
daniel-oai
47a9e2e084 Add ChatGPT device-code login to app server (#15525)
## Problem

App-server clients could only initiate ChatGPT login through the browser
callback flow, even though the shared login crate already supports
device-code auth. That left VS Code, Codex App, and other app-server
clients without a first-class way to use the existing device-code
backend when browser redirects are brittle or when the client UX wants
to own the login ceremony.

## Mental model

This change adds a second ChatGPT login start path to app-server:
clients can now call `account/login/start` with `type:
"chatgptDeviceCode"`. App-server immediately returns a `loginId` plus
the device-code UX payload (`verificationUrl` and `userCode`), then
completes the login asynchronously in the background using the existing
`codex_login` polling flow. Successful device-code login still resolves
to ordinary `chatgpt` auth, and completion continues to flow through the
existing `account/login/completed` and `account/updated` notifications.

## Non-goals

This does not introduce a new auth mode, a new account shape, or a
device-code eligibility discovery API. It also does not add automatic
fallback to browser login in core; clients remain responsible for
choosing when to request device code and whether to retry with a
different UX if the backend/admin policy rejects it.

## Tradeoffs

We intentionally keep `login_chatgpt_common` as a local validation
helper instead of turning it into a capability probe. Device-code
eligibility is checked by actually calling `request_device_code`, which
means policy-disabled cases surface as an immediate request error rather
than an async completion event. We also keep the active-login state
machine minimal: browser and device-code logins share the same public
cancel contract, but device-code cancellation is implemented with a
local cancel token rather than a larger cross-crate refactor.

## Architecture

The protocol grows a new `chatgptDeviceCode` request/response variant in
app-server v2. On the server side, the new handler reuses the existing
ChatGPT login precondition checks, calls `request_device_code`, returns
the device-code payload, and then spawns a background task that waits on
either cancellation or `complete_device_code_login`. On success, it
reuses the existing auth reload and cloud-requirements refresh path
before emitting `account/login/completed` success and `account/updated`.
On failure or cancellation, it emits only `account/login/completed`
failure. The existing `account/login/cancel { loginId }` contract
remains unchanged and now works for both browser and device-code
attempts.


## Tests

Added protocol serialization coverage for the new request/response
variant, plus app-server tests for device-code success, failure, cancel,
and start-time rejection behavior. Existing browser ChatGPT login
coverage remains in place to show that the callback-based flow is
unchanged.
2026-03-27 00:27:15 -07:00
Celia Chen
dd30c8eedd chore: refactor network permissions to use explicit domain and unix socket rule maps (#15120)
## Summary

This PR replaces the legacy network allow/deny list model with explicit
rule maps for domains and unix sockets across managed requirements,
permissions profiles, the network proxy config, and the app server
protocol.

Concretely, it:

- introduces typed domain (`allow` / `deny`) and unix socket permission
(`allow` / `none`) entries instead of separate `allowed_domains`,
`denied_domains`, and `allow_unix_sockets` lists
- updates config loading, managed requirements merging, and exec-policy
overlays to read and upsert rule entries consistently
- exposes the new shape through protocol/schema outputs, debug surfaces,
and app-server config APIs
- rejects the legacy list-based keys and updates docs/tests to reflect
the new config format

## Why

The previous representation split related network policy across multiple
parallel lists, which made merging and overriding rules harder to reason
about. Moving to explicit keyed permission maps gives us a single source
of truth per host/socket entry, makes allow/deny precedence clearer, and
gives protocol consumers access to the full rule state instead of
derived projections only.

## Backward Compatibility

### Backward compatible

- Managed requirements still accept the legacy
`experimental_network.allowed_domains`,
`experimental_network.denied_domains`, and
`experimental_network.allow_unix_sockets` fields. They are normalized
into the new canonical `domains` and `unix_sockets` maps internally.
- App-server v2 still deserializes legacy `allowedDomains`,
`deniedDomains`, and `allowUnixSockets` payloads, so older clients can
continue reading managed network requirements.
- App-server v2 responses still populate `allowedDomains`,
`deniedDomains`, and `allowUnixSockets` as legacy compatibility views
derived from the canonical maps.
- `managed_allowed_domains_only` keeps the same behavior after
normalization. Legacy managed allowlists still participate in the same
enforcement path as canonical `domains` entries.

### Not backward compatible

- Permissions profiles under `[permissions.<profile>.network]` no longer
accept the legacy list-based keys. Those configs must use the canonical
`[domains]` and `[unix_sockets]` tables instead of `allowed_domains`,
`denied_domains`, or `allow_unix_sockets`.
- Managed `experimental_network` config cannot mix canonical and legacy
forms in the same block. For example, `domains` cannot be combined with
`allowed_domains` or `denied_domains`, and `unix_sockets` cannot be
combined with `allow_unix_sockets`.
- The canonical format can express explicit `"none"` entries for unix
sockets, but those entries do not round-trip through the legacy
compatibility fields because the legacy fields only represent allow/deny
lists.
## Testing
`/target/debug/codex sandbox macos --log-denials /bin/zsh -c 'curl
https://www.example.com' ` gives 200 with config
```
[permissions.workspace.network.domains]
"www.example.com" = "allow"
```
and fails when set to deny: `curl: (56) CONNECT tunnel failed, response
403`.

Also tested backward compatibility path by verifying that adding the
following to `/etc/codex/requirements.toml` works:
```
[experimental_network]
allowed_domains = ["www.example.com"]
```
2026-03-27 06:17:59 +00:00
Andrei Eternal
c4d9887f9a [hooks] add non-streaming (non-stdin style) shell-only PostToolUse support (#15531)
CHAINED PR - note that base is eternal/hooks-pretooluse-bash, not main
-- so the following PR should be first

Matching post-tool hook to the pre-tool functionality here:
https://github.com/openai/codex/pull/15211

So, PreToolUse calls for plain shell calls, allows blocking. This
PostToolUse call runs after the command executed

example run:

```
› as a test, run in parallel the following commands:
  - echo 'one'
  - echo '[block-pre-tool-use]'
  - echo '[block-post-tool-use]'


⚠ MCP startup incomplete (failed: notion, linear)

• Cruising through those three commands in parallel now, and I’ll share the exact outputs right after
  they land.

• Running PreToolUse hook: checking the observatory runes

• Running PreToolUse hook: checking the observatory runes

• Running PreToolUse hook: checking the observatory runes

PreToolUse hook (blocked)
  warning: wizard-tower PreToolUse demo blocked a Bash command on purpose.
  feedback: PreToolUse demo blocked the command. Remove [block-pre-tool-use] to continue.

PreToolUse hook (completed)
  warning: wizard-tower PreToolUse demo inspected Bash: echo 'one'

PreToolUse hook (completed)
  warning: wizard-tower PreToolUse demo inspected Bash: echo '[block-post-tool-use]'

• Ran echo '[block-post-tool-use]'
  └ [block-post-tool-use]

• Ran echo 'one'
  └ one

• Running PostToolUse hook: reading the observatory aftermath

• Running PostToolUse hook: reading the observatory aftermath

PostToolUse hook (blocked)
  warning: wizard-tower PostToolUse demo blocked the Bash follow-through on purpose.
  feedback: PostToolUse demo blocked the result after execution. Remove [block-post-tool-use] to
continue.

PostToolUse hook (completed)
  warning: wizard-tower PostToolUse demo inspected Bash: echo 'one'
  hook context: PostToolUse demo saw the Bash result. Response preview: one

──────────────────────────────────────────────────────────────────────────────────────────────────────

• Ran them in parallel. Little wave report:

  - echo 'one' completed and returned one.
  - echo '[block-pre-tool-use]' was blocked before execution by the PreToolUse hook.
  - echo '[block-post-tool-use]' ran, but its result was blocked afterward by the PostToolUse hook.

  The hook messages were:

  - PreToolUse demo blocked the command. Remove [block-pre-tool-use] to continue.
  - PostToolUse demo blocked the result after execution. Remove [block-post-tool-use] to continue.


› for the blocked messages (correctly blocked in this test) did the tool output land back into
  context?


• Yeah, from this run:

  - PreToolUse block: the command itself did not execute, so no command output landed in context. Only
    the block message did.
  - PostToolUse block: the command did execute, but the actual stdout did not come back into context.
    Only the post-tool block message did.

  So the blocked notifications landed in context, but the blocked command payloads themselves did not.
  Nice clean guardrail, aloha.
```
2026-03-25 19:18:03 -07:00
Matthew Zeng
0b08d89304 [app-server] Add a method to override feature flags. (#15601)
- [x] Add a method to override feature flags globally and not just
thread level.
2026-03-25 02:27:00 +00:00
Ruslan Nigmatullin
301b17c2a1 app-server: add filesystem watch support (#14533)
### Summary
Add the v2 app-server filesystem watch RPCs and notifications, wire them
through the message processor, and implement connection-scoped watches
with notify-backed change delivery. This also updates the schema
fixtures, app-server documentation, and the v2 integration coverage for
watch and unwatch behavior.

This allows clients to efficiently watch for filesystem updates, e.g. to
react on branch changes.

### Testing
- exercise watch lifecycles for directory changes, atomic file
replacement, missing-file targets, and unwatch cleanup
2026-03-24 15:52:13 -07:00
xl-openai
621862a7d1 feat: include marketplace loading error in plugin/list (#15438)
Include error.
2026-03-24 11:47:23 -07:00
Andrei Eternal
73bbb07ba8 [hooks] add non-streaming (non-stdin style) shell-only PreToolUse support (#15211)
- add `PreToolUse` hook for bash-like tool execution only at first
- block shell execution before dispatch with deny-only hook behavior
- introduces common.rs matcher framework for matching when hooks are run

example run:

```
› run three parallel echo commands, and the second one should echo "[block-pre-tool-use]" as a test


• Running the three echo commands in parallel now and I’ll report the output directly.

• Running PreToolUse hook: name for demo pre tool use hook

• Running PreToolUse hook: name for demo pre tool use hook

• Running PreToolUse hook: name for demo pre tool use hook

PreToolUse hook (completed)
  warning: wizard-tower PreToolUse demo inspected Bash: echo "first parallel echo"
  
PreToolUse hook (blocked)
  warning: wizard-tower PreToolUse demo blocked a Bash command on purpose.
  feedback: PreToolUse demo blocked the command. Remove [block-pre-tool-use] to continue.

PreToolUse hook (completed)
  warning: wizard-tower PreToolUse demo inspected Bash: echo "third parallel echo"

• Ran echo "first parallel echo"
  └ first parallel echo

• Ran echo "third parallel echo"
  └ third parallel echo

• Three little waves went out in parallel.

  1. printed first parallel echo
  2. was blocked before execution because it contained the exact test string [block-pre-tool-use]
  3. printed third parallel echo

  There was also an unrelated macOS defaults warning around the successful commands, but the echoes
  themselves worked fine. If you want, I can rerun the second one with a slightly modified string so
  it passes cleanly.
```
2026-03-23 14:32:59 -07:00