Compare commits

...

200 Commits

Author SHA1 Message Date
viyatb-oai
2ab25cb2c5 refactor(permissions): lower Linux enforcement from effective filesystem permissions
Co-authored-by: Codex noreply@openai.com
2026-05-27 17:43:55 -07:00
viyatb-oai
0dc8cf86c9 refactor(permissions): lower Seatbelt from effective filesystem permissions
Co-authored-by: Codex noreply@openai.com
2026-05-27 17:41:02 -07:00
viyatb-oai
4418bce894 refactor(permissions): use effective filesystem permissions for enforcement preflight
Co-authored-by: Codex noreply@openai.com
2026-05-27 17:28:05 -07:00
viyatb-oai
e7864acdbe feat(permissions): introduce effective filesystem permissions
Co-authored-by: Codex noreply@openai.com
2026-05-27 17:08:07 -07:00
xl-openai
1de8c43467 Allow runtime enablement for remote plugins (#24707)
experimentalFeature/enablement/set now accepts remote_plugin as a
supported runtime feature key
2026-05-26 22:22:34 -07:00
efrazer-oai
8ed38fe38e fix: add noninteractive install script mode (#21567)
# Summary

The Codex standalone installers can pause after installation to ask
about an older managed install or launching Codex. That makes unattended
bootstrap and update flows hard to complete reliably.

This PR adds noninteractive installer control on macOS/Linux and Windows
through `CODEX_NON_INTERACTIVE=1`. Noninteractive operation is
environment-only, which gives automated callers one stable way to
suppress prompts. When a noninteractive install leaves an older npm,
bun, or brew-managed Codex installed, the standalone bin is configured
ahead of that command on `PATH` so the newly installed Codex is the one
future launches select. It also supports `CODEX_RELEASE` for callers
that select a release through environment variables while retaining the
existing explicit release inputs. Release selection accepts `latest`,
stable `x.y.z` versions, and Codex prereleases written as
`rust-v0.134.0-alpha.3`, `v0.134.0-alpha.3`, or `0.134.0-alpha.3`; it
validates that shape before constructing release requests.

# Stack

1. [#21567](https://github.com/openai/codex/pull/21567) - Adds release
and noninteractive environment controls to the installers. (current)
2. [#24637](https://github.com/openai/codex/pull/24637) - Runs
standalone updater installs with `CODEX_NON_INTERACTIVE=1`.
3. [#24639](https://github.com/openai/codex/pull/24639) - Removes
explicit release argument inputs in favor of `CODEX_RELEASE`.

# Evidence

| Before | After |
| --- | --- |
| ![Interactive install
prompts](https://github.com/user-attachments/assets/feecb45a-7087-4681-8775-ba57b07e97fa)
| ![Noninteractive install completes without
prompts](https://github.com/user-attachments/assets/53dcc791-383a-46e2-9a95-3b37b80ae053)
|

Environment-controlled macOS install with an existing npm-managed Codex
on `PATH`:


https://github.com/user-attachments/assets/442e0b5b-4a32-4bf5-996b-68784777380d

# Design decisions

Windows installs using the older standalone bin layout still require an
interactive migration confirmation. Noninteractive mode does not
auto-migrate that existing directory because replacing it is a
destructive transition for an early, limited-use layout; unattended
installs on that layout fail with an instruction to rerun interactively.

# Testing

Tests: installer syntax validation, release-selector acceptance and
rejection coverage including PowerShell `Latest` compatibility, macOS
live-terminal installer smoke testing with environment-controlled stable
and prerelease installation and competing PATH precedence, shell
rejection of the omitted noninteractive flag, and Windows ARM64
PowerShell smoke testing with environment-only noninteractive behavior,
retained release input, and competing PATH precedence through Parallels.
2026-05-26 22:09:54 -07:00
Adam Perry @ OpenAI
cca1e0ba1d Uprev Rust toolchain pins to 1.95.0 (#24684)
## Summary
- Bump the workspace Rust toolchain from `1.93.0` to `1.95.0` across
Cargo, Bazel, CI, release workflows, devcontainers, and the Codex
environment config.
- Refresh `MODULE.bazel.lock` so the Bazel Rust toolchain artifacts
match the new version.
- Leave purpose-specific toolchains unchanged, including the
`argument-comment-lint` nightly and the upstream `rusty_v8` `1.91.0`
build pin.
- Includes fixes for new lints from `just fix` and a few codex-authored
fixes for lints without a suggestion.
2026-05-26 20:59:47 -07:00
Anton Panasenko
64e340ad28 fix(core): instrument stalled tool-listing handoff (#24667)
## Why

When a turn needs a follow-up request after tool output is recorded,
Codex can still appear stuck in `Thinking` before the next `/responses`
request is opened. The existing local trace showed the last completed
response and the absence of a new backend request, but it did not show
whether the stall was in tool-router preparation or later request setup.

Issue: N/A (internal incident investigation)

## What Changed

Added trace spans around the pre-stream tool-router handoff in
`core/src/session/turn.rs`, including the `built_tools` phase and the
MCP manager read lock.

Added per-server MCP tool-listing spans and trace breadcrumbs in
`codex-mcp/src/connection_manager.rs` with startup snapshot /
startup-complete state so a pending MCP client is visible in feedback
logs instead of looking like a silent hang.

## Verification

- `just fmt`
- `just test -p codex-mcp`
- `just test -p codex-core` (prior full rerun fails in this workspace on
unrelated integration tests: code-mode output length expectations, one
shell timeout formatting assertion, and shell snapshot timeouts; latest
review-fix rerun compiled and passed 1160 tests before I stopped the
abnormally slow unrelated suite)
2026-05-27 02:00:40 +00:00
sayan-oai
9fe55d68e6 fix: dont compact standalone websearch schema (#24660)
add new `parse_tool_input_schema_without_compaction` to bypass the
existing compaction/trimming of client-provided tool schemas that are
over 4k bytes.

we want this for standalone web search to keep field guidance/metadata
on certain fields; this keeps us closer to parity with existing hosted
tool schema (which didnt go through this 4k byte filter).
2026-05-27 01:05:19 +00:00
pakrym-oai
0d37db4b2b [codex] Remove obsolete goal continuation turn marker (#24658)
## Why

`continuation_turn_id` was introduced to distinguish synthetic goal
continuation turns for the no-tool continuation suppression heuristic.
#20523 removed that heuristic, but left the marker behind. It is still
written and cleared without affecting any runtime decision.

## What Changed

- Remove `GoalRuntimeState::continuation_turn_id`.
- Remove the marker setter/clearer and their now-no-op start, finish,
and abort call sites.

## Testing

- Not run yet (deferred at request).
2026-05-26 17:19:02 -07:00
marksteinbrick-oai
487521733b [codex-analytics] add grouped session id to runtime events (#24655)
## Why
- Runtime analytics events report `thread_id`, which identifies the
individual thread emitting an event
- They don't report `session_id`, which identifies the shared session
for a root thread and its subagent threads
- Emitting both identifiers allows analytics to group related activity

## What Changed
- Adds `session_id` to relevant analytics events (thread_initalized,
turn, turn_steer, compaction, guardian_review)
- Tracks each thread's session ID in the analytics reducer so subsequent
thread scoped events emit the same value
- Carries the shared session ID through subagent initialization

## Verification
- `just test -p codex-analytics` validates event payloads and subagent
session grouping.
- Focused `codex-app-server` tests validate session IDs for thread,
turn, and steer events.
- Focused `codex-core` tests validate root and subagent session ID
propagation.
2026-05-26 16:38:46 -07:00
rhan-oai
dc4e54d061 Restore legacy image detail values (#24644)
## Why

Older persisted rollouts can contain `input_image.detail` values of
`auto` or `low` from before `ImageDetail` was narrowed to
`high`/`original`. Current deserialization rejects those values, which
can make resume skip later compacted checkpoints and reconstruct an
oversized raw suffix before the next compaction attempt.

Confirmed Sentry reports fixed by this compatibility path:

- [CODEX-1H3F](https://openai.sentry.io/issues/7500642496/)
- [CODEX-1H6N](https://openai.sentry.io/issues/7501025347/)
- [CODEX-1JDP](https://openai.sentry.io/issues/7504549065/)
- [CODEX-1HW6](https://openai.sentry.io/issues/7503407986/)

## Background

[openai/codex#20693](https://github.com/openai/codex/pull/20693) added
image-detail plumbing for app-server `UserInput` so input images could
explicitly request `detail: original`. The Slack discussion behind that
PR was about ScreenSpot / bridge evals where user input images were
resized, while tool output images already had MCP/code-mode ways to
request image detail.

In review, the intended new API surface was narrowed to `high` and
`original`: default to `high`, allow `original` when callers need
unchanged image handling, and avoid encouraging new `auto` or `low`
usage. That policy still makes sense for newly emitted values.

The missing compatibility piece is persisted history. Older rollouts can
already contain `auto` and `low`, and resume reconstructs typed history
by deserializing those rollout records. Rejecting old values at that
boundary causes valid compacted checkpoints to be skipped. This PR
restores `auto` and `low` as real variants so old records deserialize
and round-trip without being rewritten as `high`, while product paths
can continue to default to `high` and avoid emitting `auto` for new
behavior.

## What changed

- Restored `ImageDetail::Auto` and `ImageDetail::Low` as first-class
protocol values.
- Preserved `auto`/`low` through rollout deserialization, MCP image
metadata, code-mode image output, and schema/type generation.
- Kept local image byte handling conservative: only `original` switches
to original-resolution loading; `auto`/`low`/`high` continue through the
resize-to-fit path while retaining their detail value.
- Added regression coverage for enum round-tripping and code-mode `low`
detail handling.

## Testing

- `just write-app-server-schema`
- `just test -p codex-protocol`
- `just test -p codex-tools`
- `just test -p codex-code-mode`
- `just test -p codex-app-server-protocol`
- `just test -p codex-core
suite::rmcp_client::stdio_image_responses_preserve_original_detail_metadata`
- `just test -p codex-core
suite::code_mode::code_mode_can_use_mcp_image_result_with_image_helper`
- Loaded broken rollouts on local fixed builds, and started/completed
new turns.

I also attempted `just test -p codex-core`; the local broad run did not
finish green: 2559 tests run, 2467 passed, 55 flaky, 91 failed, 1 timed
out. The failures were broad timeout/deadline failures across unrelated
areas; targeted changed-path core tests above passed.
2026-05-26 16:24:33 -07:00
iceweasel-oai
9826581e7b Attach Windows sandbox log to feedback reports (#24623)
## Why

Windows sandbox diagnostics are currently hard to recover from
`/feedback` even though they are often the most useful artifact when
debugging sandbox behavior. Now that sandbox logging uses daily rolling
files, feedback can safely include the current day's sandbox log without
uploading the old ever-growing legacy `sandbox.log`.

## What changed

- Add a `codex-windows-sandbox` helper that resolves the current daily
sandbox log from `codex_home`.
- When feedback is submitted with logs enabled on Windows, app-server
attaches today's sandbox log if it exists.
- Upload the attachment under the stable filename `windows-sandbox.log`,
independent of the dated on-disk filename.
- Keep existing raw `extra_log_files` behavior unchanged for rollout and
desktop log attachments.

## Verification

- `cargo fmt -p codex-app-server -p codex-windows-sandbox`
- `cargo test -p codex-windows-sandbox
current_log_file_path_for_codex_home_uses_sandbox_dir`
- `cargo test -p codex-app-server
windows_sandbox_log_attachment_uses_current_log`
- Manual CLI/TUI `/feedback` test confirmed Sentry received
`windows-sandbox.log`.
2026-05-26 15:59:25 -07:00
pakrym-oai
46391f7efa [codex] remove plain image wrapper spans (#24652)
## Why

Remote image submissions currently wrap native `input_image` spans in
literal `<image>` and `</image>` text spans. Those extra prompt tokens
add structure without providing label or routing information.

## What Changed

- Serialize `UserInput::Image` directly as an `input_image` content
span.
- Preserve named local-image framing and legacy wrapper parsing for
labeled attachments and existing histories.
- Update existing request-shape expectations for drag-and-drop images,
model switching, and compaction.

## Validation

- `just test -p codex-protocol`
- Focused `codex-core` run covering
`drag_drop_image_persists_rollout_request_shape`,
`model_change_from_image_to_text_strips_prior_image_content`, and
`snapshot_request_shape_pre_turn_compaction_including_incoming_user_message`

## Notes

- A broader `just test -p codex-core` run was attempted; the affected
tests passed, while the overall run failed in unrelated CLI, MCP, and
tooling tests plus a `thread_manager` timeout.
2026-05-26 15:49:37 -07:00
Michael Bolin
0a6bc4e687 windows-sandbox: remove SandboxPolicy runner plumbing (#23813)
## Why

The Windows sandbox runner still carried the old `SandboxPolicy`
compatibility path even though core now computes `PermissionProfile`.
That meant Windows command-runner execution could only see the legacy
projection, so profile-only filesystem rules such as deny globs were not
part of the runner input.

## What Changed

- Removed the Windows-local `SandboxPolicy` parser/export and deleted
`windows-sandbox-rs/src/policy.rs`.
- Changed restricted-token capture/session setup, elevated setup,
world-writable audit, read-root grant, and command-runner session APIs
to accept `PermissionProfile` plus the profile cwd.
- Bumped the elevated command-runner IPC protocol to version 2 because
`SpawnRequest` now carries `permission_profile` /
`permission_profile_cwd` instead of the legacy `policy_json_or_preset` /
`sandbox_policy_cwd` fields.
- Updated core exec, unified exec, debug-sandbox, TUI setup/grant flows,
and app-server setup to pass the actual effective `PermissionProfile`.
- Left regression coverage asserting the old IPC policy fields are
absent and the runner serializes tagged `PermissionProfile` JSON.

## Verification

- `cargo test -p codex-windows-sandbox`
- `cargo test -p codex-core windows_sandbox`
- `cargo test -p codex-app-server
request_processors::windows_sandbox_processor`
- `just fix -p codex-windows-sandbox -p codex-core -p codex-app-server
-p codex-cli -p codex-tui`
- `just fix -p codex-cli -p codex-tui`
- `just fix -p codex-windows-sandbox -p codex-tui`
- `rg "\\bSandboxPolicy\\b" codex-rs/windows-sandbox-rs` returned no
matches.

Note: `cargo test -p codex-cli` was attempted but did not reach crate
tests because local disk filled while compiling dependencies (`No space
left on device`). The targeted clippy pass compiled the affected CLI/TUI
surfaces afterward.




---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23813).
* #24108
* __->__ #23813
2026-05-26 14:56:27 -07:00
Eric Traut
414561294c Avoid repeated marketplace upgrades for alternate layouts (#24320)
Fixes #24249.

## Why

Codex already supports discovering marketplaces under both
`.agents/plugins/marketplace.json` and
`.claude-plugin/marketplace.json`. The Git marketplace auto-upgrade
no-op check only looked for the `.agents` layout. That meant an
installed `.claude-plugin` marketplace with matching revision metadata
still looked absent, so plugin list/startup upgrade work could stage and
re-activate the same marketplace again.

That matches the failure shape in #24249: the report called out repeated
marketplace sync/cache refresh logs and a large recently-touched
`.tmp/marketplaces/.staging` directory. This change makes the
auto-upgrade path recognize the installed `.claude-plugin` marketplace
as already current, which should remove that staging/activation feedback
loop.

## What changed

`codex-rs/core-plugins/src/marketplace_upgrade.rs` now uses the existing
supported marketplace manifest discovery helper when deciding whether an
installed Git marketplace is already current. Existing local plugin
source validation is unchanged; `source: "./"` still remains invalid.

## Confidence

Confidence is high that this fixes the repeated marketplace upgrade
path: the old hardcoded layout check was definitely wrong for installed
`.claude-plugin` marketplaces, and the reported staging churn points
directly at that path.

Confidence is not 100% because we do not have a CPU profile or a fully
re-run reporter repro. A malformed marketplace entry can still be logged
as invalid if another caller repeatedly lists plugins; this PR fixes the
staging/upgrade feedback loop that likely made the failure pathological,
not every possible source of repeated marketplace resolution.
2026-05-26 14:40:06 -07:00
Eric Traut
22e45014a2 TUI config cleanup: plugin mentions (#24266)
## Summary

TUI plugin mention refresh still joined app-server plugin inventory with
client-local plugin config, which can diverge once plugin state is owned
by the app server.

This changes the TUI to mirror the GUI client: `plugin/list` is the
autocomplete source, and mention candidates are plugin-level entries
filtered to installed, enabled, and not disabled by admin. The TUI no
longer reads local plugin config or calls `plugin/read` while refreshing
plugin mention candidates.

## API shape and limitations

The current app-server API does not expose effective per-session plugin
capability summaries for mention autocomplete. As in the GUI,
autocomplete now trusts `plugin/list` metadata rather than proving which
plugin capabilities are loaded in the active session.

That avoids stale client-local reads and the cwd/remote detail gaps in
`plugin/read`, but intentionally accepts the same list-level tradeoff as
the app: if `plugin/list` reports a remote plugin before its local
bundle is materialized, the plugin can still appear as a mention
candidate.
2026-05-26 14:34:02 -07:00
Curtis 'Fjord' Hawthorne
675cb1afbd Clarify view_image tool description (#23949) 2026-05-26 14:17:43 -07:00
sayan-oai
66ff8b0f54 make direct only allowed caller for standalone websearch (#24646)
only allow `Direct` callers of the standalone websearch tool because its
not supported in codemode
2026-05-26 21:05:40 +00:00
Owen Lin
1911021c0e Add forked_from_thread_id turn metadata (#24160)
## Why

When Codex calls responsesapi, we currently send `session_id`,
`thread_id`, and `turn_id` among other things as
`client_metadata["x-codex-turn-metadata"]`. This PR adds
`forked_from_thread_id` which helps explain the "lineage" of a forked
thread.

## What's changed

- Track the immediate history source copied into a forked thread through
thread/session creation, including subagent and review turn metadata
paths.
- Include `forked_from_thread_id` in Codex turn metadata while
preventing turn-scoped Responses API client metadata from overwriting
Codex-owned lineage fields.
- Add coverage for fork lineage in turn metadata and the app-server
Responses API request path.
2026-05-26 14:05:28 -07:00
Eric Traut
5cd9b8086a Respect resume cwd overrides for idle cached threads (#24528)
Fixes #24186.

## Why
When the TUI resumes a thread through the local app-server daemon with a
selected workspace, `thread/resume` can hit an already-loaded but idle
cached thread. That path previously rejoined the cached `CodexThread`,
so cwd/config overrides in `ThreadResumeParams` were ignored and the
resumed session kept using the old cwd.

## What changed
App-server now treats a loaded-but-idle thread with no subscribers as a
cache entry when resume overrides differ: it unloads that cached thread
and lets the normal resume path rebuild it with the requested
cwd/config. Threads that still have subscribers, or active runtime work,
continue to rejoin the existing loaded thread so in-flight state remains
observable.

The existing thread teardown helper was generalized from
archive-specific cleanup to shared unload cleanup for this path.
2026-05-26 13:50:44 -07:00
Anton Panasenko
3da89d4831 fix(remote-control): surface websocket task stalls (#24473)
## Why

When the app-server remote-control websocket path stalls during
connection setup or teardown, the existing logs do not show where the
task stopped, and several awaits can keep the task from returning
promptly. That makes offline or stale-host incidents hard to distinguish
from expected shutdown or disable flow.

Issue: N/A (internal incident investigation)

## What Changed

Added structured lifecycle and status logging around remote-control
enable/disable requests, websocket task startup and exit, connection
cycles, enrollment context, and status/environment transitions.

Bound websocket connect, transport-event forwarding, and
connection-worker shutdown waits. On timeout, the code logs the stalled
operation and stops or aborts workers so the loop can reconnect or exit
instead of waiting indefinitely. Ping sends now also observe shutdown
cancellation.
2026-05-26 13:17:58 -07:00
pakrym-oai
768848ab6f Add experimental turn additional context (#24154)
## Summary

Adds experimental `additionalContext` support to `turn/start` and
`turn/steer` so clients can provide ephemeral external context, such as
browser or automation state, without turning that plumbing into a
visible user prompt or triggering user-prompt lifecycle behavior.

## API Shape

The parameter shape is:

```ts
additionalContext?: Record<string, {
  value: string
  kind: "untrusted" | "application"
}> | null
```

Example:

```json
{
  "additionalContext": {
    "browser_info": {
      "value": "Active tab is CI failures.",
      "kind": "untrusted"
    },
    "automation_info": {
      "value": "CI rerun is in progress.",
      "kind": "application"
    }
  }
}
```

The keys are opaque and caller-defined.

## Context Injection

When provided, accepted entries are inserted into model context as
hidden contextual message items, not as visible thread user-message
items.

`kind: "untrusted"` entries are inserted with role `user`:

```text
<external_${key}>${value}</external_${key}>
```

`kind: "application"` entries are inserted with role `developer`:

```text
<${key}>${value}</${key}>
```

Values are not escaped. Each value is truncated to 1k approximate tokens
before wrapping.

For `turn/start`, accepted additional context is inserted before normal
user input. For `turn/steer`, additional context is merged only when the
steer includes non-empty user input; context-only steers still reject as
empty input.

## Dedupe Strategy

`AdditionalContextStore` lives on session state and stores the latest
complete additional-context map.

Each `turn/start` or non-empty `turn/steer` treats its
`additionalContext` as the current complete set of values. Entries are
injected only when the key is new or the exact entry for that key
changed, including `value` or `kind`. After merging, the store is
replaced with the provided map, so omitted keys are removed from the
retained set and can be injected again later if reintroduced.

Omitting `additionalContext`, passing `null`, or passing an empty object
resets the store to empty and injects nothing.

## What Changed

- Threads experimental v2 `additionalContext` through app-server into
core turn start and steer handling.
- Adds separate contextual fragment types for untrusted user-role
context and application developer-role context.
- Uses pending response input items so additional context can be
combined with normal user input without treating it as prompt text.
- Adds integration coverage for start/steer flow, role routing,
dedupe/reset behavior, deletion/re-add behavior, hook-blocked input
behavior, empty context-only steer rejection, external-fragment marker
matching, and truncation.
2026-05-26 13:02:34 -07:00
canvrno-oai
cd934c8bcb tui: keep inaccessible apps out of mentions (#24625)
## Summary

Fix the TUI `$` app mention paths so App Directory rows that are not
accessible are not treated as usable apps.

This includes the core preservation fix from #24104, but expands it to
the other app mention paths:

- preserve app-server `is_accessible` flags when partial
`app/list/updated` snapshots reach the TUI
- require apps to be both accessible and enabled when resolving exact
`$slug` mentions
- require restored/stale `app://...` bindings to point at accessible,
enabled apps before emitting structured app mentions
- remove the now-unused `codex-chatgpt` dependency from `codex-tui`,
which addresses the `cargo shear` failure seen on #24104

## Root Cause

The app server already sends merged app snapshots with accessibility
computed. The TUI handled app-server app list updates as partial app
loads and re-ran the old accessible-app merge path. That path treated
every notification row as accessible, so App Directory entries with
`isAccessible=false` could appear in `$` suggestions.

Regression source: #22914 routed app-list updates through the app server
while reusing the old TUI partial-load handling. Related precursor:
#14717 introduced the partial-load path, but #22914 made it user-visible
for app-server updates.

## Issues

Fixes #24145
Fixes #24205
Fixes #24319

## Validation

- `just fmt`
- `git diff --check`
- `just bazel-lock-update`
- `just bazel-lock-check`
- `just argument-comment-lint -p codex-tui`
- `just test -p codex-tui
chatwidget::tests::popups_and_settings::apps_notification_update_excludes_inaccessible_apps_from_mentions
chatwidget::tests::composer_submission::submit_user_message_ignores_inaccessible_app_mentions_from_bindings
chatwidget::skills::tests::find_app_mentions_requires_accessible_enabled_apps_for_bound_paths
chatwidget::skills::tests::find_app_mentions_requires_accessible_enabled_apps_for_slugs`
2026-05-26 12:09:07 -07:00
Felipe Coury
833c19ed53 fix(tui): keep raw output above composer in zellij (#24593)
## Why

Raw output mode intentionally sends logical source lines to the terminal
without Codex-inserted wrapping so copied content retains its original
line structure. In Zellij, soft-wrapped continuation rows from those raw
lines are not confined by the inline history scroll region. When raw
mode replays a long transcript, continuation rows can occupy the
composer viewport and are overwritten on the following draw, leaving the
transcript visibly truncated underneath the composer.

This is specific to the combination of Zellij and raw terminal-wrapped
history. Rich output and non-Zellij terminals should continue using the
existing insertion behavior.

Related context: #20819 introduced raw output mode, and #22214 removed
the broad Zellij insertion workaround after the standard rich-output
path no longer required it.

| Before | After |
|---|---|
| <img width="1728" height="916" alt="image"
src="https://github.com/user-attachments/assets/f85398a5-e930-46d9-bcfd-106a24c41466"
/> | <img width="1723" height="912" alt="image"
src="https://github.com/user-attachments/assets/5c62e16a-a6e5-4842-bcb2-eab163cda04c"
/> |

## What Changed

- Cache Zellij detection in `Tui` and select a dedicated insertion mode
only for `HistoryLineWrapPolicy::Terminal` batches in Zellij.
- For that guarded path, clear the existing viewport, append raw source
lines through the terminal so its soft wrapping remains
selection-friendly, and reserve empty viewport rows before redrawing the
composer.
- Add snapshot regressions for both an incremental soft-wrapped raw
insert and an overflowing raw transcript replay that starts at the top
of the cleared terminal.

## How to Test

1. Start Codex inside Zellij with raw output enabled or toggle raw
output after a multiline response is in history.
2. Produce or replay output containing long logical lines, such as a
fenced shell command with several wrapped lines.
3. Confirm the wrapped history remains visible above the composer and
the composer no longer overwrites the end of the response.
4. Toggle back to rich output or run outside Zellij and confirm standard
history rendering still behaves normally.

Targeted tests run:

- `just test -p codex-tui vt100_zellij_raw -- --nocapture`

Additional validation notes:

- `just test -p codex-tui` was attempted; the two new Zellij raw
insertion tests passed, while two existing
`app::tests::update_feature_flags_disabling_guardian_*` tests failed
outside this history insertion path.
- `just argument-comment-lint` was attempted but local Bazel analysis
fails before reaching the changed source because the LLVM `compiler-rt`
package is missing `include/sanitizer/*.h`. Modified literal callsites
were inspected manually.
2026-05-26 16:08:45 -03:00
sayan-oai
a22706dfae standalone websearch extension (#23823)
## Summary

Add the extension-backed standalone `web.run` tool so Codex can call the
standalone search endpoint through the `codex-api` search client and
return its encrypted output to Responses.

- gate the new tool behind `standalone_web_search`
- install the extension in the app-server thread registry and hide
hosted `web_search` when standalone search is enabled for OpenAI
providers so the two paths stay mutually exclusive
- build search context from persisted history using a small tail
heuristic: previous user message, assistant text between the last two
user turns capped at about 1k tokens, and current user message

## Test Plan

- `cargo test -p codex-web-search-extension`
- `cargo test -p codex-api`
- `cargo test -p codex-core
hosted_tools_follow_provider_auth_model_and_config_gates`
2026-05-26 11:12:24 -07:00
jif-oai
aad59a0916 Move memory state to a dedicated SQLite DB (#24591)
## Summary

Generated memory rows and their stage-one/stage-two job state currently
live in `state_5.sqlite` alongside thread metadata. That makes memory
cleanup and regeneration share the main state schema even though those
rows are memory-pipeline data and can be rebuilt independently from the
durable thread records.

This PR moves the memory-owned tables into a dedicated
`memories_1.sqlite` runtime database while keeping thread metadata in
`state_5.sqlite`.

## Changes

- Adds a separate memories DB runtime, migrator, path helpers, telemetry
kind, and Bazel compile data for `state/memory_migrations`.
- Introduces `MemoryStore` behind `StateRuntime::memories()` and moves
memory table/job operations onto that store.
- Drops the old memory tables from the state DB and recreates their
schema in `state/memory_migrations/0001_memories.sql`.
- Updates memory startup, citation usage tracking, rollout pollution
handling, `debug clear-memories`, and app-server `memory/reset` to
operate through the memories DB.
- Preserves cross-DB behavior by hydrating thread metadata from the
state DB when selecting visible memory outputs and checking stage-one
staleness.

## Verification

- Added/updated `codex-state` tests for deleted-thread memory visibility
and already-polluted phase-two enqueue behavior.
- Updated `debug clear-memories`, app-server `memory/reset`, and
memories startup tests to seed and assert memory rows through
`memories_1.sqlite`.
2026-05-26 20:07:25 +02:00
jif-oai
823381e867 fix: restore goal accounting after thread resume (#24626)
## Why

Goal idle accounting is supposed to survive a thread resume. Previously,
the resume hook restored the active goal state inline from the extension
lifecycle contributor, which left the runtime handle without a reusable
restoration path and made the behavior hard to cover directly. When a
thread with an active goal was resumed, goal accounting could lose track
of the active idle goal instead of continuing to accrue elapsed time.

## What changed

- Moved thread-resume restoration into
`GoalRuntimeHandle::restore_after_resume()` so the runtime owns
rehydrating active goal accounting from persisted thread goal state.
- Kept disabled goal runtimes as a no-op and preserved the existing
warning path when persisted goal state cannot be loaded.
- Added a backend regression test that seeds an active goal, resumes the
thread, waits briefly, and verifies elapsed idle time is reflected on
the next external goal mutation.

## Testing

- Not run locally; this metadata update only rewrote the PR title/body.
2026-05-26 20:01:13 +02:00
Felipe Coury
8a4a537e44 fix(tui): avoid modifyOtherKeys for unknown tmux formats (#24371)
## Why

Codex 0.131 started enabling tmux `modifyOtherKeys` mode 2 when the
active tmux session reported `extended-keys-format csi-u`, and also when
that format could not be queried. The fallback was meant to help
compatible tmux panes enter extended-key mode, but it breaks iTerm2
control-mode sessions on older tmux.

Issue #23711 reproduces with:

```bash
ssh -t ubuntu@192.168.68.149 'tmux -CC new -A -s main'
```

On tmux 3.2a, `extended-keys-format` is not available. With mode 2
enabled, `Ctrl-C` is delivered as `^[[27;5;99~` instead of the normal
interrupt/control key path, so Codex does not handle it. Running with
`CODEX_TUI_DISABLE_KEYBOARD_ENHANCEMENT=1` restores `Ctrl-C`, which
points at keyboard mode setup rather than chat input routing.

## What Changed

- Only request `modifyOtherKeys` mode 2 when tmux explicitly reports
`extended-keys-format csi-u`.
- Treat an unknown or unavailable tmux extended-key format as
unsupported for this mode.
- Update the keyboard mode unit coverage so `None` no longer opts into
`modifyOtherKeys`.

This preserves the explicit modern tmux `csi-u` path from #21943 while
avoiding the unsafe fallback on older or unqueryable tmux setups.

## How to Test

Regression path from #23711:

1. Start iTerm2 tmux integration against an older tmux host:
   ```bash
   ssh -t ubuntu@192.168.68.149 'tmux -CC new -A -s main'
   ```
2. Start patched Codex.
3. Run `/keymap debug`, press a regular key, then press `Ctrl-C`.
4. Confirm `Ctrl-C` closes the inspector and Codex remains responsive
without `CODEX_TUI_DISABLE_KEYBOARD_ENHANCEMENT=1`.
5. Confirm `Shift+Enter` still inserts a newline in the same session.

Modern tmux compatibility path:

1. Start an ordinary tmux 3.6a server with explicit `csi-u`:
   ```bash
   tmux -L codex-csiu -f /dev/null new-session -d -s repro
   tmux -L codex-csiu set-option -g extended-keys on
   tmux -L codex-csiu set-option -g extended-keys-format csi-u
   tmux -L codex-csiu attach -t repro
   ```
2. Start patched Codex.
3. From another terminal, confirm the Codex pane reports `mode=Ext 2`:
   ```bash
tmux -L codex-csiu list-panes -a -F '#{pane_id} mode=#{pane_key_mode}
cmd=#{pane_current_command}'
   ```
4. Type `one`, press `Shift+Enter`, type `two`, and confirm the composer
shows two lines without submitting.
5. Press `Ctrl-C` and confirm Codex handles it normally.

Targeted tests:

- `./tools/argument-comment-lint/run.py -p codex-tui -- --lib`
- `just test -p codex-tui` runs the new keyboard mode test successfully;
the full run currently reports two unrelated guardian feature-flag test
failures:
-
`app::tests::update_feature_flags_disabling_guardian_clears_manual_review_policy_without_history`
-
`app::tests::update_feature_flags_disabling_guardian_clears_review_policy_and_restores_default`

No documentation update is needed.
2026-05-26 14:54:38 -03:00
jif-oai
08504e86fb Add goal extension telemetry parity (#24615)
## Why

`core/src/goals.rs` already emits OTEL metrics for goal creation,
resume, terminal transitions, token counts, and duration. As `/goal`
moves into `ext/goal`, the extension needs to preserve that telemetry
contract instead of only emitting app-visible `ThreadGoalUpdated`
events.

This keeps the existing `codex.goal.*` metric surface intact while goal
lifecycle ownership shifts toward the extension.

## What changed

- Added an extension-local `GoalMetrics` helper that records the
existing `codex.goal.*` counters and histograms through `codex-otel`.
- Threaded an optional `MetricsClient` through `install_with_backend`,
`GoalExtension`, `GoalRuntimeHandle`, and `GoalToolExecutor`.
- Emitted created, resumed, and terminal goal metrics from the extension
paths that create goals, restore active goals on thread resume, account
budget limits, complete or block goals, and handle external goal
mutations.
- Updated existing goal extension test setup callsites to pass `None`
for metrics when instrumentation is not under test.

## Verification

Not run locally.
2026-05-26 19:48:32 +02:00
canvrno-oai
db9cb04fb6 Move slash input logic out of chat composer (#23964)
Recent composer cleanups split state ownership out of `ChatComposer`,
but slash-command handling still mixed parsing, popup coordination,
completion, submission validation, queue behavior, and argument element
rebasing into the main composer file. Pending changes to slash command
parsing and selection inspired this code move to prevent
`chat_composer.rs` bloat.

This is just a refactor, no functional or behavioral changes are
intended.

## What changed

- Move slash-command parsing and lookup helpers into
`bottom_pane/chat_composer/slash_input.rs`.
- Move slash popup key handling, command-name completion, and popup
construction into the slash input helper module.
- Centralize bare-command, inline-args, submission-validation, and
queued-input action selection behind slash-specific helpers.
- Move command argument text-element rebasing into the slash input
module so inline command submission keeps the same element behavior with
less composer-local logic.

## Verification

- `just fmt`
- `just test -p codex-tui`
- `cargo insta pending-snapshots -p codex-tui`
2026-05-26 10:29:15 -07:00
pakrym-oai
6937e8354a Remove reserved namespaces dedup (#24609)
Avoid suffixing reserved namespaces.
2026-05-26 09:57:05 -07:00
jif-oai
9f47e19b21 test: clean up apply_patch allow-session artifact (#24611)
## Why

The
`approving_apply_patch_for_session_skips_future_prompts_for_same_file`
integration test writes `apply_patch_allow_session.txt` under the
process cwd while exercising outside-workspace patch approval behavior.
With `just test` now being the normal validation path, that file can be
left behind in the checkout when the test runs or fails, creating
confusing untracked state.

## What changed

- Registers the resolved `apply_patch_allow_session.txt` path with
`tempfile::TempPath` before the test removes and recreates it through
`apply_patch`.
- Preserves the existing outside-workspace path shape so the approval
behavior under test does not change.
- Lets `TempPath` remove the generated file when the test exits,
including panic paths.

## Verification

- `just test -p codex-core --test all
approving_apply_patch_for_session_skips_future_prompts_for_same_file`
2026-05-26 18:54:59 +02:00
jif-oai
9271e84b79 feat: add manual and remote_v2 tags to compaction metric (#24608)
## Why
`codex.task.compact` only distinguished `local` vs `remote`, which made
it hard to answer simple counter questions in Statsig. Manual `/compact`
and automatic compaction were collapsed together, and the legacy remote
path was also collapsed with `remote_compaction_v2`.

## What Changed
- route `codex.task.compact` through a shared helper in
`core/src/tasks/mod.rs`
- add a `manual=true|false` tag so manual and automatic compaction can
be counted separately
- split the remote tag into `remote` and `remote_v2`
- emit the metric from the inline auto-compaction path in
`core/src/session/turn.rs` as well as the manual `CompactTask` path in
`core/src/tasks/compact.rs`
- add focused unit coverage for the new tag shapes in
`core/src/tasks/mod_tests.rs`

## Verification
- added unit coverage in `core/src/tasks/mod_tests.rs` covering manual
`remote_v2` tags and automatic `local` tags
2026-05-26 18:47:42 +02:00
viyatb-oai
f6fd753039 tui: add named permission profile picker (#21559)
## Why

Users who opt into named permission profiles through
`default_permissions` or `[permissions.*]` should stay in named-profile
semantics when they open `/permissions`. The legacy picker rewrites
those users into anonymous preset state, which loses the active profile
identity and hides custom configured profiles.

## What changed

- Switch `/permissions` to a profile-aware picker when profile mode is
active.
- Show friendly built-in labels instead of raw `:` profile syntax.
- Include configured custom profiles and their descriptions in the
picker.
- Route selections through the split TUI profile-selection flow below
this PR.
- Add TUI snapshots and regression coverage for built-ins, custom
profiles, and conflicting legacy runtime overrides.

## Stack

1. [#22931](https://github.com/openai/codex/pull/22931):
runtime/session/network propagation for active permission profiles.
2. [#23708](https://github.com/openai/codex/pull/23708): TUI selection
plumbing and guardrail flow.
3. **This PR**: profile-aware `/permissions` menu and custom profile
display.

## UX impact

In profile mode, `/permissions` shows the same human-facing built-ins
users already know:

```text
Default
Auto-review
Full Access
Read Only
locked-down
web-enabled
```

Selecting `locked-down` keeps `active_permission_profile =
Some("locked-down")`; selecting a built-in keeps the friendly label
while switching to its named built-in profile.

## Screenshots

Live `$test-tui` smoke screenshots uploaded through GitHub attachments:

**Profile mode with built-ins and custom profiles**

<img width="832" alt="Profile mode permissions picker with custom
profiles"
src="https://github.com/user-attachments/assets/58b72431-418c-4839-9e39-575076db4c8f"
/>

**Legacy mode remains anonymous preset picker**

<img width="1232" alt="Legacy permissions picker"
src="https://github.com/user-attachments/assets/95f413ab-4cee-411c-9afb-92580a885c97"
/>

<img width="1296" height="906" alt="image"
src="https://github.com/user-attachments/assets/ea381a78-9904-4aa2-828f-b7f2e43f60f2"
/>

<img width="705" height="207" alt="Screenshot 2026-05-18 at 2 58 00 PM"
src="https://github.com/user-attachments/assets/2fa6dd71-0296-449e-a6de-a72d78a1cb70"
/>

## Validation

- `git diff --cached --check` before commit.
- Full test run skipped at the user request while pushing the split
stack.
2026-05-26 16:39:55 +00:00
jif-oai
ef6528c6c7 feat: gate dedicated memories tools in config (#24600)
## Why

The memories extension already has dedicated `list`, `read`, `search`,
and `add_ad_hoc_note` tools, but app-server registration was still
disabled. The memories app collaborator needs an explicit config switch
so those native extension tools can be exposed intentionally, without
making ordinary memory prompt usage automatically register the dedicated
tool surface.

## What changed

- Added `[memories].dedicated_tools`, defaulting to `false`, to
`MemoriesToml` / `MemoriesConfig`.
- Regenerated `core/config.schema.json` for the new setting.
- Registered the memories extension as a `ToolContributor`, while
keeping tool contribution gated on both memories being enabled and
`dedicated_tools = true`.
- Added tests for the disabled default, the enabled dedicated-tools
path, and installer registration.

## Verification

- `just test -p codex-config -p codex-memories-extension`
2026-05-26 18:18:58 +02:00
Eric Traut
b84c5898df tui: include exec sessions in resume list (#24503)
## Why

Fixes #24502.

`codex resume --include-non-interactive` should include sessions created
by `codex exec`, but the TUI was sending no `sourceKinds` filter to
`thread/list` for that mode. `thread/list` treats omitted or empty
`sourceKinds` as interactive-only (`cli`, `vscode`), so exec sessions
were still filtered out.

## What Changed

- Added a shared TUI `resume_source_kinds` helper so both resume lookup
paths always pass explicit `sourceKinds` to `thread/list`.
- Kept the default resume behavior scoped to `cli` and `vscode`.
- Made `--include-non-interactive` include `exec` and `appServer`
sessions, while continuing to exclude subagent and unknown sources.

## Verification

Added focused coverage for both affected TUI request builders:

- `latest_session_lookup_params_can_include_non_interactive_sources`
- `remote_thread_list_params_can_include_non_interactive_sources`
2026-05-26 08:27:10 -07:00
pakrym-oai
ff7513cd83 Move MCP tool naming mode into manager (#21576)
## Why

The `non_prefixed_mcp_tool_names` feature should be applied where MCP
tools become model-visible, not by remapping names later in core.
Keeping the decision in `McpConnectionManager` construction makes
`ToolInfo` the single shaped view that spec building, deferred tool
search, routing, and unavailable-tool placeholders can consume directly.

This also preserves the existing external behavior while the feature is
off, and keeps the feature-on behavior for code mode and hooks explicit
at the manager boundary.

## What Changed

- Add `McpToolNameMode` to `codex-mcp` and flow it through `McpConfig`
into `McpConnectionManager::new`.
- Normalize MCP `ToolInfo` names in the manager using either
legacy-prefixed namespaces or non-prefixed namespaces; the legacy path
adds `mcp__` without restoring the old trailing namespace suffix.
- Remove the core-side MCP name remapping path so specs, tool search,
session resolution, and unavailable-tool placeholder construction use
the manager-provided `ToolName` values directly.
- Keep code mode flattening on the `__` namespace separator.
- Preserve hook compatibility by giving non-prefixed MCP hook names
legacy `mcp__...` matcher aliases.
- Add/adjust integration and unit coverage for non-prefixed code-mode
behavior, hook matching with the feature on and off, and manager-level
legacy prefixing.

## Testing

- `cargo test -p codex-mcp --lib`
- `cargo test -p codex-core --lib tools::spec::tests -- --nocapture`
- `cargo test -p codex-core --lib mcp_tools -- --nocapture`
- `cargo test -p codex-core --lib mcp_tool_exposure -- --nocapture`
- `cargo test -p codex-core --test all mcp_tool -- --nocapture`
- `cargo test -p codex-core --test all search_tool -- --nocapture`
- `cargo test -p codex-core --test all hooks_mcp -- --nocapture`
- `cargo test -p codex-core --test all
code_mode_uses_non_prefixed_mcp_tool_names_when_feature_enabled --
--nocapture`
- `cargo test -p codex-tools`
- `cargo test -p codex-features`
2026-05-26 08:21:15 -07:00
pakrym-oai
b637fd26aa [codex] Make active turn task singular (#24105)
## Why

`ActiveTurn` already runs at most one task: starting a task requires
that no task is present, and replacement aborts existing work first.
Representing that state as an `IndexMap` leaves a multi-task shape for a
single-task invariant and makes each lifecycle lookup operate like a
collection lookup.

The slot remains optional because goal continuation uses an empty active
turn as a reservation while deciding whether to start continuation work.

## What changed

- Replace `ActiveTurn.tasks` with `task: Option<RunningTask>`.
- Update task abort/completion, session lookup and steering, input-queue
matching, goal reservation, and network-approval lookup to operate on
the singular slot.
- Mutate the singular task slot directly instead of retaining
collection-era add/remove/take helpers.
- Record token usage on the completing active task span without a
regular-task-only opt-in flag.

## Validation

- `cargo test -p codex-core --lib session::tests::steer_input`
- `cargo test -p codex-core --lib
session::tests::abort_empty_active_turn_preserves_pending_input`
- `cargo test -p codex-core --lib
session::tests::queued_response_items_for_next_turn_move_into_next_active_turn`
- `cargo test -p codex-core --lib
session::tests::active_goal_continuation_runs_again_after_no_tool_turn`
- `cargo test -p codex-core --lib
session::tests::abort_regular_task_emits_turn_aborted_only`
- `cargo test -p codex-core --lib session::input_queue::tests`
2026-05-26 08:20:58 -07:00
Eric Traut
0f91e869bd Use thread config for TUI MCP inventory (#24532)
## Summary
`/mcp` in the TUI should reflect the current loaded thread, including
project-local MCP servers from that thread config. Before this change,
`mcpServerStatus/list` only read the latest global MCP config, so the
active chat could miss project-local servers.

This adds optional `threadId` to `mcpServerStatus/list`. When present,
app-server resolves the loaded thread and lists MCP status from the
refreshed effective config for that thread; when omitted, existing
global config behavior stays unchanged.

The TUI now sends the active chat thread id for `/mcp` and `/mcp
verbose`, carries that origin through the async inventory result, and
ignores stale completions if the user has switched threads before the
fetch returns. The app-server schemas were regenerated.

## Follow-up
Once this app-server API change lands, the desktop app should make the
same `threadId` plumbing so its MCP inventory also uses the current
thread config.

Fixes #23874
2026-05-26 07:44:04 -07:00
jif-oai
c4e53d103c Wire app-server extension event sink (#24586)
## Why

The goal extension already emits `ThreadGoalUpdated` events, but
production app-server thread extensions were built with the default
no-op extension event sink. That meant extension-driven goal updates
could be produced without ever reaching app-server clients.

## What changed

- Build app-server thread extensions with a host-provided
`ExtensionEventSink`.
- Add an app-server sink that converts extension `ThreadGoalUpdated`
events into `ServerNotification::ThreadGoalUpdated` broadcasts.
- Use the existing bounded outgoing message channel via `try_send` so
event forwarding cannot create an unbounded queue.
- Pass `NoopExtensionEventSink` in app-server tests that construct a
`ThreadManager` without an app-server host.
- Refresh `Cargo.lock` for the existing `codex-memories-extension`
`codex-otel` dependency.

## Verification

- `just test -p codex-app-server
extensions::tests::app_server_event_sink_forwards_thread_goal_updates`
2026-05-26 15:28:02 +02:00
jif-oai
01a8bf0ae3 Add memory tool call metrics to memories extension (#24583)
## Why

The memories extension now receives a metrics exporter, but the useful
extension-owned signal is the memory tool call itself: which operation
ran, which memory area it touched, whether the backend call succeeded,
and whether the result was truncated.

## What changed

- Added the `codex.memories.tool.call` counter in
`ext/memories/src/metrics.rs`.
- Emit that counter from `memories/add_ad_hoc_note`, `memories/list`,
`memories/read`, and `memories/search` after backend execution.
- Tag each call with `tool`, `operation`, `scope`, `status`, and
`truncated`.
- Pass the existing `MetricsClient` through the memories extension into
the tool executors; tests use `None`.

## Verification

- `just test -p codex-memories-extension`
2026-05-26 15:27:51 +02:00
jif-oai
b77be36896 fix: drop flake (#24588)
Dropping already commented out stuff
2026-05-26 15:07:26 +02:00
jif-oai
c37884d5eb Wire metrics client into memories extension (#24567)
## Summary

- let the memories extension capture the process-global OTEL metrics
client at install time
- keep app-server/TUI/exec extension construction APIs unchanged
- store the metrics client for future memory metrics without emitting
any metrics yet

## Test plan

- `just fmt`
- `just bazel-lock-update`
- `just bazel-lock-check`
- Not run: tests/clippy per request; CI will cover them
2026-05-26 13:56:46 +02:00
jif-oai
3936ed221d Add ad-hoc memory note tool (#24562)
## Why

Codex memory updates currently rely on instructions that tell agents to
create ad-hoc note files directly in the memory workspace. The memories
extension already has a `MemoriesBackend` abstraction for local storage
and future non-filesystem backends, so the ad-hoc note writer should
live behind that same interface instead of baking local filesystem
assumptions into the tool shape.

## What

- Adds a `memories/add_ad_hoc_note` tool to the existing memories tool
bundle.
- Extends `MemoriesBackend` with `add_ad_hoc_note` plus request/response
types so remote memory stores can implement the same operation later.
- Implements the local backend by creating append-only notes under
`extensions/ad_hoc/notes`.
- Validates the tool-provided filename contract
(`YYYY-MM-DDTHH-MM-SS-<slug>.md`), rejects path-like filenames, rejects
empty notes, and uses create-new semantics so existing notes are never
overwritten.
- Keeps memories tool contribution behind the existing commented-out
registration path; this defines the tool surface without newly exposing
it through app-server.

## Test Plan

- `just test -p codex-memories-extension`
2026-05-26 12:23:24 +02:00
jif-oai
de513a83f3 chore: move memory prompt builder into extension (#24558)
## Why

The memories extension now owns the read-path developer instructions it
injects at thread start. Keeping that prompt builder and template in
`codex-memories-read` left the extension depending on a helper crate for
extension-specific prompt assembly, and kept async template/truncation
dependencies in the read crate after the remaining read surface no
longer needed them.

## What changed

- Moved `prompts.rs`, its tests, and `templates/memories/read_path.md`
from `memories/read` into `ext/memories`.
- Wired `MemoryExtension` to call the local prompt builder and added the
moved templates to `ext/memories/BUILD.bazel` compile data.
- Removed the now-unused prompt export and prompt-related dependencies
from `codex-memories-read`.

## Testing

- Not run locally.
2026-05-26 11:53:47 +02:00
jif-oai
d579dafb70 chore: drop orphaned codex memories MCP crate (#24555)
## Why

The memory read-tool surface had two implementations: the app-server
extension path under `ext/memories`, and an unused `codex-memories-mcp`
workspace crate under `memories/mcp`. The MCP crate no longer has
reverse dependents, so keeping it around preserves duplicate backend,
schema, and tool code that is not part of the live app-server memory
path.

Dropping the orphaned crate makes the remaining memory crate split
clearer: `memories/read` owns read-path prompt/citation helpers,
`memories/write` owns the write pipeline, and `ext/memories` owns the
app-server extension integration.

## What changed

- Removed the `memories/mcp` crate and its Bazel/Cargo metadata.
- Removed `memories/mcp` from the Rust workspace and lockfile.
- Updated `memories/README.md` so it only lists the remaining reusable
memory crates.

## Verification

- `cargo metadata --format-version 1 --no-deps` succeeds.
2026-05-26 11:29:37 +02:00
jif-oai
7f9ab6e083 [wip] goal shift (#23858) 2026-05-26 11:22:18 +02:00
rhan-oai
04a8580f33 centralize Responses retry policy (#24131)
## Why

#23951 added remote compaction v2 retries, but it left the retry and WS
-> HTTPS fallback behavior duplicated between normal Responses turns and
compaction. This follow-up centralizes the common retry handling so
future changes to fallback, retry delay, retry notifications, and retry
sleep do not have to be kept in sync across both callsites.

## What changed

- Added `core/src/responses_retry.rs` with a shared handler for
retryable Responses stream errors.
- Reused that handler from normal turn sampling and remote compaction
v2.
- Kept each callsite responsible for its retry budget: normal turns
still use `stream_max_retries`, while compaction v2 still uses
`min(stream_max_retries, 2)`.
- Preserved caller-specific behavior around non-retryable errors,
context-window errors, usage-limit errors, and compact-specific final
failure logging.

The shared handler now owns:

- WS -> HTTPS fallback warning emission
- retry delay selection, including server-requested stream retry delay
- retry logging
- first-WebSocket-retry notification suppression
- `Reconnecting... n/max` stream-error notification
- sleeping before the next retry attempt

## Verification

- `cargo test -p codex-core remote_compact_v2`
- `cargo test -p codex-core websocket_fallback`
- `just fix -p codex-core`

Did not run the full workspace test suite.

---------

Co-authored-by: jif-oai <jif@openai.com>
2026-05-26 11:01:18 +02:00
jif-oai
4f7d6b4ef7 chore: stop consuming legacy config profiles (#24076)
## Why

The old config-profile mechanism should no longer influence runtime
behavior now that profile selection has moved to file-based `--profile`
config files. Core already rejects a selected legacy `profile = "..."`
with a migration error in
[`core/src/config/mod.rs`](d6451fcb79/codex-rs/core/src/config/mod.rs (L2521-L2529)),
but a few residual consumers still read legacy `[profiles.*]` data while
performing managed-feature checks and personality migration.

That kept dead legacy profile state relevant after selection had been
removed, and could make personality migration depend on a stale or
missing old profile.

## What changed

- Stop scanning legacy `[profiles.*]` feature settings when validating
managed feature requirements.
- Make personality migration consider only top-level `personality` and
`model_provider` settings.
- Remove the now-unused `ConfigToml::get_config_profile` helper.
- Update personality migration coverage to verify that legacy profile
personality fields and missing legacy profile names no longer affect
that migration path.

This keeps the legacy `profile` / `profiles` config shape available for
the remaining compatibility and migration diagnostics; it only removes
these behavior consumers.

## Verification

- Updated `core/tests/suite/personality_migration.rs` for the new
legacy-profile behavior.
- Focused test command: `cargo test -p codex-core
personality_migration`.
2026-05-26 10:34:43 +02:00
Eric Traut
e8651516f4 Log rollout writer OS errors (#24474)
## Why

Refs #24425.

We have seen rollout JSONL corruption that appears consistent with a
rollout write failing after partially appending a line, followed by a
retry that appends the same item again. The available user logs did not
include the underlying OS error, so it is hard to tell whether the
trigger was `ENOSPC`, quota exhaustion, a filesystem error, or something
else.

This PR adds the missing diagnostics for future reports.

## What changed

- Include `ErrorKind` and `raw_os_error()` in rollout writer failure
logs.
- Preserve the existing append-only rollout write path; this PR is
diagnostic-only.

## Verification

- `just test -p codex-rollout`
2026-05-26 10:33:22 +02:00
Felipe Coury
8a94430bb2 fix(process-hardening): preserve macos malloc diagnostics (#24479)
## Summary

Follow-up to #24459 and partial behavioral revert of `a71fc47` / #16699.

- Stop removing `MallocStackLogging*` and `MallocLogFile*` from macOS
pre-main hardening.
- Remove documentation that claims Codex suppresses those allocator
diagnostic controls.
- Retain the shared `remove_env_vars_with_prefix` refactor and existing
`LD_` / `DYLD_` hardening.

## Why

#24459 fixes the composer-corruption problem at the terminal stderr
boundary while preserving redirected stderr. With that guard in place,
stripping macOS malloc diagnostic settings is unnecessary and can hide
diagnostics intentionally enabled by callers.

## Validation

- `just fmt`
- `just test -p codex-process-hardening`
- `just argument-comment-lint-from-source -p codex-process-hardening`
- `git diff --check`
2026-05-25 17:26:10 -03:00
Felipe Coury
599416d733 fix(tui): prevent macos stderr from corrupting composer (#24459)
## Why

Fixes #17139.

On macOS, runtime diagnostics such as `MallocStackLogging` messages can
be written directly to process stderr while the inline TUI owns the
terminal. Those bytes paint into the same viewport as the composer
without passing through the renderer or composer state, making
diagnostic output appear to leak into the input area.

## What Changed

- Add a macOS terminal stderr guard while the inline TUI owns the
viewport.
- Restore stderr when Codex returns terminal ownership for external
interactive programs, suspend/resume, panic handling, and normal
shutdown.
- Add an fd-level regression test that verifies output is suppressed
only while terminal ownership is held and restored at each handoff
boundary.

## How to Test

1. On macOS, launch the interactive TUI and leave the composer visible.
2. Exercise the workflow that triggers an allocator/runtime stderr
diagnostic during an active session, as reported in #17139.
3. Confirm the diagnostic no longer overwrites the active composer
region.
4. Suspend or exit the TUI and confirm subsequent terminal stderr output
remains visible.

The platform diagnostic is environment-dependent, so the deterministic
regression check is the new fd-lifecycle test in
`tui::terminal_stderr::tests::suppresses_stderr_only_while_terminal_is_owned`.

Targeted validation:
- `just argument-comment-lint-from-source -p codex-tui` passed.
- `just test -p codex-tui` exercised and passed the new stderr-guard
regression test. The full invocation currently fails in two unrelated
guardian-policy tests,
`update_feature_flags_disabling_guardian_clears_review_policy_and_restores_default`
and
`update_feature_flags_disabling_guardian_clears_manual_review_policy_without_history`,
which reproduce when rerun in isolation.
2026-05-25 19:53:40 +00:00
Felipe Coury
14d80e55cd fix(tui): improve multiline markdown list readability (#24351)
## Why

Numbered Markdown findings become hard to scan when long items visually
run together or when wrapped explanatory paragraphs lose their list
indentation. This is especially visible in review output: the next
number can look attached to the previous finding, and paragraph
continuation rows can jump back toward the left margin instead of
staying grouped beneath their item.

<table><tr><td>
<center>Before</center>
<img width="1718" height="836" alt="CleanShot 2026-05-24 at 14 00 49"
src="https://github.com/user-attachments/assets/f1ee0023-50fa-4f81-a641-ae08b17b99bd"
/>
</td></tr>
<tr><td> 
<center>After</center>
<img width="1714" height="906" alt="image"
src="https://github.com/user-attachments/assets/b123a5e0-a232-47bf-96d5-c935295f7c0a"
/>
</td></tr>
</table>

## What Changed

- Insert a blank separator before a sibling list item when the previous
item occupies more than one rendered line.
- Preserve compact rendering for lists whose sibling items each render
on one line.
- Preserve list-body leading whitespace when transient streamed
assistant rows require another wrapping pass for history display, so
wrapped paragraphs stay aligned beneath their item.
- Share the existing leading-whitespace prefix logic used by history
insertion instead of introducing a second indentation rule.
- Keep streamed Markdown output aligned with completed rendering and add
snapshots for findings-style spacing and streamed paragraph indentation.

## How to Test

1. Start Codex from this branch and open the recorded repro session
`019e563f-7d58-7ff2-8ec7-828f20fa61ca`.
2. Inspect the numbered `Findings` list whose items contain explanatory
paragraphs.
3. Confirm each multiline finding is separated from the next numbered
finding by one blank line.
4. Confirm wrapped rows of each indented paragraph remain aligned
beneath the finding body, rather than returning to the left edge.
5. Render a short one-line numbered or unordered list and confirm its
items remain compact without added blank rows.

Targeted tests:

- `just test -p codex-tui history_cell insert_history markdown_render
markdown_stream streaming::controller`
- `just argument-comment-lint-from-source -p codex-tui`

## Related Work

PR #24346 changes Markdown table column allocation in parallel. This PR
is intentionally limited to list-item readability and history wrapping;
both branches touch `codex-rs/tui/src/markdown_render.rs`, so a small
merge conflict may need resolution depending on merge order.
2026-05-25 15:42:28 -03:00
Felipe Coury
20d1b7674d fix(tui): improve markdown table column allocation (#24346)
## Why

Markdown tables with a long path-heavy column could allocate almost all
available width to that column and collapse neighboring prose columns to
only a few characters. In rollout summaries this made `Unit` and `What
It Adds` difficult to read, even though the long `Files` values were the
content best suited to wrapping.

The affected example also specified `Files` as right aligned in its
markdown delimiter (`---:`). This change preserves that requested
alignment while improving how width is distributed.

| Before | After |
|---|---|
| <img width="1709" height="764" alt="image"
src="https://github.com/user-attachments/assets/932ab21c-b72d-48a2-9aad-b69da87a0968"
/> | <img width="1711" height="855" alt="image"
src="https://github.com/user-attachments/assets/4028bd20-2228-4c2f-be8a-1866325b7f62"
/> |


## What Changed

- Classify table columns as narrative, token-heavy, or compact during
width allocation.
- Shrink token-heavy path and URL columns before shrinking narrative
prose, while preserving compact counts and short labels longest.
- Use readable soft floors for narrative and token-heavy content before
falling back to tighter layouts.
- Add snapshot coverage for a rollout-shaped table containing
right-aligned file paths and prose columns.

## How to Test

1. Render a markdown table with `Unit`, right-aligned `Files`, `Adds`,
`Removes`, and `What It Adds` columns at a constrained terminal width.
2. Put long repository paths in `Files` and sentence-length content in
`Unit` and `What It Adds`.
3. Confirm that `Files` remains right aligned but wraps before the
narrative columns become unreadable.
4. Confirm that the compact numeric columns remain easy to scan.

Targeted tests:
- `just test -p codex-tui markdown_render`

Validation note: `just test -p codex-tui` was also attempted and reached
two existing unrelated failures in
`app::tests::update_feature_flags_disabling_guardian_*`; the markdown
rendering regression test passes in the targeted run.
2026-05-25 15:09:17 -03:00
Eric Traut
a7836744cc Add doctor thread inventory audit (#24305)
## Why

Users have been reporting missing sessions in the app. The app server
thread listing is backed by the SQLite state DB, but the durable source
of truth for a thread still exists on disk as rollout JSONL. When the
state DB is incomplete, doctor should be able to show the mismatch
directly instead of leaving users with a generic state health result.

## What changed

This adds a `threads` doctor check that compares active and archived
rollout files under `CODEX_HOME` with rows in the SQLite `threads`
table. The check reports missing rollout rows, stale DB rows, archive
flag mismatches, duplicate rollout thread IDs, duplicate DB paths,
source/provider summaries, and bounded samples of affected rollout
paths.

It also adds a read-only state audit helper in `codex-rs/state` so
doctor can inspect thread rows without creating, migrating, or repairing
the database.

## Sample output

```text
  ⚠ threads      rollout files are missing from the state DB
      default model provider   openai
      rollout DB active files  3910
      rollout DB archived files 2037
      rollout DB scan errors   0
      rollout DB malformed file names 0
      rollout DB scan cap reached false
      rollout DB rows          5499
      rollout DB active rows   3462
      rollout DB archived rows 2037
      rollout DB missing active rows 448
      rollout DB missing archived rows 0
      rollout DB stale rows    0
      rollout DB archive mismatches 0
      rollout DB duplicate rollout thread ids 0
      rollout DB duplicate DB paths 0
      rollout DB model providers openai=5359, lmstudio=35, mock_provider=33, lite_llm=26, proxy=26, ollama=15, lms=4, local-usage-limit=1
      rollout DB sources       vscode=2587, cli=1494, subagent:thread_spawn=577, subagent:other=502, exec=281, subagent:memory_consolidation=46, subagent:review=9, unknown=3
      rollout DB missing active sample ~/.codex/sessions/2026/0…857e-a923c712e066.jsonl
      rollout DB missing active sample ~/.codex/sessions/2025/0…877a-766dff25c68d.jsonl
      rollout DB missing active sample ~/.codex/sessions/2025/0…a8b1-7bbadc836f6e.jsonl
      rollout DB missing active sample ~/.codex/sessions/2025/0…a218-e6197f3f62f8.jsonl
      rollout DB missing active sample ~/.codex/sessions/2025/0…9011-7e30784f9932.jsonl
```
2026-05-25 10:29:06 -07:00
Eric Traut
613e5149a4 TUI config cleanup: MCP inventory (#24265)
## Summary

The TUI `/mcp` inventory flow should reflect the app server’s MCP status
response. It was also joining those results with the TUI process’s local
`config.mcp_servers`, which can diverge once MCP state is owned by a
remote app server and cause stale local command, URL, status, or
empty-state details to render.

This change removes the local config join from the app-server-backed
inventory renderer. The TUI now renders directly from the existing
`mcpServerStatus/list` payload and treats an empty status response as
the empty MCP inventory state.

## Known limitation

The existing `mcpServerStatus/list` payload does not include
disabled-state or disabled-reason fields. To preserve the current
app-server API, this PR does not try to infer that state from
client-local config. If remote `/mcp` needs to show disabled/reason
details again, that should come from app-server-owned status data in a
follow-up.

Related to #22914, #22915, and #22916.
2026-05-25 09:56:21 -07:00
Eric Traut
bb55736906 TUI config cleanup: trusted projects (#24255)
## Why
TUI onboarding trusted-project persistence should go through the same
app-server config write path as other config mutations. Writing
`config.toml` directly from the trust widget bypasses that layer and can
let onboarding proceed even when the trust decision was not actually
persisted.

## What changed
- Added a TUI config helper that writes the existing project trust
structure through `config/batchWrite`.
- Persists trust decisions as `projects.<project>.trust_level =
"trusted"` using the existing project trust key helper.
- Changed the trust directory widget to only record the user selection;
onboarding performs the app-server write before reporting success.
- Keeps the user on the trust screen and shows an error if app-server
persistence fails.

## Verification
- `cargo test -p codex-tui --lib
trust_persistence_failure_keeps_trust_step_in_progress`
- `cargo test -p codex-tui --lib
trusted_project_edit_targets_project_trust_level`
- Manual: built the local `codex-cli`, accepted the trust prompt in a
temp project, confirmed `projects.<project>.trust_level = "trusted"`,
and simulated an unwritable config to verify onboarding stays on the
trust screen without writing trust.
2026-05-25 09:54:05 -07:00
Eric Traut
f05fd0e661 TUI config cleanup: oss_provider (#24254)
## Summary

Manual provider selection during `codex --oss` startup was still
persisting `oss_provider` through the legacy local `config.toml` writer.
That bypasses the app-server-owned config mutation path used by the TUI,
so this routes the write through the app server config API instead.

The net behavior is intentionally narrow: only an interactive picker
selection is persisted. Auto-detected single-running-provider startup
and explicit `--local-provider` startup remain ephemeral, so merely
having one backend running does not make that provider sticky for future
runs.

## What Changed

- Removed the TUI picker’s direct dependency on
`set_default_oss_provider`.
- Had `oss_selection` report whether the returned provider came from the
interactive picker.
- Carried only manually selected providers into startup persistence.
- Wrote `oss_provider` via `config/batchWrite` once the app server
session is available.
- Logged a warning and continued startup if the app-server config write
fails.

## Verification

Manually smoke-tested the real `codex-tui` binary with a temporary
`CODEX_HOME`, pseudo-terminal input, and a fake LM Studio HTTP server:

- Interactive picker selection persisted `oss_provider = "lmstudio"`.
- Non-picker `--local-provider lmstudio` startup did not persist
`oss_provider`.
2026-05-25 09:53:39 -07:00
Eric Traut
5fb5e47767 Respect hook trust bypass during TUI startup (#24317)
Fixes #24093.

## Why

`--dangerously-bypass-hook-trust` is a supported CLI flag intended for
headless or automated runs where enabled hooks should be allowed to run
without requiring persisted trust. In the TUI, startup hook review still
opened whenever hooks looked untrusted, so a launch using the bypass
could block on the interactive "Hooks need review" prompt.

The tricky case is persistent app-server resume: a resume may attach to
an already-running thread, where resume config overrides are ignored. In
that path, hiding the startup review would be wrong because the existing
hook engine may still filter untrusted hooks.

## What Changed

- Startup hook review now skips the prompt only when hook trust bypass
is actually safe for that launch.
- The TUI forwards `bypass_hook_trust` through the app-server request
config for fresh thread start/resume/fork paths, and the app-server
applies it as a runtime-only `ConfigOverrides` value rather than
treating it like a `config.toml` setting.
- Persistent app-server resumes keep the startup review prompt so users
still have a chance to trust hooks when the running thread cannot
receive the bypass override.

## Verification

- Added focused coverage for startup hook review with and without
`bypass_hook_trust`.
- Extended existing TUI/app-server config override tests to cover
forwarding and applying `bypass_hook_trust`.
2026-05-25 09:44:21 -07:00
Eric Traut
913270a689 Show remote connection details in /status (#24420)
## Summary

Fixes #24411.

`/status` currently has no way to show when the TUI is talking to Codex
through a remote transport. That makes embedded local sessions, local
daemon sessions, and true remote sessions look the same, and it hides
the remote server version when debugging connection-specific behavior.

This PR adds a single `Remote` row for non-embedded connections only.
The row shows the sanitized connection address and a dimmed version
parenthetical, preserving the existing status output for embedded local
sessions.

<img width="791" height="144" alt="image"
src="https://github.com/user-attachments/assets/529d7940-1c45-4586-8b06-f20a1f04b771"
/>


## Verification

- Manually validated when connecting remotely (either implicitly to
local daemon or explicitly)
2026-05-25 09:42:42 -07:00
Eric Traut
caebff3d66 tui: label compact rate-limit percentages (#24314)
## Summary

The compact TUI status line already renders rate-limit percentages as
remaining capacity, but the text did not say so. That made high-usage
red indicators ambiguous because values like `weekly 6%` could be read
as either used or remaining.

This PR labels the compact rate-limit values explicitly as `left` across
the status line, terminal title, and setup previews.

Addresses #24274
2026-05-25 09:41:32 -07:00
Eric Traut
6491d1207f Report app-server version in codex doctor (#24311)
## Why

We are seeing cases where users have an old background app-server still
running. `codex doctor` already reports background server state, but
without the running app-server version it is harder to diagnose
behaviors that depend on the daemon build.

## What changed

- Reused the app-server daemon's passive initialize probe through a
narrow `probe_app_server_version` helper.
- Updated the `codex doctor` Background Server section to report
`app-server version: <version>` when the socket is reachable.
- Preserved the not-running OK behavior and report `app-server version:
unavailable (<short error>)` when a socket exists but the passive probe
fails.
2026-05-25 09:41:12 -07:00
Felipe Coury
9f42c89c01 feat(doctor): add environment diagnostics (#24261)
## Why

Issue #23031 was hard to diagnose from existing `codex doctor` output
because support could not see the OS language, resolved Git install, Git
repo metadata, Windows console mode/code page, or terminal-title inputs
that affect the TUI startup path. This adds those read-only signals to
`codex doctor` so Windows, Linux, and macOS reports carry the context
needed to investigate similar terminal rendering regressions.

Refs #23031

## What Changed

- Add a `system.environment` check for OS type/version, OS language, and
locale env vars.
- Add a `git.environment` check for the selected Git executable, PATH
Git candidates, version, exec path/build options, repository root,
branch, `.git` entry, and `core.fsmonitor`.
- Add Windows console code page and VT-processing mode details to
terminal diagnostics.
- Add a `terminal.title` check for configured/default title items and
resolved project-title source/value.
- Surface startup warning counts in config diagnostics and teach human
output to render the new categories.

## How to Test

1. On Windows, check out this branch and run `cargo run -p codex-cli --
doctor --summary`.
2. Confirm the Environment section includes `system`, `git`, `terminal`,
and `title` rows.
3. Run `cargo run -p codex-cli -- doctor --json`.
4. Confirm the JSON contains `system.environment`, `git.environment`,
and `terminal.title`; on Windows, confirm `terminal.env` details include
console code pages and `VT processing` for stdout/stderr.
5. From a non-git directory, run the same `doctor --json` command and
confirm the Git check reports `repo detected: false` rather than
warning.

Targeted tests:

- `cargo test -p codex-cli doctor`
- `cargo test -p codex-cli`
2026-05-24 15:34:35 +00:00
xl-openai
7d47056ea4 fix: plugin bundle archive handling for upload and install (#23983)
Move plugin tar.gz packing and unpacking into a shared core-plugins
archive helper so uploaded bundles are decoded through the same tar
handling used for installs. This removes duplicate archive logic,
supports GNU long-name entries on extraction, and keeps size, traversal,
link, and entry-type checks in one place.
2026-05-22 19:31:39 -07:00
Channing Conger
f94157a4b2 code-mode: merge stored values by key (#24159)
## Summary

Change code-mode stored value updates to merge writes by key instead of
replacing the session's complete stored-value map after each cell
completes.

Previously, each cell received a snapshot of stored values and returned
the complete resulting map. When multiple cells ran concurrently, a
later completion could overwrite values written by another cell because
it committed an older snapshot.

This change moves stored-value ownership into `CodeModeService`:

- Each runtime starts from the service's current stored values.
- Runtime completion reports only keys written by that cell.
- The service merges those writes into the current stored-value map on
successful completion.
- Core no longer replaces its stored-value state from a cell result.

As a result, concurrently executing cells can update different stored
keys without clobbering one another.

The move into CodeModeService is motivated by a desire to have this
lifetime tied to a new lifetime object on that side in a subsequent PR.
2026-05-22 19:09:02 -07:00
Michael Bolin
0febb1100f package: add x64 macOS codex-zsh artifact (#24171) 2026-05-22 18:42:19 -07:00
Abhinav
5c20513a1b Default function tools into tool hooks (#23757)
# Why

`PreToolUse`, `PostToolUse`, and `updatedInput` coverage for local
function tools currently depends on each handler remembering to wire up
the hook contract itself. That makes coverage easy to miss as new
function tools are added, even though most of them share the same basic
shape: a model-facing function call with JSON arguments.

# What

This makes `CoreToolRuntime` provide the default hook contract for
ordinary local function tools:

- build generic `PreToolUse` and `PostToolUse` payloads from the
function tool name and arguments
- apply `updatedInput` rewrites back into function-tool arguments
through the same default path
- let tool outputs override the post-hook input or response when they
have a more stable hook-facing contract

The exceptions stay explicit:

- hosted tools remain outside the generic local function path
- code-mode `wait` and `write_stdin` opt out for now
- `PostToolUse` feedback replaces only the model-visible response, so
code mode keeps its typed tool result

With the generic path in place, the MCP and extension-tool adapters no
longer need their own duplicate pre/post hook plumbing. The new coverage
exercises the registry default plus end-to-end local function behavior
for pre-hook blocking, `updatedInput` rewriting, and post-hook context.
2026-05-23 00:56:58 +00:00
Michael Bolin
c7bcb90f9b package: include zsh fork in Codex package (#23756)
## Why

The package layout gives Codex a stable place for runtime helpers that
should travel with the entrypoint. `shell_zsh_fork` still required users
to configure `zsh_path` manually, even though we already publish
prebuilt zsh fork artifacts.

This PR builds on #24129 and uses the shared DotSlash artifact fetcher
to include the zsh fork in Codex packages when a matching target
artifact exists. Packaged Codex builds can then discover the bundled
fork automatically; the user/profile `zsh_path` override is removed so
the feature uses the package-managed artifact instead of a legacy path
knob.

## What Changed

- Added `scripts/codex_package/codex-zsh`, a checked-in DotSlash
manifest for the current macOS arm64 and Linux zsh fork artifacts.
- Taught `scripts/build_codex_package.py` to fetch the matching zsh fork
artifact and install it at `codex-resources/zsh/bin/zsh` when available
for the selected target.
- Added package layout validation for the optional bundled zsh resource.
- Added `InstallContext::bundled_zsh_path()` and
`InstallContext::bundled_zsh_bin_dir()` for package-layout resource
discovery.
- Threaded the packaged zsh path through config loading as the runtime
`zsh_path` for packaged installs, and removed the config/profile/CLI
override path.
- Kept the packaged default zsh override typed as `AbsolutePathBuf`
until the existing runtime `Config::zsh_path` boundary.
- Updated app-server zsh-fork integration tests to spawn
`codex-app-server` from a temporary package layout with
`codex-resources/zsh/bin/zsh`, matching the new packaged discovery path
instead of setting `zsh_path` in config.
- Switched package executable copying from metadata-preserving `copy2()`
to `copyfile()` plus explicit executable bits, which avoids macOS
file-flag failures when local smoke tests use system binaries as inputs.

## Testing

To verify that the `zsh` executable from the Codex package is picked up
correctly, first I ran:

```shell
./scripts/build_codex_package.py
```

which created:

```
/private/var/folders/vw/x2knqmks50sfhfpy27nftl900000gp/T/codex-package-pms94kdp/
```

so then I ran:

```
/private/var/folders/vw/x2knqmks50sfhfpy27nftl900000gp/T/codex-package-pms94kdp/bin/codex exec --enable shell_zsh_fork 'run `echo $0`'
```

which reported the following, as expected:

```
/private/var/folders/vw/x2knqmks50sfhfpy27nftl900000gp/T/codex-package-pms94kdp/codex-resources/zsh/bin/zsh
```



---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23756).
* #23768
* __->__ #23756
2026-05-22 17:54:07 -07:00
Anton Panasenko
03e6c5f600 fix(remote-control): cap reconnect backoff (#24164)
## Why

Remote-control websocket reconnects currently use the shared exponential
backoff helper without a local ceiling, so a long failure streak can
stretch retries out indefinitely and leave the runtime behavior hard to
inspect from logs.

## What Changed

Cap the remote-control reconnect delay at 30 seconds, then reset the
reconnect attempt counter once that capped delay is emitted so the next
failure starts from the initial jittered delay again.

The reconnect failure log now records the attempt number, chosen delay,
and whether the cap triggered a reset, with a separate info log when the
backoff counter is reset after the cap.

## Verification

`just test -p codex-app-server-transport`

Related issue: N/A
2026-05-23 00:38:22 +00:00
Michael Bolin
ed47f1ab1e release: build macOS x64 zsh artifact (#24165)
## Why

The zsh release workflow currently publishes macOS arm64 and Linux zsh
fork artifacts, but no macOS x64 artifact. The Codex package builder
therefore cannot include codex-resources/zsh/bin/zsh for
x86_64-apple-darwin packages.

## What Changed

- Added an x86_64-apple-darwin row to the macOS zsh release matrix.
- Runs that row on macos-15-large, the Intel macOS runner appropriate
for the native zsh build.
- Added the matching macos-x86_64 platform to the zsh DotSlash publish
config so the generated release manifest can reference the new tarball.
2026-05-22 17:08:59 -07:00
dhruvgupta-oai
4bcabbfbec Display workspace usage limit error copy from response header (#24114)
## Why

`openai/openai#947613` adds `X-Codex-Rate-Limit-Reached-Type` for Codex
workspace credit-depletion and spend-cap responses. The CLI currently
reads the adjacent promo header but otherwise renders generic
usage-limit copy, so those responses do not explain the
workspace-specific action the user needs to take.

Backend dependency: https://github.com/openai/openai/pull/947613

## What Changed

- Parse `X-Codex-Rate-Limit-Reached-Type` in the usage-limit error
handling path alongside `x-codex-promo-message`.
- Keep the header value parsing with the shared `RateLimitReachedType`
enum.
- Carry the parsed type on `UsageLimitReachedError` and render
client-owned copy for the four workspace owner/member credit and
spend-cap values.
- Preserve existing promo and plan-based text for absent, generic, or
unknown header values.
- Keep the existing TUI workspace-owner nudge state path unchanged; the
response header only selects the displayed error string.
- Add focused display coverage for all specific type values and the
generic fallback case.

## Test Plan

- Added `usage_limit_reached_error_formats_rate_limit_reached_types`
coverage.
- Not run manually, per request; CI runs validation on the pushed
commit.
2026-05-22 23:58:49 +00:00
pakrym-oai
6ad3a83509 [codex] Remove external client session reset plumbing (#24157)
## Why

The turn loop no longer needs to decide when a `ModelClientSession`
should reset its websocket state after compaction. That reset behavior
belongs inside the model client, where the websocket cache and retry
state are owned. The repo guidance now calls this out explicitly so
future changes let the incremental request logic decide whether the
previous request can be reused.

## What Changed

- Removed the `reset_client_session` return value from pre-sampling and
auto-compact helpers in `core/src/session/turn.rs`.
- Changed compaction helpers to return `CodexResult<()>` so callers only
handle success or failure.
- Made `ModelClientSession::reset_websocket_session` private to
`core/src/client.rs`, leaving it callable only from model-client
internals.
- Added `AGENTS.md` guidance not to call `reset_client_session`
unnecessarily.

## Validation

- `just test -p codex-core session::turn`
2026-05-22 16:46:25 -07:00
Celia Chen
10ac2781eb chore: add JSON schema policy fixture coverage (#24152)
## Why

Before changing the Codex Bridge JSON schema policy, add integration
coverage around real connector-like MCP tool schemas. The existing unit
tests cover individual sanitizer behaviors, but they do not make it easy
to see whether full fixture schemas keep model-visible guidance, prune
only unreachable definitions, drop unsupported JSON Schema fields, and
stay within the Responses API schema budget.

## What Changed

- Added `tools/tests/json_schema_policy_fixtures.rs`, which converts MCP
tool fixtures through `mcp_tool_to_responses_api_tool` and validates the
resulting Responses tool parameters.
- Added connector-style fixtures for Slack, Google Calendar, Google
Drive, Notion, and Microsoft Outlook Email under
`tools/tests/fixtures/json_schema_policy/`.
- Added fixture assertions for preserved guidance, pruned definitions,
expected field drops after `JsonSchema` conversion, marker count
baselines, and dangling local `$ref` prevention.
- Added a real oversized golden Notion `create_page` input schema
fixture to exercise the compaction path that strips descriptions, drops
root `$defs`, rewrites local refs, and fits the compacted schema under
the budget.
2026-05-22 16:31:33 -07:00
Adam Perry @ OpenAI
7924743c38 [codex] Add image re-encoding benchmarks (#23935)
## Summary
- add Divan benchmarks for prompt image re-encoding paths
- wire the image benchmark smoke test into Rust CI workflows

## Why
Image prompt handling includes re-encoding work that benefits from
repeatable benchmark coverage so changes can be measured in CI and
locally.

This already helped identify a potential regression from changing compiler flags.

## Impact
Developers can run and compare the new image re-encoding benchmarks, and
CI exercises the benchmark target via the Rust benchmark smoke test.
2026-05-22 22:38:40 +00:00
pakrym-oai
fbd4efa9ed [codex] Use TurnInput for session task input (#24151)
## Why

The idea here is to erase the difference between initial and followup
inputs to a turn. Followup inputs are already represented as TurnInput.

Eventual goal is not to have explicit on task input at all and pull
everything from input Q.

## What Changed

- Changes `SessionTask::run` and the erased `AnySessionTask::run` path
to accept `Vec<TurnInput>`.
- Wraps user-submitted spawn input as `TurnInput::UserInput` at the
session task start boundary.
- Updates `run_turn` to record initial `TurnInput` using the same hook
and recording path used for pending input.
- Keeps review-specific conversion local to `ReviewTask`, where the
sub-Codex one-shot API still expects `Vec<UserInput>`.
- Moves the synthetic compact prompt into `CompactTask` and starts
compact tasks with empty task input.

## Validation

- `cargo check -p codex-core`
- `just test -p codex-core -E
'test(task_finish_emits_turn_item_lifecycle_for_leftover_pending_user_input)
| test(queued_response_items_for_next_turn_move_into_next_active_turn) |
test(steered_input_reopens_mailbox_delivery_for_current_turn)'`
2026-05-22 15:21:08 -07:00
Michael Bolin
195ba3eb88 package: factor DotSlash executable fetching (#24129)
## Why

The package builder already fetches `rg` from a checked-in DotSlash
manifest. The zsh packaging work needs the same
fetch/cache/size-check/SHA-256/extract path for another manifest, but
keeping that refactor inside the zsh PR makes the review harder to
follow.

This PR factors the existing `rg`-specific implementation into a
reusable helper with no intended behavior change for `rg` packaging.

## What Changed

- Added `scripts/codex_package/dotslash.py` for checked-in DotSlash
manifest parsing, archive download, cache reuse, size validation,
SHA-256 validation, and member extraction.
- Updated `scripts/codex_package/ripgrep.py` to delegate to the shared
helper.
- Preserved the existing `rg` manifest path, cache key, destination
filename, and executable-bit behavior.

## Testing

- `python3 -m py_compile scripts/codex_package/dotslash.py
scripts/codex_package/ripgrep.py scripts/codex_package/cli.py
scripts/codex_package/layout.py scripts/codex_package/zsh.py`
- `python3 -m unittest discover scripts/codex_package`


---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/24129).
* #23768
* #23756
* __->__ #24129
2026-05-22 14:38:44 -07:00
rhan-oai
6419402a7c [codex-analytics] split compaction v2 analytics implementation (#24146)
## What changed

- Add a distinct `responses_compaction_v2` value for
`CodexCompactionEvent.implementation`.
- Emit that value from the remote compaction v2 path.
- Keep local compaction as `responses` and legacy `/responses/compact`
as `responses_compact`.

## Why

Remote compaction v2 and local prompt-based compaction were both
reported as `responses`, which made the analytics table collapse two
different compaction mechanisms into one implementation bucket.

## Validation

- `just fmt`
- `just test -p codex-analytics`

`just test -p codex-core` was started locally, but this PR is
intentionally being pushed for CI to finish the remaining validation.
2026-05-22 21:34:22 +00:00
Won Park
423488480f Add typed Images client to codex-api (#23989)
## Why

Standalone image generation needs a typed `codex-api` client surface for
the Codex image proxy routes before the harness and model-facing tool
layers are wired in.

## What changed

- Added `ImagesClient` support for JSON `images/generations` and
`images/edits` requests.
- Added typed request and response shapes for generation, JSON edit
image URLs, image metadata, and base64 image outputs.
- Kept generation model slugs open-ended while requiring the generation
model field that the downstream endpoint expects.
- Exported the new client and image types from `codex-api`.
- Added coverage for generation and edit wire shapes, extra response
metadata that the client ignores, and malformed image responses missing
`data`.

## Validation

- `cargo test -p codex-api`
- `just fix -p codex-api`
- `just fmt`
- `git diff --check main`
2026-05-22 14:10:55 -07:00
Matthew Zeng
6963145cb6 Support OAuth options in codex mcp add (#24120)
## Summary
- add `--oauth-client-id` and `--oauth-resource` options for streamable
HTTP `codex mcp add` registrations
- persist those options in MCP server config and use them during the
immediate OAuth login flow
- cover add-time serialization of both OAuth options in the CLI
integration tests

## Testing
- `just fmt`
- `cargo test -p codex-cli`
- `just fix -p codex-cli`
2026-05-22 13:21:01 -07:00
mchen-oai
3c83e57bfa Add trace_id to TurnStartedEvent (#23980)
## Why
[Recent PR](https://github.com/openai/codex/pull/22709) removed
`trace_id` from `TurnContextItem`.

## What changed
- Add to `TurnStartedEvent` so rollout consumers can correlate turns
with telemetry traces.
- Note that the branch name is out of date because I originally re-added
to `TurnContextItem`, but we decided to move it to `TurnStartedEvent`.

## Verification
- `cargo test -p codex-protocol`
- `cargo test -p codex-core --lib
regular_turn_emits_turn_started_without_waiting_for_startup_prewarm`
- `cargo test -p codex-core --test all
emits_warning_when_resumed_model_differs`
- `cargo test -p codex-rollout`
- `cargo test -p codex-state`
2026-05-22 13:10:56 -07:00
Michael Bolin
36a71a88bf cli: support --profile for codex sandbox (#24110)
## Why

`codex sandbox` now always runs the host sandbox backend, so it should
accept the same profile selection mechanism as the rest of the runtime
CLI surface. Without `--profile`, sandbox debugging can exercise only
the default config stack unless users manually translate profile config
into ad hoc `-c` overrides.

Supporting `--profile` lets sandbox invocations load
`$CODEX_HOME/<name>.config.toml`, including permission profile
configuration, before resolving the sandbox policy for the command being
run.

## What Changed

- Added `--profile NAME` / `-p NAME` to the host-specific `codex
sandbox` argument structs as `config_profile`.
- Allowed root-level `codex --profile NAME sandbox ...` and made a
sandbox-local `codex sandbox --profile NAME ...` override the root
selection.
- Threaded `LoaderOverrides` through sandbox config loading so selected
config profile files participate in permission resolution before the
legacy read-only fallback.
- Documented the new sandbox flag in `codex-rs/README.md`.

## Verification

- Added parser coverage for `codex sandbox --profile`.
- Added sandbox config-loader coverage that verifies selected config
profile loader overrides select the profile config rather than falling
back to read-only.
- Ran `cargo test -p codex-cli`.
2026-05-22 13:00:53 -07:00
Felipe Coury
acd851e89f fix(tui): restore Windows VT before TUI renders (#24082)
## Why

Older Git for Windows versions can leave the Windows console output mode
without virtual terminal processing after Codex runs git metadata
commands in a repository. When the TUI later emits ANSI control
sequences for redraws, restore, or image rendering, Windows Terminal can
show raw escape bytes or leave the prompt/status area corrupted.

This is a targeted mitigation for the repo-conditioned Windows rendering
corruption reported in #23888 and related reports #23512 and #23628.
Updating Git avoids the trigger for affected users, but Codex should
also reassert the terminal mode before it writes TUI control sequences.

| Before | After |
|---|---|
| <img width="2100" height="1359" alt="CleanShot 2026-05-22 at 11 23 21"
src="https://github.com/user-attachments/assets/3218c379-5f97-4c71-ab25-805c9d20578a"
/> | <img width="2100" height="1359" alt="CleanShot 2026-05-22 at 11 23
58"
src="https://github.com/user-attachments/assets/55ac72bb-37d0-400e-99bc-12dd5ea4092d"
/> |


## What Changed

- Re-enable Windows virtual terminal processing for stdout and stderr
before TUI mode setup, restore, redraw, resume, and pet image render
paths.
- Treat invalid, null, or non-console handles as no-ops so redirected or
non-console output is unaffected.
- Keep the helper as a no-op on non-Windows platforms.

## How to Test

1. On Windows Terminal with a Git 2.28.0 for Windows install, start
Codex inside a valid Git repository.
2. Start a new Codex CLI session.
3. Confirm the prompt, working indicator, and bottom status line remain
readable instead of showing raw ANSI escape sequences.
4. Repeat outside a Git repository to confirm the ordinary non-repo
startup path is unchanged.

Targeted tests:
- Not run locally; the behavior depends on Windows console mode APIs and
the current worktree is on macOS.
2026-05-22 16:20:09 -03:00
Michael Bolin
75b7e06621 docs: update README.md to mention curl-based installer (#24106)
Now that users can install via `curl` (or `irm`), we should tell them
about it so they no longer need to use `npm`!

Note that on one Windows machine I tested on, when I ran:

```
irm https://chatgpt.com/codex/install.ps1 | iex
```

I got this error:

```
iex : The property 'OSArchitecture' cannot be found on this object. Verify that the property exists.
At line:1 char:45
+ irm https://chatgpt.com/codex/install.ps1 | iex
+                                             ~~~
    + CategoryInfo          : NotSpecified: (:) [Invoke-Expression], PropertyNotFoundException
    + FullyQualifiedErrorId : PropertyNotFoundStrict,Microsoft.PowerShell.Commands.InvokeExpressionCommand
```

so we'll recommend the following that works from both `cmd.exe` and
PowerShell:

```
powershell -ExecutionPolicy ByPass -c "irm https://chatgpt.com/codex/install.ps1 | iex"
```

This PR makes a slight update to `codex-rs/tui/src/update_action.rs` to
match.
2026-05-22 18:39:08 +00:00
iceweasel-oai
5b1b6a20dd [codex] Use rolling files for Windows sandbox logs (#24117)
## Why

Windows sandbox diagnostics currently append to a single `sandbox.log`
under `CODEX_HOME/.sandbox`. That file never rolls over, which makes it
hard to safely include sandbox diagnostics in future feedback reports
without risking unbounded growth.

## What changed

- Replaced direct append-open sandbox logging with
`tracing_appender::rolling::RollingFileAppender`.
- Configured sandbox logs to rotate daily using names like
`sandbox.YYYY-MM-DD.log`.
- Added a conservative `MAX_LOG_FILES` cap of 90 retained matching log
files.
- Routed the Windows sandbox setup helper through the same rolling
writer.
- Added helpers for resolving the current daily sandbox log path so
future feedback upload work can use the same filename logic.
- Updated tests and test diagnostics to read the dated daily log file.

This intentionally does not include sandbox logs in `/feedback` yet;
scrubbing and attachment behavior can happen in a follow-up.

## Testing

- `cargo fmt -p codex-windows-sandbox`
- `cargo check -p codex-windows-sandbox`
- `cargo test -p codex-windows-sandbox`
- `cargo test -p codex-windows-sandbox logging::tests`
- `cargo clippy -p codex-windows-sandbox --all-targets -- -D warnings`
2026-05-22 11:37:01 -07:00
adams-oai
865ca936db Add new enterprise requirement gate (#23736)
Add new enterprise requirement gate.

Validation:
- `cargo test -p codex-config --lib`
- `cargo test -p codex-app-server-protocol --lib`
- `cargo test -p codex-tui --lib debug_config`
- `cargo test -p codex-app-server --lib` *(fails: stack overflow in
`in_process::tests::in_process_start_initializes_and_handles_typed_v2_request`;
reproduces when run alone)*
2026-05-22 11:33:44 -07:00
jif-oai
162a6e746b app-server: drop legacy profile config surface (#24067)
## Why

Legacy `[profiles.<name>]` config tables and the legacy `profile`
selector are being retired in favor of profile files selected with
`--profile <name>`. After #23886 removed the CLI-side legacy profile
plumbing, the app-server config surface still exposed those fields and
still carried conversion code for the old protocol shape.

## What changed

- Remove `profile`, `profiles`, and `ProfileV2` from the app-server
config protocol/schema output so `config/read` no longer returns legacy
profile config.
- Drop the old v1 `UserSavedConfig` profile conversion path from
`config`.
- Reject new app-server config writes under `profiles.*` with the same
migration direction used for `profile`, while still allowing callers to
clear existing legacy profile tables.
- Refresh app-server config coverage and the experimental API README
example around the remaining `Config` nesting path.

## Verification

- Added config-manager coverage that `config/read` omits legacy profile
config, `profiles.*` writes are rejected, and existing legacy profile
tables can still be cleared.
- Updated the v2 config RPC test to cover the rejected `profiles.*`
batch-write path.
2026-05-22 19:41:39 +02:00
Michael Bolin
c0b16cfc6b cli: infer host sandbox backend (#24102)
## Why

`codex sandbox` previously required an OS subcommand like `linux`,
`macos`, or `windows`, even though the command can only run the sandbox
backend available on the current host. That made the CLI imply a
cross-OS choice that does not exist.

## What changed

- Collapse `codex sandbox <os>` into `codex sandbox [COMMAND]...` by
wiring the `sandbox` parser directly to the host-specific backend args
with `cfg`.
- Keep the existing backend runners for Seatbelt, Linux sandbox, and
Windows restricted token.
- Rename the public Windows debug sandbox runner to
`run_command_under_windows_sandbox` for clarity.
- Update the Rust sandbox docs and related README references to describe
host OS selection and avoid pointing readers at legacy `sandbox_mode`
config.

## Arg0 compatibility

The `codex-linux-sandbox` helper path is still handled before normal CLI
parsing. `arg0_dispatch()` checks whether the executable basename is
`codex-linux-sandbox` and directly calls
`codex_linux_sandbox::run_main()`, so removing the `sandbox linux`
parser branch does not affect the arg0 helper flow.

## Verification

- `cargo test -p codex-cli`
- `cargo test -p codex-arg0`
- `just fix -p codex-cli`
2026-05-22 10:23:59 -07:00
jif-oai
f55f864b9f tui: make codex-tui.log opt-in (#24081)
## Why

The TUI currently creates a shared plaintext `codex-tui.log` under the
default log directory. That append-only file can keep growing across
runs even though the TUI already records diagnostics in bounded local
stores.

Make the plaintext file log an explicit troubleshooting choice instead
of a default side effect.

This is possible because logs are also stored in the DB with proper
rotation

## What changed

- Only install the TUI file logging layer when `log_dir` is explicitly
set.
- Remove the prior `codex-tui.log` at startup before an opt-in file
layer is created.
- Clarify the `log_dir` config/schema text and `docs/install.md` example
so users opt in with `codex -c log_dir=...` when they need a plaintext
log.
2026-05-22 17:19:51 +00:00
rhan-oai
dac98cb635 retry remote compaction v2 requests (#23951)
## Why

Remote compaction v2 sends a normal `/responses` request with a
compaction trigger. It should follow the retry semantics used by normal
Responses streaming calls for transient stream/request failures, while
keeping a smaller per-transport retry budget because compact attempts
can run much longer than normal turns.

## What changed

- Add a v2 compaction retry loop that uses `stream_max_retries`,
matching normal Responses turn retry mechanics.
- Cap the compact v2 retry budget at 2 retries per transport with
`min(stream_max_retries, 2)`.
- Retry retryable request-open and post-open stream collection failures
through the same loop.
- Use the existing 200ms exponential backoff and requested retry delay
handling used by normal turn retries.
- Emit the same `Reconnecting... n/max` stream-error notification
pattern.
- Fall back from WebSockets to HTTPS after the compact v2 stream retry
budget is exhausted, then reset the retry counter for HTTPS.
- Keep final remote-compaction failure logging after retries/fallback
are exhausted.
- Treat compact stream EOF before `response.completed` as a retryable
stream failure.
- Add compact v2 regression coverage with `request_max_retries = 0` and
`stream_max_retries = 2`, covering both request-open failure and
opened-stream EOF in one end-to-end test.

## Tests

- `just fmt`
- `cargo test -p codex-core remote_compact_v2`
- `just fix -p codex-core`
2026-05-22 10:14:14 -07:00
anp-oai
d53e68954a Prefer just test over cargo test in docs (#23910)
`cargo test` for the core and other crates fails on a fresh macOS
checkout without the right stack size variable. This change encourages
using the just test command that sets the environment up correctly.

As a bonus, this should encourage agents to get more benefit out of
nextest's parallel execution.
2026-05-22 16:58:14 +00:00
Owen Lin
cff960896c fix(app-server): fix optional bool annotations (#24099)
`#[serde(default)]` wasn't sufficient for our generated TS types to
reflect that clients didn't have to set them. We also need
`skip_serializing_if = "std::ops::Not::not"`. This is already a rule in
our agents.md file.
2026-05-22 16:52:53 +00:00
Channing Conger
014f19af5f ci: Use codex produced v8 artifacts for release builds (#23934)
Updates our build script to pull down the artifacts like we do in CI for
building v8 into our targets.

This changes the flow so that we now pre-install rusty v8 assets for all
of our release targets from pre-built in workflow.
Secondarily if running it locally we now optionally pull the assets down
on python run assuming the user hasn't set the proper values, it then
provides them.

Sorry for the miss here.
2026-05-22 09:42:08 -07:00
jif-oai
932f72c225 fix: reject legacy profile selectors (#24059)
## Why

`--profile` now selects `<name>.config.toml`, so the legacy `profile`
selector should not be reintroduced through config write or MCP tool
paths. A matching legacy selector in base user config also needs the
same migration guard as a matching legacy `[profiles.<name>]` table so
profile loading fails with one clear migration error instead of mixing
the old and new profile models.

## What

- reject non-null app-server config writes to the top-level legacy
`profile` selector
- make `--profile <name>` reject base user config that still selects the
same legacy `profile = "<name>"` value, alongside the existing matching
legacy profile-table guard
- reject removed MCP `codex` tool fields such as `profile` by denying
unknown tool-call parameters and exposing that restriction in the
generated schema
- add regression coverage for the app-server write paths, config loader
guard, and MCP tool input/schema behavior

## Verification

- targeted regression tests cover the new app-server, config loader, and
MCP rejection paths
2026-05-22 13:19:47 +02:00
jif-oai
47476e8a8a otel: drop legacy profile usage telemetry (#24061)
## Summary
- drop the dead legacy profile usage metric and active-profile
conversation-start fields
- update role comments so they describe provider and service-tier
preservation without legacy config-profile wording
- pair the code cleanup with the file-backed profile docs update in
openai/developers-website#1476

## Testing
- `just fmt`
- `cargo test -p codex-otel`
- `cargo test -p codex-core` *(fails: existing stack overflow in
`mcp_tool_call::tests::guardian_mode_mcp_denial_returns_rationale_message`)*
- `cargo test -p codex-core --lib
mcp_tool_call::tests::guardian_mode_mcp_denial_returns_rationale_message`
*(fails with the same stack overflow)*
2026-05-22 13:14:44 +02:00
jif-oai
5865ec45e5 Avoid config snapshots in live agent subtree traversal (#24057)
## Why
`/feedback` asks `ThreadManager` for the selected agent subtree before
it uploads logs. The previous live subtree path reconstructed
parent-child links by iterating every loaded thread and awaiting each
thread config snapshot, so unrelated loaded-thread state could stall
feedback subtree enumeration.

The loaded-thread set already belongs to
[`ThreadManagerState`](50e6644c94/codex-rs/core/src/thread_manager.rs).
Reading thread-spawn parents from the captured `CodexThread` session
sources at that boundary keeps unload and resume behavior manager-owned
while avoiding per-session config inspection.

## What Changed
- expose parent-child thread-spawn edges for loaded, non-internal
threads from `ThreadManagerState`
- build the live child map from those edges while keeping agent metadata
lookup and ordering in `AgentControl`
- add regression coverage for live subtree enumeration when no state DB
is available

## Validation
- `git diff --check`
- local Rust tests not run per request
2026-05-22 13:06:40 +02:00
jif-oai
2c6605ab35 config: remove legacy profile write paths (#24055)
## Why

[#23883](https://github.com/openai/codex/pull/23883) moved the
user-facing `--profile` flag onto profile v2 and
[#23886](https://github.com/openai/codex/pull/23886) removed CLI
forwarding for the legacy profile-v1 path. Core and TUI config
persistence still carried `active_profile` and
`ConfigEditsBuilder::with_profile`, which let later writes continue
targeting legacy `[profiles.<name>]` tables after profile selection
moved to profile-v2 config files.

## What

- Remove legacy profile routing from
[`ConfigEditsBuilder`](4b38e9c22e/codex-rs/core/src/config/edit.rs (L1064-L1294)),
so core config edits no longer carry `with_profile` or infer
`[profiles.*]` write targets from a `profile` key.
- Drop `active_profile` plumbing from runtime `Config`, TUI
startup/state, app-server config override forwarding, and Windows
sandbox setup persistence.
- Make app-server-backed TUI config edits use unscoped model,
service-tier, feature, Auto-review, plan-mode, and Windows sandbox paths
through
[`tui/src/config_update.rs`](4b38e9c22e/codex-rs/tui/src/config_update.rs (L43-L112)).
- Update config edit coverage so legacy `profile` state stays untouched
by direct model writes, and remove tests whose only contract was the
deleted profile-scoped persistence path.

## Testing

- Not run locally.
2026-05-22 12:50:42 +02:00
jif-oai
fd72e99384 config: remove legacy profile v1 resolution (#24051)
## Why

[#23883](https://github.com/openai/codex/pull/23883) moved user-facing
`--profile` selection onto profile v2, and
[#23886](https://github.com/openai/codex/pull/23886) removed the old CLI
`config_profile` override path. Core still had a second legacy path:
`profile = "..."` could select `[profiles.*]` values while runtime
config was built. Keeping that resolver alive preserves the old
precedence model and profile-carrying surfaces even though profile
selection now points at `$CODEX_HOME/<name>.config.toml`.

## What

- Reject legacy top-level `profile = "..."` config while loading runtime
config, with an error that points callers at `--profile <name>` and
`<name>.config.toml` in the [core load
path](3d923366ec/codex-rs/core/src/config/mod.rs (L2524-L2531)).
- Remove the remaining profile-v1 merge points from runtime config
resolution, including features, permissions, model/provider selection,
web search, Windows sandbox settings, TUI settings, role reloads, and
OSS provider lookup.
- Drop the leftover profile override surface from
[`ConfigOverrides`](3d923366ec/codex-rs/core/src/config/mod.rs (L2118-L2148))
and from the MCP server `codex` tool schema.
- Prune profile-precedence tests that only exercised the removed
resolver and replace them with rejection coverage for the legacy
selector.

## Testing

- Not run in this metadata pass.
- Added
[`legacy_profile_selection_is_rejected`](3d923366ec/codex-rs/core/src/config/config_tests.rs (L7942-L7965))
coverage for the new runtime guard.
2026-05-22 12:13:52 +02:00
jif-oai
ed80e5f558 mcp: surface profile migration guidance under --profile (#23890)
## Why

`codex --profile <name> mcp ...` should reach the same profile-v2
migration guard as runtime commands. Otherwise legacy
`[profiles.<name>]` users see the generic command-scope rejection
instead of the existing guidance to move settings into
`$CODEX_HOME/<name>.config.toml`.

## What

- Allow `codex mcp` through the `--profile` subcommand gate.
- Pass profile loader overrides into the MCP entry point only to
validate profile-v2 migration when a profile is present.
- Keep MCP add/remove/list/get/login/logout behavior otherwise
unchanged; this does not add profile-scoped MCP server management.
- Cover the legacy profile migration error for `codex --profile work mcp
list`.

## Testing

- `cargo test -p codex-cli`
2026-05-22 10:40:33 +02:00
rreichel3-oai
b14f11d3d2 [codex] Enable Node env proxy for managed network proxy (#23905)
## Summary
- set `NODE_USE_ENV_PROXY=1` when Codex applies managed network proxy
environment overrides
- keep the Node opt-in in the proxy environment key set used by
shell/runtime env handling
- cover the new env var in the focused network proxy env test

## Why
Codex already sets HTTP proxy environment variables for child processes
when the managed network proxy is active. Node's built-in network
behavior needs the `NODE_USE_ENV_PROXY` opt-in to honor those env vars,
so Node-based skill scripts can otherwise skip the managed proxy path
and fail under restricted network access.

## Validation
- `just fmt` in `codex-rs`
- `cargo test -p codex-network-proxy` in `codex-rs`
2026-05-22 01:27:25 -04:00
anp-oai
c83ba22359 Allow parallel MCP tool calls when annotated readOnly (#23750)
## Summary
- Treat MCP tools with `readOnlyHint: true` as parallel-safe even when
`supports_parallel_tool_calls` is unset or `false`.
- Keep server-level `supports_parallel_tool_calls` as an additive
override for non-read-only tools.
- Add focused unit coverage for the MCP handler eligibility decision.
- Update RMCP integration coverage to keep the serial baseline on a
mutable tool, verify read-only concurrency without server opt-in, and
preserve the server opt-in concurrency path separately.

## Testing
- `just fmt`
- `cargo test -p codex-core --lib tools::handlers::mcp::tests::`
- `cargo test -p codex-core --test all
stdio_mcp_read_only_tool_calls_run_concurrently_without_server_opt_in`
- `cargo test -p codex-core --test all
stdio_mcp_parallel_tool_calls_opt_in_runs_concurrently`
- `cargo test -p codex-rmcp-client`
2026-05-21 20:40:34 -07:00
Celia Chen
464ab40dfa feat: best-effort compact large tool schemas (#23904)
## Why

The `dev/cc/ref-def` branch preserves richer JSON Schema detail for
connector tools, including `$defs` and nested shapes. That improves
fidelity, but it pushes the largest connector schemas well past the
intended tool-schema budget. This PR adds a best-effort compaction pass
for unusually large tool input schemas so the p99 and max tails stay
small while ordinary schemas are left alone.

## What Changed

- Added best-effort large-schema compaction in
`codex-rs/tools/src/json_schema.rs` after schema sanitization and
definition pruning.
- Compaction runs as a waterfall only while the compact JSON budget
proxy is exceeded:
  1. Strip schema `description` metadata.
  2. Drop root `$defs` / `definitions`.
  3. Collapse deep nested complex schema objects to `{}`.
- Kept top-level argument names and immediate schema shape where
possible.

## Corpus Results

Scope: 2,025 schemas under `golden_schemas`, all parsed successfully.
Token count is `o200k_base` over compact JSON from
`parse_tool_input_schema`.

| Percentile | Before `origin/main` `4dbca61e20` | After branch
`dev/cc/ref-def` `f9bf071758` | After this PR |
|---|---:|---:|---:|
| p0 | 9 | 9 | 9 |
| p10 | 59 | 63 | 63 |
| p25 | 81 | 86 | 86 |
| p50 | 114 | 127 | 125 |
| p75 | 174 | 205 | 202 |
| p90 | 295 | 335 | 322 |
| p95 | 391 | 526 | 422 |
| p99 | 794 | 1,303 | 689 |
| max | 2,836 | 3,337 | 887 |

After this PR, `0 / 2,025` schemas are over 1k tokens.

### Compaction Savings

These are cumulative waterfall stages over the same corpus. Later passes
only run for schemas that are still over the compact JSON budget proxy.

| Stage | Total tokens | Step savings | Schemas changed by step |
|---|---:|---:|---:|
| No compaction | 391,862 | - | - |
| Strip schema `description` metadata | 350,961 | 40,901 | 66 |
| Drop root `$defs` / `definitions` | 340,683 | 10,278 | 13 |
| Collapse deep complex schemas to `{}` | 335,875 | 4,808 | 6 |
2026-05-22 01:26:17 +00:00
sayan-oai
7e802b22f1 Expose conversation history to extension tools (#23963)
## Why

Extension tools that need conversation context should be able to read it
from the live tool invocation instead of reaching into thread
persistence themselves.

## What changed

- Add a `ConversationHistory` snapshot to extension `ToolCall`s and
populate it from the current raw in-memory response history.
- Expose all history items at this boundary so each extension can filter
and bound the subset it needs before consuming or forwarding it.
- Cover the adapter and registry dispatch paths and update existing
extension tests that construct `ToolCall` literals.

## Test plan

- `cargo test -p codex-tools`
- `cargo test -p codex-extension-api`
- `cargo test -p codex-goal-extension`
- `cargo test -p codex-memories-extension`
- `cargo test -p codex-core passes_turn_fields_to_extension_call`
- `cargo test -p codex-core
extension_tool_executors_are_model_visible_and_dispatchable`
2026-05-22 01:11:47 +00:00
Celia Chen
0cec508148 feat: support local refs and defs in tool input schemas (#23357)
# Why

Some connector tool input schemas use local JSON Schema references and
definition tables to avoid duplicating large nested shapes. Codex
previously lowered these schemas into the supported subset in a way that
could discard `$ref`-only schema objects and lose the corresponding
definitions, which made non-strict tool registration less faithful than
the original connector schema.

This keeps the existing minimal-lowering policy: Codex still does not
raw-pass through arbitrary JSON Schema, but it now preserves local
reference structure that fits the Responses-compatible subset and prunes
definition entries that cannot be reached by following `$ref`s from the
root schema after sanitization, including refs found transitively inside
other reachable definitions. The pruning matters because Responses
parses definition tables even when entries are unused, so keeping dead
definitions wastes prompt tokens.

# What changed

- Added `$ref`, `$defs`, and legacy `definitions` fields to the tool
`JsonSchema` representation.
- Updated `parse_tool_input_schema` lowering so `$ref`-only schema
objects survive sanitization instead of becoming `{}`.
- Sanitized definition tables recursively and dropped malformed
definition tables so non-strict registration degrades gracefully.
- Added reachability pruning for root definition tables by starting from
refs outside definition tables, then following refs inside reachable
definitions.
- Added JSON Pointer decoding for local definition refs such as
`#/$defs/Foo~1Bar`.

# Verification
ran local golden-schema probes against representative connector schemas
to validate behavior on real generated schemas:

| Golden schema | Before bytes | After bytes | `$defs` before -> after |
`$ref` before -> after | Result |
|---|---:|---:|---:|---:|---|
| `google_calendar/create_space` | 7111 | 4526 | 7 -> 7 | 7 -> 7 | all
definitions preserved because all are reachable |
| `figma/apply_file_variable_changes` | 4609 | 999 | 8 -> 5 | 8 -> 5 |
unused defs pruned after unsupported `oneOf` shapes lower away |
| `snowflake/list_catalog_integrations` | 1380 | 404 | 3 -> 0 | 0 -> 0 |
all defs pruned because none are referenced |
| `dropbox/create_shared_link` | 8894 | 1836 | 14 -> 4 | 9 -> 4 | only
defs reachable from the root schema after sanitization are retained,
including transitively through other retained defs |

Token increase across golden schema due to this change:
<img width="817" height="366" alt="Screenshot 2026-05-19 at 1 47 04 PM"
src="https://github.com/user-attachments/assets/d5c80fe9-da85-41e6-8ac7-a01d1e0b0f71"
/>
2026-05-22 00:32:14 +00:00
Eric Traut
5a6e905994 Fix auto-review permission profile override (#23956)
## Summary
The auto-review runtime sync path was assigning a raw
`PermissionProfile` into `runtime_permission_profile_override`, whose
field now expects `RuntimePermissionProfileOverride`. That broke the TUI
Bazel build.

This changes the assignment to store
`RuntimePermissionProfileOverride::from_config(&self.config)`, matching
the other runtime override paths and preserving the active profile and
network metadata with the permission profile.
2026-05-21 16:52:36 -07:00
CHARLESPALEN-OAI
5381240f57 Add Bedrock Mantle GovCloud region (#23860)
## Summary
- Add us-gov-west-1 to the Bedrock Mantle supported region list
- Cover the GovCloud endpoint URL in the existing base_url unit test

## Test
- cargo test -p codex-model-provider
2026-05-21 19:19:26 -04:00
xl-openai
247e22a9f6 fix: Allow plugin skills to share plugin-level icon assets (#23776)
Thread the plugin root through plugin skill loading so skill interface
icons can reference shared plugin assets, such as ../../assets/logo.svg.
2026-05-21 16:11:59 -07:00
Eric Traut
e8378c7f0c [3 of 4] tui: route feature and memory toggles through app server (#22915)
## Why
Experimental feature toggles and memory settings can update several
related config values in one interaction. Keeping those writes local in
a remote TUI session is especially dangerous because the UI can diverge
from the app-server config while also leaving behind partially stale
supporting keys.

This is **[3 of 4]** in a stacked series that moves TUI-owned config
mutations onto app-server APIs.

## What changed
- Routed feature flag persistence through app-server batch writes,
including the supporting reviewer and permission updates used by
guardian approval.
- Routed Windows sandbox mode persistence and legacy Windows feature
cleanup through app-server writes.
- Routed memory settings through app-server batch writes and updated the
TUI tests to exercise the embedded app-server path.

## Config keys affected
- `features.<feature_key>`
- `profiles.<profile>.features.<feature_key>`
- `approval_policy`
- `sandbox_mode`
- `approvals_reviewer`
- `windows.sandbox`
- `features.experimental_windows_sandbox`
- `features.elevated_windows_sandbox`
- `features.enable_experimental_windows_sandbox`
- Profile-scoped Windows legacy feature variants under
`profiles.<profile>.features.*`
- `memories.use_memories`
- `memories.generate_memories`
- Profile-scoped memory variants under `profiles.<profile>.memories.*`

## Suggested manual validation
- Connect the TUI to a remote app server, toggle guardian approval on
and off, and confirm the remote config updates
`features.guardian_approval`, reviewer state, approval policy, and
sandbox mode coherently.
- Toggle a default-false experimental feature at the root level, disable
it again, and confirm the key clears instead of lingering as an
unnecessary explicit `false`.
- Change memory settings and confirm the remote config updates both
memory keys while the running TUI reflects the new state.
- On Windows, switch sandbox mode through the TUI and confirm
`windows.sandbox` is updated while the legacy Windows feature keys are
cleared.

## Stack
1. [#22913](https://github.com/openai/codex/pull/22913) `[1 of 4]`
primary settings writes
2. [#22914](https://github.com/openai/codex/pull/22914) `[2 of 4]` app
and skill enablement
3. [#22915](https://github.com/openai/codex/pull/22915) `[3 of 4]`
feature and memory toggles
4. [#22916](https://github.com/openai/codex/pull/22916) `[4 of 4]`
startup and onboarding bookkeeping
2026-05-21 16:03:11 -07:00
Abhinav
16d85e2708 Add subagent identity to hook inputs (#22882)
# What

When a normal hook fires inside a thread-spawned subagent, Codex now
includes these optional top-level fields in the hook input:

- `agent_id`: the child thread id
- `agent_type`: the subagent role

Root-agent hook inputs omit these fields. `SubagentStart` and
`SubagentStop` keep their existing required `agent_id` and `agent_type`
fields because those events are inherently subagent-scoped.

This does not change matcher behavior. Tool hooks still match on tool
name, compact hooks still match on trigger, and `UserPromptSubmit` still
ignores matchers. Only `SubagentStart` and `SubagentStop` match on
`agent_type`.
2026-05-21 14:54:01 -07:00
Anton Panasenko
58be470d15 fix(remote-control): retry after auth recovery (#23775)
## Why

When remote control hits an auth failure such as a revoked or reused
refresh token, the websocket loop falls into reconnect backoff. If the
user fixes auth while that loop is sleeping, remote control can stay
offline until the old retry timer expires because nothing wakes the loop
or resets its exhausted auth recovery state.

## What Changed

Added an auth-change watch on `AuthManager` for refresh-relevant cached
auth updates.

The remote-control websocket loop now subscribes to that signal, resets
`UnauthorizedRecovery` and reconnect backoff when auth changes, and
retries immediately instead of waiting for the previous delay.

Updated the remote-control transport test to verify that reloading auth
with the now-available account id wakes enrollment before the prior
retry delay.

## Verification

`cargo test -p codex-app-server-transport
remote_control_waits_for_account_id_before_enrolling`
2026-05-21 14:38:30 -07:00
Francis Chalissery
05cf2fc4ce [codex] Make thread search case-insensitive (#23921)
## Summary
- make rollout content search prefilter rollout files case-insensitively
- keep the no-ripgrep fallback scan and visible snippet matcher aligned
with that behavior
- cover a lowercase `thread/search` query matching mixed-case
conversation content

## Why
The rollout-backed `thread/search` path used exact string matching in
both its `rg` prefilter and semantic snippet generation. A content
result could be missed solely because the query casing did not match the
stored conversation text.

## Validation
- `just fmt`
- `cargo test -p codex-app-server thread_search_returns_content_matches`
- `cargo test -p codex-rollout`
- `just bazel-lock-update`
- `just bazel-lock-check`
- `cargo build -p codex-cli`
- launched a local Electron dev instance with the rebuilt CLI binary
2026-05-21 14:14:01 -07:00
Michael Bolin
b20e969f23 npm: remove legacy package artifact synthesis (#23836)
## Why

`rust-release` now publishes `codex-package-<target>.tar.gz` as the
canonical native package payload. npm staging should consume those
archives directly instead of keeping legacy synthesis code that fetched
`rg`, copied standalone binaries, and rebuilt an approximate package
layout.

That also means the package builder should not know the internal shape
of `codex-package`. It should extract and copy the target payload
wholesale so future layout changes stay localized to the archive
producer.

The release job stages `codex`, `codex-responses-api-proxy`, and
`codex-sdk` together, so native artifact download should be filtered,
observable, and shared across component installs. Since that native
hydration is now only used by release staging, keeping a separate
`install_native_deps.py` CLI adds an extra wrapper without a real
caller.

## What Changed

- Removed legacy `codex-package` synthesis and related compatibility
flags from npm staging.
- Folded the remaining native artifact hydration code into
`scripts/stage_npm_packages.py` and deleted
`codex-cli/scripts/install_native_deps.py`.
- Made platform package staging copy the full extracted target directory
instead of enumerating package entries.
- Kept non-`codex-package` native components under their component
directory name instead of using a legacy destination map.
- Split native staging by component set while sharing one
workflow-artifact cache across the invocation.
- Changed workflow artifact download to select target artifacts by name,
print sizes/progress, and reuse cached artifacts.
- Removed the implicit `CI=true` default from `build_npm_package.py`;
local CI-shaped runs should set that environment explicitly.
- Kept `npm pack` cache/log output in its temporary directory so packing
does not write to the user npm cache.

## Verification

- `python3 -m py_compile scripts/stage_npm_packages.py
codex-cli/scripts/build_npm_package.py`
- `python3 -m unittest discover -s scripts/codex_package -p "test_*.py"`
- `scripts/stage_npm_packages.py --help`
- `codex-cli/scripts/build_npm_package.py --help`
- Ran the release-shaped staging command from `rust-release.yml` against
workflow run https://github.com/openai/codex/actions/runs/26240748758
with `CI=true` set locally to match GitHub Actions:

```sh
CI=true python3 ./scripts/stage_npm_packages.py \
  --release-version 0.133.0 \
  --workflow-url https://github.com/openai/codex/actions/runs/26240748758 \
  --package codex \
  --package codex-responses-api-proxy \
  --package codex-sdk
```

That completed successfully, downloaded only the six target artifacts
once, reused the cache for `codex-responses-api-proxy`, and produced all
nine npm tarballs. Generated tarballs and staging/artifact temp dirs
were cleaned afterward.
2026-05-21 20:43:48 +00:00
Abhinav
24faf49b2a Remove plugin hooks feature flag (#22552)
# Why

This is a follow-up stacked on top of the `plugin_hooks` default-on
change. Once we are comfortable making plugin hooks part of the normal
plugin behavior, the separate feature flag stops buying us much and
leaves extra branching/cache state behind.

# What

- remove the `PluginHooks` feature and generated config-schema entries
- make plugin hook loading/listing follow plugin enablement directly
- drop plugin-manager cache/state that only existed to distinguish
hook-flag toggles
- remove tests and fixtures that modeled `plugin_hooks = true/false`
2026-05-21 19:15:18 +00:00
Francis Chalissery
ac0bff27e7 [codex] Add rollout-backed thread content search (#23519)
## Summary
- add experimental `thread/search` for local rollout-backed thread
search using `rg` over JSONL rollouts
- return search-specific result rows with optional previews instead of
storing preview data on `StoredThread` or ordinary `Thread` responses
- keep `thread/list` separate from full-content search and document the
new app-server surface

## Testing
- `cargo test -p codex-app-server-protocol`
- `cargo test -p codex-app-server
thread_search_returns_content_and_title_matches -- --nocapture`
2026-05-21 11:52:24 -07:00
Eric Traut
4acb456bfe TUI: skip goal replace prompt for completed goals (#23792)
## Why
Users reported that the replacement confirmation feels unnecessary when
the current thread goal is already complete. In that state, `/goal
<objective>` is starting fresh rather than interrupting active work.

## What changed
`/goal <objective>` now skips the replace confirmation when the existing
goal has `complete` status and uses the existing fresh replacement path.
Goals that are active, paused, blocked, usage-limited, or budget-limited
still require confirmation before being replaced.
2026-05-21 10:45:43 -07:00
starr-openai
de80fa6e31 Reconnect disconnected exec-server websocket clients with fresh sessions (#23867)
## Summary
- replace the one-shot lazy remote exec-server cache with a
lock-protected current client
- when the cached websocket client is already disconnected, create one
fresh websocket client/session on the next `get()`
- keep existing disconnect failure behavior for old process sessions and
HTTP body streams; do not add session resume or request retry

## Why
The prior PR direction was trying to grow into session restore: resume
the old `session_id`, preserve existing process handles, and add
reconnect retry policy. That is more machinery than we want for this
slice.

For now, the useful minimum is simpler: later fresh remote operations
should not be stuck behind a dead cached websocket client, but anything
already attached to the dead connection should fail loudly through the
existing disconnect path. The server already has detached-session
cleanup via its existing TTL, so this PR does not need to add
client-side session preservation.

## What Changed
- `LazyRemoteExecServerClient::get()` now keeps the current concrete
client in a small mutex-protected cache plus one async connect lock.
- If that cached client is still connected, `get()` returns it.
- If that cached websocket client has observed the transport close,
`get()` creates a brand-new websocket client with a brand-new
exec-server session and replaces the cache.
- If that cached client is stdio-backed, behavior stays one-shot: the
dead client is returned and later work surfaces the existing disconnect
error.
- No `resume_session_id`, backoff, request replay, or existing
`RemoteExecProcess` rebinding is added here.
- Added focused websocket coverage that proves two concurrent `get()`
calls after disconnect share one fresh replacement client/session.
2026-05-21 18:43:45 +02:00
Eric Traut
b132fec000 Improve /goal error messages for ephemeral sessions (#23796)
## Why

When a user runs `/goal` in a temporary session, the TUI can currently
surface an internal app-server failure such as `thread/goal/get failed
in TUI`. That message is technically true, but it does not explain the
actual constraint: goals require a saved session because goal state is
persisted with the thread.

This is especially confusing when `codex doctor` reports the background
app-server as running in ephemeral mode, since that wording is easy to
conflate with ephemeral thread/session behavior.

## What changed

- Added a TUI-side formatter for thread-goal RPC failures in
`codex-rs/tui/src/app/thread_goal_actions.rs`.
- Detects app-server/core errors that indicate goals are unsupported for
an ephemeral thread/session.
- Replaces the internal RPC failure with a user-facing explanation:

```text
Goals need a saved session. This session is temporary.
Run `codex` to start a saved session, or `codex resume` / `/resume` to reopen one.
```

- Preserves the existing generic failure wording for non-ephemeral goal
errors.

## Verification

- `cargo test -p codex-tui thread_goal_error_message --lib`

I also tried `cargo test -p codex-tui`; it built successfully but the
test runner aborted in an unrelated side-thread stack overflow
(`app::tests::discard_side_thread_removes_agent_navigation_entry`),
which reproduced when run by itself.
2026-05-21 09:33:17 -07:00
Michael Bolin
c07f66c9ec packaging: move rg manifest out of npm bin (#23833)
## Why

Installing `@openai/codex` currently places a Dotslash `rg` manifest at
`node_modules/@openai/codex/bin/rg`, even though the native optional
dependency already ships the actual helper under
`vendor/<target>/codex-path/rg`. The launcher prepends that `codex-path`
directory, so the top-level `bin/rg` file is redundant in the npm
install.

The remaining direct consumers of the manifest are package-building
paths: `scripts/codex_package/ripgrep.py` and
`codex-cli/scripts/install_native_deps.py`. Keeping the manifest under
`codex-cli/bin` makes it look like a shipped npm binary, so this moves
it next to the package-builder code that owns it. The checked-in
`@openai/codex` package metadata should likewise describe only the meta
package payload; generated platform packages continue to publish
`vendor`.

## What Changed

- Moved the Dotslash ripgrep manifest from `codex-cli/bin/rg` to
`scripts/codex_package/rg`.
- Updated the package builder, npm native-artifact hydrator, README, and
CLI help text to reference the new manifest location.
- Stopped `codex-cli/scripts/build_npm_package.py` from copying `rg`
into the `@openai/codex` meta package.
- Narrowed the checked-in meta package `files` whitelist to
`bin/codex.js`.

## Verification

- `python3 -m unittest discover -s scripts/codex_package -p "test_*.py"`
- `python3 -m unittest discover -s codex-cli/scripts -p "test_*.py"`
- `python3 -m py_compile codex-cli/scripts/build_npm_package.py
codex-cli/scripts/install_native_deps.py
scripts/codex_package/ripgrep.py scripts/codex_package/cli.py
scripts/stage_npm_packages.py`
- `codex-cli/scripts/build_npm_package.py --package codex --version
0.0.0-test --pack-output <tmp>/codex-meta-no-vendor.tgz`
- `tar -tf <tmp>/codex-meta-no-vendor.tgz` showed only
`package/bin/codex.js`, `package/package.json`, and `package/README.md`.
- Direct staging check showed `codex` uses `files: ["bin/codex.js"]`
while `codex-darwin-arm64` still uses `files: ["vendor"]`.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23833).
* #23836
* __->__ #23833
2026-05-21 15:48:42 +00:00
viyatb-oai
fcff0d6c52 tui: plumb permission profile selection (#23708)
## Why

The named-profile `/permissions` picker needs a small TUI action path
that can select permission profiles without folding the menu UI and
profile metadata into the same review.

## What changed

- Carry permission-profile selections through the TUI app event flow.
- Persist selected profiles while preserving the existing approval
settings and guardrail prompts.
- Keep the legacy `/permissions` picker behavior in this layer; the
profile-mode menu stays in the follow-up PR.

## Stack

1. [#22931](https://github.com/openai/codex/pull/22931):
runtime/session/network propagation for active permission profiles.
2. **This PR**: TUI selection plumbing and guardrail flow.
3. [#21559](https://github.com/openai/codex/pull/21559): profile-aware
`/permissions` menu and custom profile display.

<img width="1632" height="1186" alt="image"
src="https://github.com/user-attachments/assets/69ddcd5e-b57c-468d-8c1d-246916323c15"
/>

## Validation

- `git diff --cached --check` before commit.
- Full test run skipped at the user request while pushing the split
stack.
2026-05-21 12:26:36 -03:00
jif-oai
e0e304b123 cli: remove legacy profile v1 plumbing (#23886)
## Why

[#23883](https://github.com/openai/codex/pull/23883) moved the
user-facing `--profile` flag onto profile v2. The shared CLI option
layer still carried the old `config_profile` slot and several CLI
entrypoints still copied that value into legacy config overrides.
Leaving that path around makes the CLI surface look like it still
selects legacy `[profiles.*]` state even though `--profile` now means
`$CODEX_HOME/<name>.config.toml`.

## What

- Remove the legacy `config_profile` field and merge/copy path from
[`SharedCliOptions`](95baaf7292/codex-rs/utils/cli/src/shared_options.rs (L8-L177)).
- Stop forwarding profile-v1 overrides from CLI, exec, TUI, doctor,
debug, feature, and exec-server paths; runtime profile selection remains
on `config_profile_v2` through
[`loader_overrides_for_profile`](95baaf7292/codex-rs/cli/src/main.rs (L1606-L1619)).
- Resolve local OSS provider selection from the base config in exec and
TUI now that the legacy profile argument is gone.

## Testing

- Not run (cleanup-only follow-up to #23883).
2026-05-21 17:21:37 +02:00
starr-openai
298e5cfce1 Route MCP servers through explicit environments (#23583)
## Summary
- route each configured MCP server through an explicit per-server
`environment_id` instead of a manager-wide remote toggle
- default omitted `environment_id` to `local`, resolve named ids through
`EnvironmentManager`, and fail only the affected MCP server when an
explicit id is unknown
- keep local stdio on the existing local launcher path for now, while
named-environment stdio uses the selected environment backend and
requires an absolute `cwd`
- allow local HTTP MCP servers to keep using the ambient HTTP client
when no local `Environment` is configured; named-environment HTTP MCPs
use that environment's HTTP client

## Validation
- devbox Bazel build: `bazel build --bes_backend= --bes_results_url=
//codex-rs/cli:codex //codex-rs/rmcp-client:test_stdio_server
//codex-rs/rmcp-client:test_streamable_http_server`
- devbox app-server config matrix with real `config.toml` /
`environments.toml` files covering omitted local, explicit local,
omitted local under remote default, explicit remote stdio, local HTTP
without local env, explicit remote HTTP, local stdio without local env,
unknown explicit env, and remote stdio without `cwd`
2026-05-21 17:19:54 +02:00
Michael Bolin
97b390fbd4 docs: add description to codex-cli/package.json (#23835)
Fix this eyesore where our lack of a `"description"` was causing our
`README.md` to be used for previews on npm.

<img width="1291" height="178" alt="image"
src="https://github.com/user-attachments/assets/a9bc08c5-0def-4755-8bcc-0c90e096b9c2"
/>
2026-05-21 08:19:50 -07:00
jif-oai
8a511d5881 cli: rename profile v2 flag to --profile (#23883)
## Why

Profile v2 is taking over the user-facing profile selection path, so the
CLI no longer needs to expose the transitional `--profile-v2` spelling.
This switches the public args surface to `--profile` before the
remaining legacy profile plumbing is removed separately.

## What

- Rebind `--profile` and `-p` to the v2 profile name argument that
selects `$CODEX_HOME/<name>.config.toml`.
- Stop parsing the legacy shared CLI profile argument while keeping its
implementation path in place for follow-up cleanup.
- Update CLI validation, profile-name parse errors, and the
legacy-profile collision message/tests to refer to `--profile`.

## Testing

- `cargo test -p codex-cli -p codex-config -p codex-protocol -p
codex-utils-cli`
2026-05-21 16:45:27 +02:00
jif-oai
c1d7f4c8f8 chore: link doc in profile error messages (#23879)
Just updating the error message with a link to the doc
2026-05-21 16:32:12 +02:00
jif-oai
e6c8371e4e refactor: centralize tool exposure planning (#23876)
## Why

Tool exposure is a planning concern, but the deferred MCP path and
dispatch-only legacy shell path were carrying those decisions in handler
constructors and a shell-only tool-family builder. Keeping those
decisions in `spec_plan` makes the core tool plan easier to follow and
keeps handlers focused on runtime behavior.

## What changed

- add `PlannedTools` helpers for ordinary runtimes, exposure overrides,
dispatch-only runtimes, and hosted specs
- inline shell tool assembly into `core/src/tools/spec_plan.rs` and
remove the shell-only `tool_family` module
- remove exposure state and special exposure constructors from
`McpHandler` and `ShellCommandHandler`
- keep hidden runtime behavior centralized in `ExposureOverride`,
including disabling parallel tool calls for hidden handlers

## Testing

- Not run (refactor only)
2026-05-21 16:21:23 +02:00
jif-oai
2a25602783 [codex] Stabilize subagent start hook test (#23882)
## What

Remove the exact captured request-count assertion from the
`SubagentStart` hook integration test while still waiting for the child
request that matches the injected hook context.

## Why

The test owns the start-hook behavior and already verifies that the
child request reaches the context matcher plus that the start/session
hook logs have the expected invocations. Counting every request captured
by the response mock makes the test sensitive to lifecycle timing
outside that contract and has been flaky in CI.

## Testing

- `cargo test -p codex-core --test all
suite::subagent_notifications::subagent_start_replaces_session_start_and_injects_context
-- --exact`
2026-05-21 15:54:23 +02:00
jif-oai
516f134641 Make tool executor specs mandatory (#23870)
## Why

`ToolExecutor` is the runtime contract that keeps a callable tool and
its model-visible spec together. Leaving `spec()` optional lets a
registered runtime silently omit that half of the contract, and it also
overloads a missing spec as an exposure decision for tools that should
stay dispatchable without being shown to the model.

## What

- Make `ToolExecutor::spec()` required and update core, extension, and
test tool executors to return a concrete `ToolSpec`.
- Add `ToolExposure::Hidden` for dispatch-only tools. The legacy
`shell_command` runtime in unified-exec sessions now uses that explicit
exposure instead of hiding itself by omitting a spec.
- Build MCP tool specs when `McpHandler` is constructed so invalid MCP
specs are skipped before the handler is registered.
- Keep tool planning aligned with the new contract for direct, deferred,
hidden, code-mode, dynamic, and namespaced tool paths.

## Testing

- Added tool-plan coverage that invalid MCP tool specs are not
registered.
- Updated shell-family coverage for the hidden legacy `shell_command`
runtime and the affected tool executor test fixtures.
2026-05-21 15:25:56 +02:00
jif-oai
94442b7f95 feat: retain remote compaction truncation parity in v2 (#23728)
## Why

Remote compaction now has two implementations: the existing
server-rebuilt v1 path and the newer client-rebuilt v2 path behind
`remote_compaction_v2`. The v1 path bounds retained
user/developer/system history before installing the compaction item,
while v2 was previously carrying the full retained history forward. That
made the two paths diverge for large pre-compaction transcripts even
though they are meant to preserve the same compaction contract.

This aligns v2 with the retained-history budget expected from v1 so
switching the feature flag does not materially change which
pre-compaction messages survive into the rebuilt history.

## What changed

- Apply a retained-message character budget while rebuilding v2
compacted history in `core/src/compact_remote_v2.rs`.
- Keep newest retained messages first, truncate the boundary message
with the shared `truncate_text(...)` helper, and drop older retained
messages once the budget is exhausted.
- Preserve non-text retained message content such as images while
truncating text content.
- Use the current `64_000` token retained-message default translated to
the existing `4x` character budget.

## Testing

- `cargo test -p codex-core compact_remote_v2::tests::`
- Added focused coverage for newest-first retention and truncating
multipart retained messages without dropping images.
2026-05-21 15:07:03 +02:00
jif-oai
a6bedc8a7c fix: cargo lock (#23861) 2026-05-21 13:46:29 +02:00
jif-oai
791b69dd53 [codex] Steer budget-limited goal extension turns (#23718)
## What
- Add a small extension capability for injecting model-visible response
items into the active turn
- Have the goal extension inject hidden goal-context steering when
tool-finish accounting reaches `BudgetLimited`
- Cover the extension backend path with an assertion on the injected
steering item

## Why
PR #23696 persists and emits the budget-limited goal update from
tool-finish accounting, but it leaves the model unaware of that
transition. The existing core runtime steers the model to wrap up in
this case; the extension path should do the same through an explicit
host capability.

## Testing
- `just fmt`
- `cargo test -p codex-goal-extension`
- `cargo test -p codex-extension-api`
2026-05-21 12:54:00 +02:00
jif-oai
20fedafff8 Trace logical websocket request after untraced warmup (#23581)
## Why

`prewarm_websocket` intentionally stays out of rollout inference
tracing, but the next traced websocket request can still reuse the
warmup `response_id` and send an empty `input` delta. If tracing records
that wire payload verbatim, replay sees an incremental request whose
parent was never traced and cannot reconstruct the conversation.

This fixes that at the producer boundary instead of relaxing
`rollout-trace` replay semantics around unresolved
`previous_response_id` values.

## What

- track whether the last websocket response came from an untraced warmup
and clear that state when the websocket session is reset or reconnected
- when a traced websocket request reuses that warmup parent, keep
sending the compressed websocket request on the wire but record the
logical `ResponsesApiRequest` in the rollout trace
- add a regression test that proves replay reconstructs the logical user
message even though the websocket follow-up carries
`previous_response_id = warm-1` with empty `input`
- update `InferenceTraceAttempt::record_started` docs to reflect that
callers may record a logical request rather than the exact transport
payload

## Testing

- `cargo test -p codex-core --test all
responses_websocket_request_prewarm_traces_logical_request`
2026-05-21 11:13:23 +02:00
Michael Bolin
0b4f86095c sdk: launch packaged Codex runtimes (#23786)
## Why

The Python and TypeScript SDKs launch the native Codex runtime directly,
so they need to consume the same package artifact shape that release
jobs now produce. The runtime wheel should be built from the canonical
Codex package archive rather than reconstructing a parallel layout from
loose binaries.

## What Changed

- Stage `openai-codex-cli-bin` by extracting
`codex-package-<target>.tar.gz` into `src/codex_cli_bin` and validating
the expected package layout.
- Update release workflows to pass the generated package archive into
`stage-runtime` instead of the temporary package directory.
- Update Python runtime setup to download `codex-package-*.tar.gz`
release assets directly.
- Expose Python runtime helpers for the bundled package directory and
`codex-path`, and prepend that path when `openai_codex` launches the
installed runtime without duplicating Windows `Path`/`PATH` keys.
- Teach the TypeScript SDK to resolve package-layout optional
dependencies while keeping the existing npm fallback layout, and
preserve the existing Windows path variable casing when prepending
`codex-path`.

## Test Plan

- `python3 -m py_compile sdk/python/scripts/update_sdk_artifacts.py
sdk/python/_runtime_setup.py sdk/python/src/openai_codex/client.py
sdk/python-runtime/src/codex_cli_bin/__init__.py`
- `uv run --frozen --project sdk/python --extra dev ruff check
sdk/python/scripts/update_sdk_artifacts.py sdk/python/_runtime_setup.py
sdk/python/src/openai_codex/client.py
sdk/python/tests/test_artifact_workflow_and_binaries.py
sdk/python-runtime/src/codex_cli_bin/__init__.py`
- `uv run --frozen --project sdk/python --extra dev pytest
sdk/python/tests/test_artifact_workflow_and_binaries.py`
- `pnpm eslint src/exec.ts tests/exec.test.ts`
- `pnpm test --runInBand tests/exec.test.ts`
2026-05-20 18:01:22 -07:00
Michael Bolin
63a72e6b78 core: pass permission profiles to Windows runner (#23715)
## Why

This is the functional handoff PR for the Windows sandbox
`PermissionProfile` migration. After #23714, the Windows elevated
backend can accept a profile-native request, but core still sent a
compatibility `SandboxPolicy` into the elevated command-runner path.
That meant profile-only details such as deny globs had to be translated
through side channels instead of being preserved in the runner
`SpawnRequest`.

Passing the real `PermissionProfile` completes the command-runner
handoff while leaving the unelevated restricted-token fallback on the
legacy policy-string API.

## What

- Updates one-shot Windows elevated execution in `core/src/exec.rs` to
call `run_windows_sandbox_capture_for_permission_profile_elevated`.
- Updates unified exec in `core/src/unified_exec/process_manager.rs` to
call `spawn_windows_sandbox_session_elevated_for_permission_profile`.
- Passes `request.permission_profile` /
`exec_request.permission_profile` and the stored Windows sandbox policy
cwd to the elevated backend.
- Keeps compatibility `SandboxPolicy` serialization only for the
non-elevated restricted-token fallback.

## Verification

- `cargo test -p codex-core --test all --no-run`
2026-05-20 17:57:36 -07:00
viyatb-oai
713a5b1b00 feat: support managed permission profiles in requirements.toml (#23433)
## Why

Cloud-managed `requirements.toml` should be able to define the managed
permission profiles a client may select and constrain that selectable
set without requiring local user config to recreate the profile catalog.

This keeps requirements focused on restrictions. The selected default
remains a config or session choice, while requirements contribute the
managed profile bodies and `allowed_permissions` allowlist that the
config-loading boundary validates before a resolved runtime
`PermissionProfile` is installed.

## What changed

- Add `requirements.toml` support for a managed permission-profile
catalog plus its allowlist:

```toml
allowed_permissions = ["review", "build"]

[permissions.review]
extends = ":read-only"

[permissions.build]
extends = ":workspace"
```

- Merge requirements-defined profile bodies into the effective
permission catalog and reject profile ids that collide with
config-defined profiles.
- Validate that every `allowed_permissions` entry resolves to a built-in
or catalog profile before selection uses it.
- Preserve allowed configured named-profile selections. When a
configured named profile is disallowed, fall back to the first allowed
requirements profile with a startup warning.
- Keep built-in selections and the stock trust-based `:read-only` /
`:workspace` fallback path intact when no permission profile is
explicitly selected.
- Centralize the managed catalog and allowlist selection path in
`EffectivePermissionSelection` so the requirements boundary is visible
in config loading.
- Surface `allowedPermissions` through `configRequirements/read`, and
update the generated app-server schema fixtures plus the app-server
README.

## Validation

- `cargo test -p codex-config`
- `cargo test -p codex-core system_requirements_`
- `cargo test -p codex-core system_allowed_permissions_`
- `cargo test -p codex-app-server-protocol`
- `just write-app-server-schema`

## Related work

- Uses merged permission-profile inheritance support from #22270 and
#23705.
- Kept separate from the in-flight permission profile listing API in
#23412.
2026-05-20 17:33:01 -07:00
Michael Bolin
c9ff067e31 windows-sandbox: add profile-native elevated APIs (#23714)
## Why

This is the next step after #23167 in the Windows sandbox
`PermissionProfile` migration. The elevated Windows backend still
exposed policy-string entry points, which forced callers to pass a
compatibility `SandboxPolicy` before the command-runner IPC could
receive a profile.

Adding profile-native APIs first keeps the core switch in the next PR
small: reviewers can see that the Windows crate can prepare elevated
setup, capability SIDs, and runner IPC from a resolved
`PermissionProfile` without changing core behavior yet.

## What

- Adds `ElevatedSandboxProfileCaptureRequest` and
`run_windows_sandbox_capture_for_permission_profile_elevated` for
one-shot elevated capture.
- Adds `spawn_windows_sandbox_session_elevated_for_permission_profile`
for unified exec sessions.
- Factors elevated spawn prep through
`prepare_elevated_spawn_context_for_permissions`, so both new APIs
operate from `ResolvedWindowsSandboxPermissions` directly.
- Keeps the existing legacy policy-string APIs as adapters for callers
that have not moved yet.

## Verification

- `cargo test -p codex-windows-sandbox`












---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23714).
* #23715
* __->__ #23714
2026-05-21 00:25:31 +00:00
viyatb-oai
a27d3847b5 [codex] Reject read-only fallback with approvals disabled (#23774)
## Why

If a user configures `approval_policy = "never"` with `sandbox_mode =
"danger-full-access"`, managed requirements can reject full access and
force the existing permission fallback to read-only. That leaves Codex
in a dead-end session: writes are blocked by the sandbox, while
approvals are disabled so the session cannot ask to proceed.

This PR rejects that constrained configuration during startup instead of
letting the TUI enter a read-only session that cannot make progress. The
rejection is attached to the requirement-constrained permission path in
[`Config`](39f0abc0a7/codex-rs/core/src/config/mod.rs (L3301-L3318)).

## What changed

- Reject the `danger-full-access` to read-only managed-requirements
fallback when the effective approval policy is `never`.
- Explain in the startup config error why the fallback is invalid and
how to fix it.
- Add a regression test for the managed requirements path.
2026-05-20 17:17:59 -07:00
evawong-oai
3cae84009a Use named MITM permissions config (#18240)
## Stack
1. Parent PR: #18868 adds MITM hook config and model only.
2. Parent PR: #20659 wires hook enforcement into the proxy request path.
3. This PR changes the user facing PermissionProfile TOML shape.

## Why
1. The broader goal is to make MITM clamping usable from the same
permission profile that already controls network behavior.
2. This PR is the config UX layer for the stack. It moves MITM policy
into `[permissions.<profile>.network.mitm]` instead of exposing the flat
runtime shape to users.
3. The named hook and action tables belong here because users need
reusable policy blocks that are easy to review, while the proxy runtime
only needs a flat hook list.
4. This PR validates action refs during config parsing so mistakes in
the user facing policy fail before a proxy session starts.
5. Keeping the lowering here lets the proxy keep its simpler runtime
model and lets PermissionProfile remain the single source of network
permission policy.

## Summary
1. Keep MITM policy inside `[permissions.<profile>.network.mitm]` so the
selected PermissionProfile owns network proxy policy.
2. Use named MITM hooks under
`[permissions.<profile>.network.mitm.hooks.<name>]`.
3. Put host, methods, path prefixes, query, headers, body, and action
refs on the hook table.
4. Define reusable action blocks under
`[permissions.<profile>.network.mitm.actions.<name>]`.
5. Represent action blocks with `NetworkMitmActionToml`, then lower them
into the proxy runtime action config.
6. Reject unknown refs, empty refs, and empty action blocks during
config parsing.
7. Keep the runtime hook model unchanged by lowering config into the
existing proxy hook list.
8. Preserve the #20659 activation fix for nested MITM policy.

## Example
```toml
[permissions.workspace.network.mitm]
enabled = true

[permissions.workspace.network.mitm.hooks.github_write]
host = "api.github.com"
methods = ["POST", "PUT"]
path_prefixes = ["/repos/openai/"]
action = ["strip_auth"]

[permissions.workspace.network.mitm.actions.strip_auth]
strip_request_headers = ["authorization"]
```

## Validation
1. Regenerated the config schema.
2. Ran the core MITM config parsing and validation tests.
3. Ran the core PermissionProfile MITM proxy activation tests.
4. Ran the core config schema fixture test.
5. Ran the network proxy MITM policy tests.
6. Ran the scoped Clippy fixer for the network proxy crate.
7. Ran the scoped Clippy fixer for the core crate.

---------

Co-authored-by: Winston Howes <winston@openai.com>
2026-05-20 17:10:37 -07:00
Matthew Zeng
0a4179bb19 [codex] Add plugin id to MCP tool call items (#23737)
Add owning plugin id to MCP tool call items so we can better filter them
at plugin level.

## Summary
- add optional `plugin_id` to MCP tool-call items and legacy begin/end
events
- propagate plugin metadata into emitted core items and app-server v2
`ThreadItem::McpToolCall`
- preserve plugin ids through app-server replay/redaction paths and
regenerate v2 schema fixtures

## Testing
- `just write-app-server-schema`
- `just fmt`
- `just fix -p codex-core`
- `cargo test -p codex-protocol -p codex-app-server-protocol`
- `cargo test -p codex-app-server-protocol`
- `cargo test -p codex-core mcp_tool_call_item_includes_plugin_id --lib`
- `cargo check -p codex-tui --tests`
- `cargo check -p codex-app-server --tests`
- `git diff --check`

## Notes
- `just fix -p codex-core` completed with two non-fatal
`too_many_arguments` warnings on the touched MCP notification helpers.
- A broader `cargo test -p codex-core` run passed core unit tests, then
hit shell/sandbox/snapshot failures in the integration target.
- A broader app-server downstream run hit the existing
`in_process::tests::in_process_start_clamps_zero_channel_capacity` stack
overflow; `cargo test -p codex-exec` also hit the existing sandbox
expectation mismatch in
`thread_lifecycle_params_include_legacy_sandbox_when_no_active_profile`.
2026-05-20 17:02:10 -07:00
Michael Bolin
0b5cf85b64 ci: run Codex package builder tests (#23760)
## Why

#23752 and #23759 add Python unit tests for the Codex package builder,
but the root CI workflow did not run tests under
`scripts/codex_package`. That left the `zstd` resolution and
prebuilt-resource packaging behavior covered locally without a CI check.

## What changed

- Add a root CI step in `.github/workflows/ci.yml` that runs `python3 -m
unittest discover -s scripts/codex_package -p "test_*.py"`.
- Keep the step with the existing Python verification checks before
Node/pnpm setup.

## Verification

- `python3 -m unittest discover -s scripts/codex_package -p "test_*.py"`
- `python3 -m py_compile scripts/codex_package/*.py`
2026-05-20 17:00:55 -07:00
Casey Chow
60b45d92d9 [codex] List marketplaces considered by plugin discovery
Co-authored-by: Codex <noreply@openai.com>
2026-05-20 19:17:46 -04:00
iceweasel-oai
8253ae4e5c Remove Windows sandbox resource stamping (#23764)
## Why

The `codex-windows-sandbox` crate was embedding Windows resource
metadata through a package-level `build.rs`. Because that package also
exposes the `codex_windows_sandbox` library, downstream binaries that
link the library could inherit `FileDescription` / `ProductName` values
of `codex-windows-sandbox`.

That made ordinary Codex binaries, including the long-lived `codex.exe`
app-server sidecar, appear as `codex-windows-sandbox` in Windows UI
surfaces such as Task Manager / file properties.

We do not rely on this metadata enough to justify a larger bin-only
resource split, so this removes the resource stamping entirely.

## What changed

- Removed the `windows-sandbox-rs` build script that invoked `winres`.
- Removed the setup manifest that was only consumed by that build
script.
- Removed the `winres` build dependency and corresponding `Cargo.lock` /
`MODULE.bazel.lock` entries.
- Removed the now-unused Bazel build-script data.

## Verification

- `cargo build -p codex-windows-sandbox --bins`
- `cargo build -p codex-cli --bin codex`
- `bazel mod deps --lockfile_mode=update` via Bazelisk, with local
remote-cache-disabling flags because `bazel` is not installed on PATH
here
- `bazel mod deps --lockfile_mode=error` via Bazelisk, with the same
local flags
- Verified rebuilt `codex.exe`, `codex-command-runner.exe`, and
`codex-windows-sandbox-setup.exe` now have blank `FileDescription` /
`ProductName` fields.
- `cargo test -p codex-windows-sandbox` still fails on two legacy
Windows sandbox tests with `CreateRestrictedToken failed: 87` and the
follow-on poisoned test lock; 85 passed, 2 ignored.
2026-05-20 16:15:21 -07:00
guinness-oai
d6d03d42ea [codex] Fix realtime v1 websocket compatibility (#23771)
## Why

Realtime v1 websocket sessions now expect a slightly different boundary
shape for text input, completed input transcripts, and connection
headers. Codex was still using the older shape, so some v1 text appends
could be rejected before the existing conversation flow could handle
them.

## What changed

- Send v1 user text items with `input_text` content
- Accept v1 turn-marked input transcript events as completed transcripts
- Add the v1 alpha header only for v1 realtime sessions
- Cover the outbound text shape, transcript parsing, and versioned
headers

## Test plan

- `cargo test -p codex-api endpoint::realtime_websocket::methods::tests`
- `cargo test -p codex-core quicksilver_alpha_header`
2026-05-20 16:03:51 -07:00
Shijie Rao
370b13afc9 Honor client-resolved service tier defaults (#23537)
## Why

Model catalog responses can now advertise a nullable
`default_service_tier` for each model. Codex needs to preserve three
distinct states all the way from config/app-server inputs to inference:

- no explicit service tier, so the client may apply the current model
catalog default when FastMode is enabled
- explicit `default`, meaning the user intentionally wants standard
routing
- explicit catalog tier ids such as `priority`, `flex`, or future tiers

Keeping those states distinct prevents the UI from showing one tier
while core sends another, especially after model switches or app-server
`thread/start` / `turn/start` updates.

## What Changed

- Plumbed `default_service_tier` through model catalog protocol types,
app-server model responses, generated schemas, model cache fixtures, and
provider/model-manager conversions.
- Added the request-only `default` service tier sentinel and normalized
legacy config spelling so `fast` in `config.toml` still materializes as
the runtime/request id `priority`.
- Moved catalog default resolution to the TUI/client side, including
recomputing the effective service tier when model/FastMode-dependent
surfaces change.
- Updated app-server thread lifecycle config construction so
`serviceTier: null` preserves explicit standard-routing intent by
mapping to `default` instead of internal `None`.
- Kept core responsible for validating explicit tiers against the
current model and stripping `default` before `/v1/responses`, without
applying catalog defaults itself.

## Validation

- `CARGO_INCREMENTAL=0 cargo build -p codex-cli`
- `CARGO_INCREMENTAL=0 cargo test -p codex-app-server model_list`
- `cargo test -p codex-tui service_tier`
- `cargo test -p codex-protocol service_tier_for_request`
- `cargo test -p codex-core get_service_tier`
- `RUST_MIN_STACK=8388608 CARGO_INCREMENTAL=0 cargo test -p codex-core
service_tier`
2026-05-20 15:57:50 -07:00
Eric Traut
0e9d222178 Make goals feature on by default and no longer experimental (#23732)
## Why

The `goals` feature is ready to be available without requiring users to
opt into experimental features. Keeping it behind the beta flag leaves
persisted thread goals and automatic goal continuation disabled by
default.

This PR also marks the goal-related app server APIs and events as no
longer experimental.

## What changed

- Mark `goals` as `Stage::Stable`.
- Enable `goals` by default in `codex-rs/features/src/lib.rs`.
2026-05-20 15:07:35 -07:00
Casey Chow
3075061bdd feat(plugins): tabulate plugin list output (#23727)
## Summary
- render `codex plugin list` as one table per marketplace with the
marketplace manifest path shown above each table
- surface the installed plugin version in the CLI output by threading
`installed_version` through marketplace listing state
- narrow the system-root exemption so only known bundled/runtime
marketplaces skip missing-manifest failures, and keep `VERSION` empty
for cached-but-unconfigured plugins

## Rationale
The plugin list UX was hard to scan as a flat list and did not show
which installed version was active. This change makes the CLI output
easier to read in the real multi-marketplace case, keeps the plugin path
visible, fixes the Sapphire regression where bundled/runtime marketplace
roots were blocking `plugin list`, and addresses the two review findings
that came out of the follow-up deep review.

## Key Decisions
- kept the CLI output grouped per marketplace instead of one global
table so the marketplace path can live with the rows it owns
- kept `VERSION` as the installed version, which means it is empty until
a plugin is actually installed
- handled the bundled/runtime regression in the CLI snapshot validation
path rather than widening app-server protocol or changing marketplace
loading behavior
- narrowed the exemption to known system marketplace names plus expected
system paths, so user-configured marketplaces under those directories
still fail loudly
- gated `installed_version` on actual installed state so `VERSION`
cannot show stale cache state for `not installed` rows

## Validation
- `just fmt`
- Sapphire: `cargo test -p codex-cli --test plugin_cli` (`14 passed; 0
failed`)
- Sapphire smoke test: bundled/runtime roots still work
  - `cargo run -q -p codex-cli -- plugin add sample@debug`
  - `cargo run -q -p codex-cli -- plugin list`
- verified the bundled/runtime-root scenario no longer errors and shows
the expected marketplace table output
- Sapphire smoke test: custom marketplace under bundled path still
errors
- verified `failed to load configured marketplace snapshot(s)` for
`custom-marketplace`
- Sapphire smoke test: cached-but-unconfigured plugin hides version
- verified `sample@debug not installed` renders with an empty `VERSION`
column

## Sample Output
```text
/tmp/custom-marketplace/plugin.json
NAME          VERSION  STATUS         DESCRIPTION
sample@debug  1.0.0    enabled        Debug sample plugin
other@local            not installed  Local development plugin
```
2026-05-20 18:04:49 -04:00
Abhinav
eee3e60db3 Add SubagentStop hook (#22873)
# What

<img width="1792" height="1024" alt="image"
src="https://github.com/user-attachments/assets/8f81d232-5813-4994-a61d-e42a05a93a3e"
/>

`SubagentStop` runs when a thread-spawned subagent turn is about to
finish. Thread-spawned subagents use `SubagentStop` instead of the
normal root-agent `Stop` hook.

Configured handlers match on `agent_type`. Hook input includes the
normal stop fields plus:

- `agent_id`: the child thread id.
- `agent_type`: the resolved subagent type.
- `agent_transcript_path`: the child subagent transcript path.
- `transcript_path`: the parent thread transcript path.
- `last_assistant_message`: the final assistant message from the child
turn, when available.
- `stop_hook_active`: `true` when the child is already continuing
because an earlier stop-like hook blocked completion.

`SubagentStop` shares the same completion-control semantics as `Stop`,
scoped to the child turn:

- No decision allows the child turn to finish.
- `decision: "block"` with a non-empty `reason` records that reason as
hook feedback and continues the child with that prompt.
- `continue: false` stops the child turn. If `stopReason` is present,
Codex surfaces it as the stop reason.

# Lifecycle Scope

Only thread-spawned subagents run `SubagentStop`.

Internal/system subagents such as Review, Compact, MemoryConsolidation,
and Other do not run normal `Stop` hooks and do not run `SubagentStop`.
This avoids exposing synthetic matcher labels for internal
implementation paths.

# Stack

1. #22782: add `SubagentStart`.
2. This PR: add `SubagentStop`.
3. #22882: add subagent identity to normal hook inputs.
2026-05-20 14:59:41 -07:00
viyatb-oai
40ad7be2b5 core: refresh active permission profiles at runtime (#22931)
## Why

Once a named permission profile is selected, runtime state has to keep
that profile identity intact instead of collapsing back to anonymous
effective permissions. The session refresh path also needs to rebuild
profile-derived network proxy state so active profile switches take
effect consistently.

## What changed

- Preserve the active permission profile through session updates.
- Rebuild profile-derived runtime/network configuration when the active
profile changes.
- Keep the runtime path aligned with the current session configuration
APIs.
- Tighten the affected tests, including the Windows delete-pending
memory-file case that was intermittently tripping CI.

## Stack

1. **This PR**: runtime/session/network propagation for active
permission profiles.
2. [#23708](https://github.com/openai/codex/pull/23708): TUI selection
plumbing and guardrail flow.
3. [#21559](https://github.com/openai/codex/pull/21559): profile-aware
`/permissions` menu and custom profile display.

<img width="1296" height="906" alt="image"
src="https://github.com/user-attachments/assets/077fa3a7-80cb-4925-80b1-d2395018d90a"
/>
2026-05-20 21:55:21 +00:00
Michael Bolin
896ee672cc windows-sandbox: feed setup from resolved permissions (#23167)
## Why

This is the next step in the Windows sandbox migration away from the
legacy `SandboxPolicy` abstraction. #22923 moved write-root and token
decisions onto `ResolvedWindowsSandboxPermissions`, but setup and
identity still accepted `SandboxPolicy` and converted internally. This
PR pushes that conversion outward so the setup path consumes the
resolved Windows permission view directly.

## What Changed

- Changed `SandboxSetupRequest` to carry
`ResolvedWindowsSandboxPermissions` instead of `SandboxPolicy` plus
policy cwd.
- Updated setup refresh/elevation and identity credential preparation to
use resolved permissions for read roots, write roots, network identity,
and deny-write payload planning.
- Removed the production `allow.rs` legacy wrapper; allow-path
computation now takes resolved permissions directly.
- Added a permissions-based world-writable audit entry point while
keeping the existing legacy wrapper for compatibility.
- Updated legacy ACL setup and the core Windows setup bridge to
construct resolved permissions at the boundary.
- Hardened the Windows sandbox integration test helper staging so Bazel
retries can reuse an already-staged helper if a prior sandbox helper
process still has the executable open.

## Verification

- `cargo test -p codex-windows-sandbox`
- `cargo test -p codex-core --test all --no-run`
- `just fix -p codex-windows-sandbox`
- `just fix -p codex-core`
- Attempted `cargo check -p codex-windows-sandbox --target
x86_64-pc-windows-gnullvm`, but the local machine is missing
`x86_64-w64-mingw32-clang`; Windows CI should cover that target.











---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23167).
* #23715
* #23714
* __->__ #23167
2026-05-20 14:52:38 -07:00
Michael Bolin
80c4a978f8 release: package prebuilt resource binaries (#23759)
## Why

Release packaging should be a staging step once release binaries have
already been built and signed. The Windows release job was downloading
and signing `codex-command-runner.exe` and
`codex-windows-sandbox-setup.exe`, but `scripts/build_codex_package.py`
still rebuilt those helpers while creating the package archives.

That makes the package step slower and, more importantly, risks putting
helper binaries in the archive that were produced after the signing
step. Linux had the same shape for package resources: `bwrap` could be
rebuilt by the package builder instead of being passed in as a prebuilt
release artifact.

This builds on #23752, which fixes `.tar.zst` creation when Windows
runners rely on the repository DotSlash `zstd` wrapper.

## What changed

- Add explicit prebuilt resource inputs to the Codex package builder:
  - `--bwrap-bin`
  - `--codex-command-runner-bin`
  - `--codex-windows-sandbox-setup-bin`
- Make `.github/scripts/build-codex-package-archive.sh` pass resource
binaries from the release output directory when they are already
present.
- Build Linux `bwrap` for app-server release jobs too, so app-server
package creation does not invoke Cargo just to supply the package
resource.
- Keep macOS package creation as a no-Cargo path when `--entrypoint-bin`
is provided, since macOS packages have no resource binaries.
- Add unit coverage showing prebuilt macOS, Linux, and Windows package
inputs result in no source-built binaries.

## Verification

- `python3 -m unittest discover -s scripts/codex_package -p 'test_*.py'`
- `python3 -m py_compile scripts/codex_package/*.py`
- `bash -n .github/scripts/build-codex-package-archive.sh`
- Dry-ran Linux and Windows package builds with fake prebuilt resources
and a nonexistent Cargo path to verify the package builder did not
invoke Cargo.


---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23759).
* #23760
* __->__ #23759
2026-05-20 14:51:46 -07:00
Michael Bolin
96aa389c79 chore: use Codex Linux runners for Rust releases (#23761)
## Why

Linux release jobs build the MUSL artifacts that ship in Codex releases,
including both the primary CLI bundle and the app-server bundle. Those
builds should run on the Codex Linux runner pools instead of generic
Ubuntu-hosted runners so release builds use the x64 and arm64 capacity
intended for Codex artifacts.

## What Changed

- Moves the `x86_64-unknown-linux-musl` release matrix entries in
`.github/workflows/rust-release.yml` from `ubuntu-24.04` to
`codex-linux-x64-xl`.
- Moves the `aarch64-unknown-linux-musl` release matrix entries from
`ubuntu-24.04-arm` to `codex-linux-arm64`.
- Leaves macOS release jobs, target triples, bundle names, and artifact
names unchanged.

## Verification

- Reviewed the workflow matrix diff for
`.github/workflows/rust-release.yml`.
- Not run locally; this is a GitHub Actions runner configuration change.
2026-05-20 14:45:19 -07:00
Michael Bolin
e1ec0eee5f windows-sandbox: drive write roots from resolved permissions (#22923)
## Why

This is the third PR in the Windows sandbox `SandboxPolicy` ->
`PermissionProfile` migration stack.

#22896 introduced `ResolvedWindowsSandboxPermissions`, and #22918 moved
elevated runner IPC to carry `PermissionProfile`. This PR starts moving
the remaining setup/spawn helpers away from asking legacy enum questions
like “is this `WorkspaceWrite`?” and toward resolved runtime permission
questions like “does this profile require write capability roots?”

## What changed

- Added resolved-permissions helpers for network identity and
write-capability detection.
- Moved setup write-root gathering to operate on
`ResolvedWindowsSandboxPermissions`, with the legacy `SandboxPolicy`
wrapper left in place for existing call sites.
- Updated identity setup, elevated capture setup, and world-writable
audit denies to use resolved write roots.
- Updated spawn preparation to carry resolved permissions in
`SpawnContext` and use them for network blocking, setup write roots,
elevated capability SID selection, and legacy capability roots.
- Removed a now-unused legacy write-root helper.

## Verification

- `cargo test -p codex-windows-sandbox`
- `just fix -p codex-windows-sandbox`
- Existing stack checks are green on #22896 and #22918; CI has started
for this PR.
















---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22923).
* #23715
* #23714
* #23167
* __->__ #22923
2026-05-20 14:30:42 -07:00
Michael Bolin
f48be015d6 release: use DotSlash zstd for package archives (#23752)
## Why

The Windows release job installed DotSlash successfully, but package
archive creation still failed while writing `codex-package-*.tar.zst`.
The Python archiver used `shutil.which("zstd")`, which does not reliably
find the extensionless DotSlash manifest at `.github/workflows/zstd`
from native Windows Python.

That left release packaging dependent on a command named exactly `zstd`
being discoverable on `PATH`, even though the repository already carries
a DotSlash wrapper for Windows runners.

## What changed

- Add `resolve_zstd_command()` to prefer a real `zstd` binary when
present.
- Fall back to invoking `dotslash .github/workflows/zstd` when `zstd` is
not on `PATH`.
- Keep the error explicit when neither `zstd` nor the DotSlash fallback
is available.
- Add unit coverage for direct `zstd`, DotSlash fallback, and
missing-tool error paths.

## Verification

- `python3 -m unittest discover -s scripts/codex_package -p 'test_*.py'`
- `python3 -m py_compile scripts/codex_package/*.py`
2026-05-20 14:28:11 -07:00
evawong-oai
f6970214d2 Wire MITM hooks into runtime enforcement (#20659)
## Stack
1. Parent PR: #18868 adds MITM hook config and model only.
2. This PR wires runtime enforcement.
3. User facing config follow up: #18240 moves MITM policy into the
PermissionProfile network tree.

## Why
1. After the hook model exists, the proxy needs a separate behavior
change that can be tested at the request path.
2. This PR makes hooked HTTPS hosts require MITM, evaluates inner
requests after CONNECT, mutates headers for matching hooks, and blocks
hooked hosts when no hook matches.
3. It also fixes the activation path so a permission profile with MITM
hook policy starts the managed proxy.
4. Keeping this separate from #18868 lets reviewers focus on runtime
effects, telemetry, and request mutation.

## Summary
1. Store compiled MITM hooks in network proxy state.
2. Require MITM for hooked hosts even when network mode is full.
3. Evaluate inner HTTPS requests against host specific hooks.
4. Apply hook actions by replacing request headers before forwarding.
5. Block hooked hosts when no hook matches and record block telemetry.
6. Treat profile MITM hook policy as managed proxy policy so the proxy
starts when needed.
7. Keep the duplicate authorization header replacement and query
preserving request rebuild in this runtime PR.
8. Add runtime tests and README guidance for hook enforcement.

## Validation
1. Ran the network proxy MITM policy tests.
2. Ran the hooked host CONNECT test.
3. Ran the authorization header replacement test.
4. Ran the core permission profile proxy activation test for MITM hooks.
5. Ran the scoped Clippy fixer for the network proxy crate.
6. Ran the scoped Clippy fixer for the core crate.
2026-05-20 14:08:14 -07:00
Abhinav
af49d38373 Support compact SessionStart hooks (#21272)
# Why

Compaction replaces the live conversation history, so hooks that use
`SessionStart` to re-inject durable model context need a way to run
again after that rewrite.

Related - #19905 adds dedicated compact lifecycle hooks

# What

- add `compact` as a supported `SessionStart` source and matcher value
- change pending `SessionStart` state from a single slot to a small FIFO
queue so `resume` / `startup` / `clear` can be preserved alongside a
later `compact`
- drain all queued `SessionStart` sources before the next model request,
preserving their original order

# Testing

The new integration coverage verifies both the basic `compact` matcher
path and the stacked `resume` -> `compact` case where both hooks
contribute `additionalContext` to the next model turn.
2026-05-20 20:46:19 +00:00
Casey Chow
9265701b7f [skills] Create a personal update flow for plugin creator (#23542)
## Summary
Creates a personal-marketplace update flow for the plugin-creator skill
when iterating on an existing local plugin.

## Context
Plugin creation already had a scaffold path, but the follow-up story for
updating an existing local plugin during development was not explicit.
The goal of this change is to make that default personal-marketplace
update loop legible at the point of use instead of leaving it implied or
hidden behind a larger helper.

## Decision
Keep the scaffold flow intact, add a dedicated update/reinstall
reference centered on the personal marketplace, document the actual
`codex plugin add` and marketplace-check commands directly, and keep
helper automation narrowly scoped to the repetitive local-update steps.

## Changes
- update plugin-creator to point existing-plugin iteration at a
personal-marketplace update flow
- add `references/installing-and-updating.md` with the explicit
marketplace check and reinstall sequence
- add small helper scripts for reading marketplace names and updating
plugin versions during local iteration

## Tests
- `python3
codex-rs/skills/src/assets/samples/skill-creator/scripts/quick_validate.py
codex-rs/skills/src/assets/samples/plugin-creator`
- `python3 -m py_compile
codex-rs/skills/src/assets/samples/plugin-creator/scripts/create_basic_plugin.py
codex-rs/skills/src/assets/samples/plugin-creator/scripts/read_marketplace_name.py
codex-rs/skills/src/assets/samples/plugin-creator/scripts/update_plugin_cachebuster.py`
2026-05-20 16:44:41 -04:00
Michael Bolin
d1e3d54192 cli: add strict config to exec-server (#23719)
## Why

PR #20559 added opt-in strict config parsing to the config-loading
command surfaces, but `codex exec-server` was left out. That meant
`codex exec-server --strict-config` was rejected even though the command
can load config for remote registration, and local server startup had no
way to fail fast on misspelled config keys.

## What Changed

- Added `--strict-config` to `codex exec-server`.
- Allowed root-level inheritance from `codex --strict-config
exec-server`.
- Validated config before local exec-server startup when strict mode is
requested.
- Reused the loaded strict-config-aware config for remote exec-server
registration auth.
- Added CLI coverage showing `codex exec-server --strict-config` rejects
unknown config fields.

## Verification

- `cargo test -p codex-cli`
- New integration test:
`strict_config_rejects_unknown_config_fields_for_exec_server`

## Documentation

Any strict-config command list on developers.openai.com/codex should
include `codex exec-server` with the other supported config-loading
entry points.
2026-05-20 13:12:31 -07:00
viyatb-oai
fe7c069fe6 feat(permissions): resolve permission profile inheritance (#22270)
## Stack

This is the foundation PR for the permission-profile inheritance stack.

- This PR adds config-level `extends` resolution and merge semantics.
- Follow-up: #23705 applies resolved profiles at runtime and updates the
active-profile protocol surfaces.

## Why

Permission profiles are starting to carry enough policy that
copy-pasting near-identical definitions becomes hard to review and easy
to drift. Before the runtime can consume inherited profiles, the config
layer needs one explicit resolver that can merge parent chains and
reject unsafe or invalid inheritance shapes.

## What changed

- Add `extends` to permission-profile TOML and resolve parent chains in
inheritance order.
- Merge inherited profile TOML with the existing config merge behavior
while preserving the permission-specific normalization needed for
network domain keys.
- Keep parent descriptions out of resolved child profiles and record
inherited profile names separately for downstream consumers.
- Reject undefined parents, unsupported built-in parents, and
inheritance cycles with targeted errors.
- Cover resolver behavior with TOML fixture tests and refresh the
generated config schema.

## Validation

- `cargo test -p codex-config`
- `cargo test -p codex-core permissions_profiles_`
2026-05-20 20:12:07 +00:00
evawong-oai
3d94e24a3d Add MITM hook config model (#18868)
## Stack
1. This PR adds MITM hook config and model only.
2. Runtime follow up: #20659 wires hook enforcement into the proxy
request path.
3. User facing config follow up: #18240 moves MITM policy into the
PermissionProfile network tree.

## Why
1. Viyat asked for the original parent PR to be split so reviewers can
inspect the policy model before request behavior changes.
2. This PR gives the proxy a typed MITM hook model, validation, matcher
compilation, permissions TOML plumbing, schema support, and config
tests.
3. This PR deliberately does not change CONNECT or MITM request
handling.
4. Keeping runtime behavior out of this PR makes the review boundary
simple: does the policy model parse, validate, compile, and lower
correctly.

## Summary
1. Add the MITM hook config model and matcher compilation.
2. Validate hosts, methods, paths, query matchers, header matchers,
secret sources, and reserved body matching.
3. Add wildcard matcher support for path, query value, and header value
matching.
4. Add permissions TOML and schema support for flat runtime hook config.
5. Add config loader tests for MITM hook overlay behavior.

## Validation
1. Regenerated the config schema.
2. Ran the network proxy MITM hook unit tests.
3. Ran the core permission profile MITM hook parsing tests.
4. Ran the core config schema fixture test.
5. Ran the scoped Clippy fixer for the network proxy crate.
6. Ran the scoped Clippy fixer for the core crate.

## Notes
1. Runtime enforcement moved to #20659.
2. User facing PermissionProfile TOML shape remains in #18240.
2026-05-20 12:51:12 -07:00
Michael Bolin
61aae56571 windows-sandbox: share bundled helper lookup (#23735)
## Summary

Follow-up to #23636 review feedback: the Windows sandbox had two copies
of the same bundled-helper lookup order, one for
`codex-command-runner.exe` in `helper_materialization.rs` and one for
`codex-windows-sandbox-setup.exe` in `setup.rs`.

This PR centralizes that lookup in
`helper_materialization::bundled_executable_path_for_exe()` and has
setup reuse it for `codex-windows-sandbox-setup.exe`. The lookup
behavior is unchanged: direct sibling first, package-root
`codex-resources/` when running from `bin/`, then legacy sibling
`codex-resources/`.

## Test plan

- `cargo test -p codex-windows-sandbox`

## Notes

I also attempted `cargo check -p codex-windows-sandbox --target
x86_64-pc-windows-gnullvm`, but this local host is missing
`x86_64-w64-mingw32-clang`.
2026-05-20 19:50:38 +00:00
Michael Bolin
729bdf3c8d windows-sandbox: send permission profiles to elevated runner (#22918)
## Why

This is the next PR in the Windows sandbox migration stack after #22896.
The bottom PR introduces a Windows-local resolved permissions helper
while existing callers still start from legacy `SandboxPolicy`. This PR
moves the elevated runner IPC boundary to `PermissionProfile`, which
makes the direction of the stack visible without changing the public
core call sites yet.

Because that changes the CLI-to-command-runner message shape, the framed
IPC protocol version is bumped in the same PR so the boundary change is
explicit.

## What changed

- Replaced elevated IPC `policy_json_or_preset`/`sandbox_policy_cwd`
fields with `permission_profile`/`permission_profile_cwd`.
- Bumped the elevated command-runner IPC protocol to
`IPC_PROTOCOL_VERSION = 2` and switched parent/runner frames to use the
shared constant.
- Converted the parent elevated paths from the parsed legacy policy into
a materialized `PermissionProfile` before sending the runner request.
- Added `WindowsSandboxTokenMode` resolution for managed
`PermissionProfile` values and made the runner choose read-only vs
writable-root capability tokens from that resolved profile.
- Rejected disabled, external, unrestricted, and full-disk-write
profiles before token selection.
- Added IPC JSON coverage for tagged `PermissionProfile` payloads and
token-mode unit coverage for the resolved permission helper.

## Verification

- `cargo test -p codex-windows-sandbox`
- `just fix -p codex-windows-sandbox`
- `cargo check -p codex-windows-sandbox --target x86_64-pc-windows-msvc
--tests` was attempted locally but blocked before crate type-checking
because the macOS compiler environment lacks Windows C headers such as
`windows.h` and `assert.h`; GitHub Windows CI is the required
verification for the runner path.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22918).
* #23715
* #23714
* #23167
* #22923
* __->__ #22918
2026-05-20 12:41:06 -07:00
Michael Bolin
cb05de6724 dotslash: publish Codex entrypoints from package archives (#23638)
## Summary

DotSlash should resolve the same canonical package archives used by
standalone installers and npm platform packages, rather than continuing
to point at single-binary zstd artifacts or the older Linux bundle
archive.

This updates the Codex CLI and `codex-app-server` DotSlash release
config entries to match `codex-package-<target>.tar.gz` and
`codex-app-server-package-<target>.tar.gz`, with paths that select
`bin/codex` or `bin/codex-app-server` inside the extracted package. The
other helper outputs stay on their existing per-binary artifacts for
now.

## Test plan

- `python3 -m json.tool .github/dotslash-config.json > /dev/null`
- Ran a Python regex smoke test that checked every updated `codex` and
`codex-app-server` platform entry against the archive names emitted by
`.github/scripts/build-codex-package-archive.sh`.
2026-05-20 12:18:10 -07:00
viyatb-oai
0edcc4b94e fix(config): resolve cloud requirements deny-read globs (#23729)
## Why

Cloud-managed `requirements.toml` contents were deserialized without an
`AbsolutePathBuf` base directory. Relative managed
`permissions.filesystem.deny_read` glob entries therefore failed while
the equivalent local system requirements path succeeded under its
`AbsolutePathBufGuard`. This follows the `codex_home` base path
convention clarified in https://github.com/openai/codex/pull/15707.

## What changed

- Resolve cloud requirements TOML under an `AbsolutePathBufGuard` rooted
at `codex_home`.
- Reuse the same base for cloud requirements loaded from the signed
cache.
- Add a regression test for a relative cloud-managed `deny_read` glob.

## Validation

- `just fmt`
- `cargo test -p codex-cloud-requirements`
- `cargo clippy -p codex-cloud-requirements --all-targets --no-deps`
- `just bazel-lock-update`
- `just bazel-lock-check`
- `git diff --check`
2026-05-20 12:15:44 -07:00
Michael Bolin
e389e01f83 npm: ship platform packages in Codex package layout (#23637)
## Summary

The npm platform packages should stop carrying a bespoke native layout
now that the release workflow builds canonical Codex package archives.
Keeping npm on the same `bin/`, `codex-resources/`, and `codex-path/`
structure lets the Rust package-layout detection behave consistently
across standalone, npm, and future DotSlash installs.

This changes platform npm packages to stage the `codex-package` artifact
for each target under `vendor/<target>`. The Node launcher now resolves
`bin/codex` and prepends `codex-path`, while retaining legacy
`vendor/<target>/codex` and `vendor/<target>/path` fallback support for
local development and migration. The npm staging helper downloads
`codex-package` archives instead of rebuilding the CLI payload from
individual `codex`, `rg`, `bwrap`, and sandbox helper artifacts.

CI still needs to stage npm packages from historical rust-release
workflow artifacts that predate package archives, so the staging scripts
expose an explicit `--allow-legacy-codex-package` fallback. That
fallback synthesizes the canonical package layout from legacy per-binary
artifacts and is wired only into the CI smoke path; release staging
remains strict and continues to require real package archives.

For direct local use, `install_native_deps.py` now points its built-in
default workflow at the same recent artifact run used by CI and
automatically enables legacy package synthesis only when
`--workflow-url` is omitted. Explicit workflow URLs remain strict unless
callers opt in with `--allow-legacy-codex-package`.

## Test plan

- `python3 -m py_compile codex-cli/scripts/build_npm_package.py
codex-cli/scripts/install_native_deps.py scripts/stage_npm_packages.py
scripts/codex_package/cli.py`
- `node --check codex-cli/bin/codex.js`
- `ruby -e 'require "yaml";
YAML.load_file(".github/workflows/rust-release.yml");
YAML.load_file(".github/workflows/ci.yml"); puts "ok"'`
- Staged a synthetic `codex-linux-x64` platform package from a canonical
vendor tree and verified it copied only `bin/`, `codex-path/`,
`codex-resources/`, and `codex-package.json`.
- Imported `install_native_deps.py` and extracted a synthetic
`codex-package-x86_64-unknown-linux-musl.tar.gz` into `vendor/<target>`.
- Ran legacy-layout conversion smokes for Linux, Windows, and unsigned
macOS artifact naming.
- Ran a synthetic `install_native_deps.py` default-workflow smoke that
verifies legacy package synthesis is automatic only when
`--workflow-url` is omitted.
- `NPM_CONFIG_CACHE="$tmp_dir/npm-cache" python3
./scripts/stage_npm_packages.py --release-version 0.125.0 --workflow-url
https://github.com/openai/codex/actions/runs/26131514935 --package codex
--allow-legacy-codex-package --output-dir "$tmp_dir"`
- `node codex-cli/bin/codex.js --version`


---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23637).
* #23638
* __->__ #23637
2026-05-20 12:02:32 -07:00
Eric Traut
7c3cc1db81 Fix thread settings clippy failure (#23724)
## Why

`main` picked up two small Rust build failures after nearby merges:

- #23507 added a real handler for
`ServerNotification::ThreadSettingsUpdated`, but the same variant was
still listed in the ignored-notification match arm. Full Clippy runs
treat the resulting unreachable-pattern warning as an error.
- #23666 added `turn_id` and `truncation_policy` to
`codex_tools::ToolCall`, while the goal extension backend test fixtures
from the goal-extension work still used the old shape. That left
`codex-goal-extension` tests unable to compile once the branches met on
`main`.

## What changed

Removed the duplicate `ThreadSettingsUpdated` match pattern from
`tui/src/chatwidget/protocol.rs`.

Updated the goal extension test `tool_call` helper to populate the new
`ToolCall` fields, and reused that helper for the one direct literal
that still had the old field list.

## Verification

- `just fix -p codex-tui`
- `cargo test -p codex-goal-extension`
2026-05-20 11:58:23 -07:00
sayan-oai
ed6d73b3b9 add standalone websearch api client (#23655)
add standalone web search request types and a `codex-api` client ahead
of the extension-contributed search tool.

this adds typed commands/settings and opaque encrypted output handling
for the new standalone search flow. the endpoint types are close to
finalized but may still shift slightly as that API settles.
2026-05-20 11:38:21 -07:00
jif-oai
d84b824d53 [codex] Preserve failed goal accounting flushes (#23717)
## What
- Preserve database accounting failures from the goal extension instead
of collapsing them into `None`
- Warn with turn/tool context when a flush fails
- Keep stop/abort accounting snapshots alive when the final flush did
not persist

## Why
PR #23696 can finish and discard a turn snapshot after
`account_thread_goal_usage` fails. That loses the final accumulated
accounting state silently. This follow-up keeps that failure explicit
and avoids deleting the local snapshot in the failing path.

## Testing
- `just fmt`
- `cargo test -p codex-goal-extension`
2026-05-20 20:37:27 +02:00
Michael Bolin
110b30d545 install: consume Codex package archives (#23636)
## Summary

Standalone installs should exercise the same canonical package archive
layout that release builds produce, rather than unpacking npm platform
packages and reconstructing a parallel install tree.

This updates `install.sh` and `install.ps1` to prefer
`codex-package-<target>.tar.gz` plus `codex-package_SHA256SUMS`
introduced in https://github.com/openai/codex/pull/23635, authenticate
the checksum manifest against GitHub release metadata, verify the
selected package archive against the authenticated manifest, and install
the package archive directly.

## Compatibility Notes

Package installs still leave a compatibility command at `current/codex`
for managed daemon flows, while visible command shims point at
`bin/codex` inside the package layout.

Recent releases that predate package archives still publish per-platform
npm artifacts, so both installers keep a legacy platform npm fallback
for those versions and verify those archives against release metadata
directly.

Releases old enough to publish only the single root
`codex-npm-<version>.tgz` archive are intentionally out of scope. The
installers fail clearly when neither package archives nor per-platform
npm archives are present.

On Windows, the runtime helper lookups now recognize package-layout
installs where `codex.exe` runs from `bin/`, so
`codex-command-runner.exe` and `codex-windows-sandbox-setup.exe` resolve
from the top-level `codex-resources/` directory. The direct-sibling and
older sibling-resource fallbacks are preserved.

## Test plan

- `sh -n scripts/install/install.sh`
- `bash -n scripts/install/install.sh`
- `pwsh -NoProfile -Command '$tokens=$null; $errors=$null; $null =
[System.Management.Automation.Language.Parser]::ParseFile("scripts/install/install.ps1",
[ref]$tokens, [ref]$errors); if ($errors.Count) { $errors | Format-List
*; exit 1 }'`
- `HOME="$home_dir" CODEX_HOME="$tmp_dir/codex-home"
CODEX_INSTALL_DIR="$bin_dir" PATH="$bin_dir:$PATH" sh
scripts/install/install.sh --release 0.125.0`
- Verified the 0.125.0 isolated install leaves the visible command
pointed at `current/codex` and includes the legacy `codex-resources/rg`
payload.
- `cargo test -p codex-windows-sandbox`
- `just fix -p codex-windows-sandbox`

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23636).
* #23638
* #23637
* __->__ #23636
2026-05-20 11:20:11 -07:00
jif-oai
c5bd131567 feat: add turn_id and truncation_policy to extension tool calls (#23666)
## Why

Extension-owned tools currently receive a stripped `ToolCall` with only
`call_id`, `tool_name`, and `payload`.
That makes extension work that needs turn-local execution context
awkward, especially web-search extension work that needs the active
`truncation_policy` at tool invocation time.

Reconstructing that value from config or `ExtensionData` would be
indirect and could drift from the actual turn context, so the cleaner
fix is to pass the needed turn metadata directly on the extension-facing
invocation type.

## What changed

- added `turn_id` and `truncation_policy` to `codex_tools::ToolCall`
- populated those fields when core adapts `ToolInvocation` into an
extension tool call
- added a focused adapter test that verifies extension executors receive
the forwarded turn metadata
- updated the memories extension tests to construct the richer
`ToolCall`
- added the `codex-utils-output-truncation` dependency to `codex-tools`
and refreshed lockfiles

## Testing

- `cargo test -p codex-tools`
- `cargo test -p codex-memories-extension`
- `cargo test -p codex-core passes_turn_fields_to_extension_call`
- `just bazel-lock-update`
- `just bazel-lock-check`
2026-05-20 20:14:41 +02:00
Eric Traut
edc48e4612 Sync TUI thread settings through app server (#23507)
Builds on #23502.

## Why

#23502 adds the app-server `thread/settings/update` API and matching
`thread/settings/updated` notification. The TUI already lets users
change thread-scoped settings such as model, reasoning effort, service
tier, approvals, permissions, personality, and collaboration mode, but
those updates need to flow through the app server so embedded and
connected clients observe the same thread state.

This is a rework (simplification) of PR
https://github.com/openai/codex/pull/22510. It has the same
functionality, but the underlying `thread/settings/update` api is now
simpler in that it no longer returns the effective settings as a
response. Now, clients receive the effective settings only through the
`thread/settings/updated` notification.

## What Changed

This updates the TUI to send `thread/settings/update` whenever those
thread-scoped settings change and to treat the RPC response as the
authoritative acknowledgement. It also routes `thread/settings/updated`
notifications back into cached session state and the visible chat widget
so active and inactive threads stay in sync after app-server-originated
changes.

The implementation is kept to the TUI layer: settings conversion and
merge logic live under `codex-rs/tui/src/app/thread_settings.rs`, with
dispatch/routing hooks in the existing app and chat widget paths.

## Verification

I manually tested using `codex app-server --listen unix://` and then
launching two copies of the TUI that use the same local app server. I
then resumed the same thread on both and verified that changes like plan
mode, fast mode, model, reasoning effort, etc. are reflected "live" in
the second client when modified in the first and vice versa.
2026-05-20 11:05:14 -07:00
Eric Traut
771a4e74ac Add thread/settings/update app-server API (#23502)
## Why

App-server clients need a way to update a thread's next-turn settings
without starting a turn, adding transcript content, or waiting for turn
lifecycle events. This gives settings UI a direct path for durable
thread settings while clients observe the eventual effective state
through a notification.

This is a simplified rework of PR
https://github.com/openai/codex/pull/22509. In particular, it changes
the `thread/settings/update` api to return immediately rather than
waiting and returning the effective (updated) thread settings. This
makes the new api consistent with `turn/start` and greatly reduces the
complexity of the implementation relative to the earlier attempt.

## What Changed

- Adds experimental `thread/settings/update` with partial-update request
fields and an empty acknowledgment response.
- Adds experimental `thread/settings/updated`, carrying full effective
`ThreadSettings` and scoped by `threadId` to subscribed clients for the
affected thread.
- Shares durable settings validation with `turn/start`, including
`sandboxPolicy` plus `permissions` rejection and `serviceTier: null`
clearing.
- Emits the same settings notification when `turn/start` overrides
change the stored effective thread settings.
- Regenerates app-server protocol schema fixtures and updates
`app-server/README.md`.
2026-05-20 11:03:20 -07:00
Michael Bolin
2b4898cc47 windows-sandbox: add resolved permissions helper (#22896)
## Why

The Windows sandbox migration away from the legacy `SandboxPolicy`
abstraction needs a small local bridge before IPC and core wiring can
move to `PermissionProfile`. Leaf helpers currently branch directly on
`WorkspaceWrite`, which spreads legacy assumptions through path planning
and token setup code.

This PR introduces a Windows-local resolved permissions view so those
helpers can ask Windows-specific questions about runtime
filesystem/network permissions without matching on the legacy policy
enum everywhere.

## What changed

- Added `ResolvedWindowsSandboxPermissions` in
`windows-sandbox-rs/src/resolved_permissions.rs`, with legacy
`SandboxPolicy` constructors for the current call sites.
- Moved `allow.rs` writable-root and read-only-subpath planning onto the
resolved permissions type.
- Preserved Windows `TEMP`/`TMP` writable-root behavior when the
effective policy includes writable tmpdir access.
- Avoided resolving Unix `:slash_tmp` or parent-process `TMPDIR` while
computing Windows writable roots.
- Reused the shared allow-path result for setup write-root gathering and
routed network-block selection through the resolved abstraction.

## Verification

- `cargo test -p codex-windows-sandbox`
- `just fix -p codex-windows-sandbox`
- GitHub CI restarted on the amended commit; Windows Bazel is the
required signal for the Windows-only code paths.












---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22896).
* #23715
* #23714
* #23167
* #22923
* #22918
* __->__ #22896
2026-05-20 17:30:46 +00:00
Felipe Coury
050a2e2668 fix(app-server): speed up shutdown (#23578)
## Why

Pressing `Ctrl+C` or `Ctrl+D` in the TUI could make Codex pause during
shutdown when app-server background work still held outbound sender
clones.

Shutdown tracing against the current `~/.codex` path found three
relevant holders:

- `SkillsWatcher` kept its event-loop task alive until the shutdown
timeout path.
- `AppServerAttestationProvider` retained a strong
`Arc<OutgoingMessageSender>`, which could keep outbound teardown waiting
after the processor task had exited.
- A background `apps/list` task could still own an outbound sender when
shutdown began, causing the in-process app-server runtime to wait for
its outbound channel to close.

## What Changed

- Give `SkillsWatcher` an explicit shutdown `CancellationToken` and
cancel it from app-server teardown so its event loop drops the outbound
sender promptly.
- Change `AppServerAttestationProvider` to keep a
`Weak<OutgoingMessageSender>` and return immediately when it can no
longer be upgraded.
- Give `AppsRequestProcessor` a shutdown `CancellationToken` and cancel
in-flight background `apps/list` work during teardown.

## How to Test

1. Start Codex TUI from a real home configuration.
2. Press `Ctrl+C`.
3. Confirm Codex exits promptly instead of pausing during shutdown.
4. Repeat with `Ctrl+D` and confirm the same prompt exit path.

Focused manual trace validation from the investigation:

- Before the full fix, reproduced shutdown traces showed outbound
teardown waiting on lingering owners, including `attestation.provider=1`
and later `apps.list.task=1`.
- After the fix, fresh real-home `Ctrl+D` traces showed
`app_server.runtime.outbound_state_after_processor_join` with
`owners=none`, `app_server.runtime.wait_outbound_handle = 0ms`, and
total TUI app-server shutdown around `18ms`.

Targeted validation:

- `RUST_MIN_STACK=8388608 cargo test -p codex-app-server`
2026-05-20 17:30:19 +00:00
Eric Traut
c0f7e1b99f [2 of 2] Start fresh TUI thread in background (#23176)
## Why

After the terminal-probe work in #23175, fresh-session startup still
waits for `thread/start` before the chat input can become usable. The
chat widget already has the machinery to hold early submissions until a
session is configured, so fresh `thread/start` does not need to stay on
the input-ready hot path.

Refs #16335.

## What

This PR starts fresh app-server threads in a background task, reports
completion through a startup app event, and attaches the primary session
once `thread/start` returns. Resume and fork startup paths remain
synchronous.

## Benchmark

In the local pty startup benchmark, this PR's pre-optimization base
branch, #23175, measured about 152ms median from launch to accepted chat
input. The stacked result measured about 66ms median, for an approximate
additional savings of 85-95ms. For broader context, the original `main`
baseline before either startup optimization was about 250.5ms median. We
also measured Codex 0.117.0 on the same machine at about 64.6ms median,
so the stacked branch is back in the old-startup-time range.

## Stack

1. [#23175: [1 of 2] Optimize TUI startup terminal
probes](https://github.com/openai/codex/pull/23175) — base PR
2. [#23176: [2 of 2] Start fresh TUI thread in
background](https://github.com/openai/codex/pull/23176) — this PR

## Verification

- `cargo test -p codex-tui`
2026-05-20 10:00:33 -07:00
jif-oai
d4f842f3b3 feat: account active goal progress in the goal extension (#23696)
## Why

The goal extension can create and surface goals, but the live
turn-accounting path still stopped short of persisting active-goal
progress. That leaves token and wall-clock usage, plus
`ThreadGoalUpdated` events, out of sync with the extension boundary once
work actually advances or a goal transitions out of active state.

## What changed

- Teach `GoalAccountingState` to track the current turn, active goal,
token deltas, and wall-clock progress snapshots against the persisted
goal id.
- Flush active-goal accounting from tool-finish, turn-stop, and
turn-abort lifecycle hooks, and emit `ThreadGoalUpdated` events when
persisted progress changes.
- Route `create_goal` and `update_goal` through the same accounting
state so new goals start from the right baseline, final progress is
flushed before status changes, and `update_goal` can mark a goal
`blocked` as well as `complete`.
- Keep budget-limited goals accruing through the end of the turn while
clearing local active-goal state once a turn or explicit update is
finished.
- Expand backend and lifecycle coverage around store ids, baseline
reset, tool-finish accounting, budget-limited carry-through, and
blocked-goal updates.

## Testing

- Added focused backend coverage in
`codex-rs/ext/goal/tests/goal_extension_backend.rs` for baseline reset,
tool-finish accounting, budget-limited turns, and blocked-goal updates.
- Extended `codex-rs/core/src/session/tests.rs` to assert that lifecycle
inputs expose the expected session, thread, and turn store ids.
2026-05-20 18:36:37 +02:00
anp-oai
f198ca115b feat: Add btw alias for side slash command (#23592) 2026-05-20 15:49:35 +00:00
Michael Bolin
e9f59e30d9 release: publish Codex package archive checksums (#23635)
## Summary

Standalone installers and other downstream package consumers need a
stable checksum source for the canonical package archives. Relying on
per-asset metadata makes that harder to consume uniformly, especially
when several package archives are produced in the same release.

This keeps the `codex-package-*.tar.gz` and
`codex-app-server-package-*.tar.gz` assets in the GitHub Release upload
set and adds `codex-package_SHA256SUMS` to `dist/` before the release is
created. The manifest contains one SHA-256 line per package archive and
fails the release job if no package archives are present.




---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23635).
* #23638
* #23637
* #23636
* __->__ #23635
2026-05-20 08:48:04 -07:00
Michael Bolin
b0b383bea3 runtime: use install context for bundled bwrap (#23634)
## Summary

The Linux sandbox should find bundled `bwrap` through the same
package-layout abstraction as the rest of the runtime, instead of
maintaining a separate standalone-specific lookup path.

This adds an `InstallContext` helper for bundled resources and updates
`codex-linux-sandbox` to ask the current install context for
`codex-resources/bwrap` before falling back to the old
executable-relative probes. The tests cover npm-style, standalone, and
canonical package layouts so `bwrap` lookup follows the package
structure introduced earlier in the stack.

## Test plan

- `cargo test -p codex-install-context`
- `cargo test -p codex-linux-sandbox --lib`
- `just fix -p codex-install-context -p codex-linux-sandbox`
- `just bazel-lock-check`





---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23634).
* #23638
* #23637
* #23636
* #23635
* __->__ #23634
2026-05-20 08:24:43 -07:00
pakrym-oai
a52c91d8b5 [codex] Hide deferred tools from code mode prompt (#23605)
## Why

`code_mode_only_guides_all_tools_search_and_calls_deferred_app_tools`
was failing because code-mode prompt generation used the same nested
tool spec list for both the model-visible `exec` guide and the runtime
`ALL_TOOLS` surface. That allowed deferred MCP/app tools, such as
`calendar_timezone_option_99`, to leak into the `exec` description even
though they should only be discoverable through `ALL_TOOLS` at runtime.

## What changed

Split code-mode nested tool planning into two sets in
`core/src/tools/spec_plan.rs`:

- runtime nested tool specs still include deferred tools, so
`tools[...]` and `ALL_TOOLS` can call them
- `exec` prompt docs only render non-deferred tools, so deferred app
tools stay out of the model-visible guide

## Validation

- `cargo test -p codex-core --test all
code_mode_only_guides_all_tools_search_and_calls_deferred_app_tools --
--nocapture`
- looped the same focused test 5 additional times with `cargo test -q -p
codex-core --test all
code_mode_only_guides_all_tools_search_and_calls_deferred_app_tools`
2026-05-20 08:09:45 -07:00
jif-oai
59507b8491 feat: expose turn-start metadata to extensions (#23688)
## Why

The goal extension needs more context when a turn starts than
`turn_store` alone provides.

In particular, goal accounting needs the stable turn id, the effective
collaboration mode, and the cumulative token-usage baseline captured at
turn start so it can:

- suppress goal accounting for plan-mode turns
- compute exact per-turn deltas from cumulative `total_token_usage`
snapshots instead of relying on the most recent usage event alone
- keep the extension-owned accounting path aligned with the host turn
lifecycle

## What

- extend `codex_extension_api::TurnStartInput` to expose `turn_id`,
`collaboration_mode`, and `token_usage_at_turn_start`
- pass the full `TurnContext` plus the captured token-usage baseline
through the turn-start lifecycle emission path
- initialize goal turn accounting from the turn-start baseline and
collaboration mode
- switch goal token accounting to compute deltas from cumulative
`total_token_usage` snapshots
- add coverage for the new turn-start lifecycle fields and for
goal-accounting baseline behavior

## Testing

- added `turn_start_lifecycle_exposes_turn_metadata_and_token_baseline`
in `codex-rs/core/src/session/tests.rs`
- added `ext/goal/tests/accounting.rs` coverage for baseline-aware goal
accounting and plan-mode suppression
2026-05-20 15:54:29 +02:00
jif-oai
1392a2a770 feat: async turn item process (#23692)
Mechanical change
2026-05-20 15:30:01 +02:00
jif-oai
f64fce61b3 feat: async approval contrib (#23690) 2026-05-20 15:13:54 +02:00
jif-oai
b555dd5d1d feat: wire goal extension tools to the dedicated goal store (#23685)
## Why

`ext/goal` already had the tool specs and contributor wiring for
`/goal`, but the installed tools still depended on a placeholder backend
that always errored. That meant the extension could not actually own
goal persistence even though the dedicated `thread_goals` store already
exists.

This change wires the extension tools directly to the dedicated goal
store so the extension can create, read, and complete goals against real
state instead of falling back to host-side placeholders.

## What changed

- make `install_with_backend(...)` require
`Arc<codex_state::StateRuntime>` so goal storage is always available
when the extension is installed
- remove the unused no-backend/public backend abstraction from
`ext/goal` and have the tool executors talk directly to `StateRuntime`
- map `thread_goals` rows into the existing protocol response shape for
`get_goal`, `create_goal`, and `update_goal`
- preserve current thread-list behavior by filling an empty thread
preview from the goal objective when a goal is created through the
extension path
- add integration coverage for the installed tool surface, including
successful goal creation and duplicate-create rejection

## Testing

- `cargo test -p codex-goal-extension`
2026-05-20 14:44:17 +02:00
jif-oai
51d6616431 fix: main (#23675)
Fix main due to conflicting merges
This is only fixing some imports and mechanics
2026-05-20 12:27:39 +02:00
jif-oai
9483b09ea4 feat: rename 2 (#23668)
Just a mechanical renaming
2026-05-20 12:11:44 +02:00
jif-oai
66d5edf825 feat: rename 3 (#23669)
Just a mechanical renaming
2026-05-20 12:07:06 +02:00
jif-oai
93456320ef feat: rename 1 (#23667)
Just a mechanical renaming
2026-05-20 12:05:58 +02:00
jif-oai
18cefba922 Add timeout for remote compaction requests (#23451)
## Why

Remote compaction currently sends a unary `POST /responses/compact` and
waits for the full response before replacing history or emitting the
completed `ContextCompaction` item. Unlike normal `/responses` streaming
requests, this unary compact request had no timeout boundary. If the
backend accepts the request and then stalls before returning a body, the
existing request retry policy never sees a transport error, so the
compact turn can remain stuck after the started item with no completion
or actionable error.

That matches the reported hang shape in issues such as #18363, where
logs show `responses/compact` was posted but no corresponding compact
completion followed. A bounded request timeout gives the existing retry
policy a concrete timeout error to retry instead of letting the user sit
indefinitely on automatic context compaction.

## What

- Add a request timeout to legacy `/responses/compact` calls.
- Size that timeout from the provider stream idle timeout with a
conservative multiplier, so the default compact attempt gets 20 minutes
rather than the 5 minute stream idle window.
- Map API transport timeouts to a request timeout error instead of the
child-process timeout message.

## Testing

- Not run (per request; CI will cover).
2026-05-20 11:56:00 +02:00
richardopenai
000bf5ce6d Migrate exec-server remote registration to environments (#23633)
## Summary
- migrate exec-server remote registration naming from executor to
environment
- align CLI, public Rust exports, registry error messages, and relay
test fixtures with the environment registry contract
- keep the live registration path and response model consistent with
`/cloud/environment/{environment_id}/register`

## Verification
- `cargo test -p codex-exec-server
remote::tests::register_environment_posts_with_auth_provider_headers
--manifest-path /Users/richardlee/code/codex/codex-rs/Cargo.toml`
- `cargo test -p codex-exec-server --test relay
multiplexed_remote_environment_routes_independent_virtual_streams
--manifest-path /Users/richardlee/code/codex/codex-rs/Cargo.toml`
- `cargo check -p codex-cli --manifest-path
/Users/richardlee/code/codex/codex-rs/Cargo.toml` (still running when PR
opened; will update after completion if needed)
2026-05-20 00:25:04 -07:00
sayan-oai
34aad43684 add encryptedcontent to functioncalloutput (#23500)
add new `EncryptedContent` variant to `FunctionCallOutputContentItem`
ahead of standalone websearch.

we need to be able to receive and pass encrypted function call output
from the new web search endpoint back to responsesapi, as we cannot
expose direct search results.
2026-05-19 23:47:48 -07:00
Michael Bolin
cfa16fcc2e runtime: detect Codex package layout (#23596)
## Why

The package-builder stack now creates a canonical Codex package
directory where the entrypoint lives under `bin/`, bundled helper
resources live under `codex-resources/`, and bundled PATH-style tools
live under `codex-path/`. That layout is not specific to the standalone
installer: npm, brew, install scripts, and manually unpacked artifacts
should all be able to use the same package shape.

The Rust runtime still only knew about the legacy standalone release
layout, where resources sit next to the executable. A packaged binary
therefore would not identify its package root or prefer the bundled `rg`
from `codex-path/`.

## What changed

- Adds `CodexPackageLayout` to `codex-install-context` and detects it
from an executable path shaped like `<package>/bin/<entrypoint>` when
`<package>/codex-package.json` is present.
- Splits `InstallContext` into an install `method` plus an optional
package layout so the layout is shared across npm, bun, brew,
standalone, and other launch contexts.
- Stores package-layout paths as `AbsolutePathBuf` values.
- Keeps `codex-resources/` and `codex-path/` optional so Codex can still
run with degraded behavior if sidecar directories are missing.
- Updates `InstallContext::rg_command()` to prefer bundled
`codex-path/rg` or `rg.exe`, then fall back to the legacy standalone
resources location, then system `rg`.
- Updates `codex doctor` reporting so package installs show package,
bin, resources, and path directories, and so bundled search detection
recognizes `codex-path/` for any install method.

## Test plan

- `cargo test -p codex-install-context`
- `cargo test -p codex-cli`
- `cargo test -p codex-tui
update_action::tests::maps_install_context_to_update_action`
- `just bazel-lock-check`
2026-05-19 23:13:49 -07:00
Michael Bolin
57a68fb9e3 ci: build Codex package archives in release workflow (#23582)
## Why

Release CI already builds the Codex entrypoints before staging
artifacts, and the package builder can now package those prebuilt
binaries directly. The workflow should produce package-shaped sidecar
archives from the same staged entrypoints that downstream distribution
channels will eventually consume, without rebuilding `codex` or
`codex-app-server` inside the packaging step.

This intentionally does **not** publish the new package archives as
GitHub Release assets yet. The archives are kept with workflow artifacts
until npm, Homebrew, `install.sh`, winget, and related consumers are
ready to switch over.

## What changed

- Adds a `Build Codex package archive` step to
`.github/workflows/rust-release.yml` after target artifacts are staged.
- Runs `scripts/build_codex_package.py` for both release bundles:
- `primary` builds `codex-package-${TARGET}.tar.gz` with `--variant
codex`.
- `app-server` builds `codex-app-server-package-${TARGET}.tar.gz` with
`--variant codex-app-server`.
- Passes `--entrypoint-bin target/${TARGET}/release/<entrypoint>` so
packages contain the entrypoint already built by the workflow.
- Deletes both package archive names before the final GitHub Release
upload so they remain workflow artifacts only for now.

## Verification

- Parsed `.github/workflows/rust-release.yml` with Ruby's YAML loader.








---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23582).
* #23596
* __->__ #23582
2026-05-20 05:43:53 +00:00
Michael Bolin
343a74076f build: package prebuilt Codex entrypoints (#23586)
## Why

The package builder should describe the binaries it is actually
packaging, not require callers to restate release metadata out of band.
A caller-provided `--version` flag can drift from the workspace version,
but running the target entrypoint to discover its version breaks
cross-target packages when the produced binary cannot execute on the
build host.

This PR keeps package metadata tied to the repository source of truth by
reading `[workspace.package].version` from `codex-rs/Cargo.toml`. It
also prepares the package layout for `codex-app-server` packages: the
same package structure can now represent either the CLI entrypoint or
the app-server entrypoint while keeping shared sidecars such as `rg`,
`bwrap`, and Windows sandbox helpers in the existing package
directories.

## What changed

- Removes the `--version` CLI flag from
`scripts/build_codex_package.py`.
- Adds Cargo.toml version discovery for `codex-package.json.version` via
`codex-rs/Cargo.toml`.
- Adds `--entrypoint-bin` so callers can package a prebuilt entrypoint
instead of rebuilding it with Cargo.
- Makes `--variant` an explicit choice between `codex` and
`codex-app-server`, and uses it to select the cargo binary and packaged
`bin/` entrypoint name.
- Updates `scripts/codex_package/README.md` to document variants,
prebuilt entrypoints, and Cargo.toml version detection.

## Verification

- Compiled `scripts/build_codex_package.py` and
`scripts/codex_package/*.py` with `PYTHONDONTWRITEBYTECODE=1`.
- Ran `scripts/build_codex_package.py --help` and verified `--version`
is gone while `--variant` and `--entrypoint-bin` are present.
- Verified the package builder reads version `0.0.0` from
`codex-rs/Cargo.toml`.
- Built a fake cross-target `codex-app-server` package using a
non-executable `--entrypoint-bin`; verified metadata records version
`0.0.0`, variant `codex-app-server`, and `bin/codex-app-server` as the
entrypoint.
2026-05-19 22:10:03 -07:00
xl-openai
dc255b0d8a feat: Add vertical remote plugin collection support (#23584)
- Adds an explicit vertical marketplace kind for plugin/list that
fail-open fetches collection=vertical only when full remote plugins are
disabled.

- Renames the global remote marketplace/cache identity to
openai-curated-remote and materializes remote installs with backend
release versions and app manifests.
2026-05-19 22:03:08 -07:00
Eric Traut
9dda71dbae Warn on invalid UTF-8 in AGENTS.md files (#23232)
Fixes #23223.

## Why

Malformed AGENTS instructions should not fail silently. The reported
issue had invalid UTF-8 in a global `AGENTS.md`; before this change,
Codex treated that decode failure like a missing file, so the personal
instructions disappeared without a user-visible explanation and the
rollout had no `# AGENTS.md instructions` block.

Project-level AGENTS files already used lossy decoding, so their
instructions still appeared, but invalid bytes were replaced without
telling the user. Global and project AGENTS files should behave
consistently: keep usable instruction text when possible, and surface a
diagnostic when bytes had to be replaced.

## What changed

Global `AGENTS.override.md` and `AGENTS.md` loading now reads bytes and
decodes with replacement characters on invalid UTF-8, matching
project-level AGENTS behavior. Both global and project AGENTS loading
now emit a startup warning when invalid UTF-8 is found, and both keep
the instruction text with invalid byte sequences replaced.

Missing files, non-file candidates, empty files, and the existing
`AGENTS.override.md` before `AGENTS.md` precedence keep their current
behavior.

## How users see it

The warnings flow through the existing startup warning surface.
App-server clients receive config-time startup warnings as
`configWarning` notifications during initialization, and thread startup
emits startup warnings as thread-scoped `warning` notifications.

Global AGENTS invalid UTF-8 warnings can appear on both surfaces.
Project-level AGENTS invalid UTF-8 warnings are discovered while
building thread instructions, so they appear as thread-scoped `warning`
notifications. Clients that render warning notifications in the
conversation surface show the message as a visible diagnostic instead of
silently hiding or altering instructions.
2026-05-19 21:56:46 -07:00
Ahmed Ibrahim
5a4202ad90 [codex] Preserve raw code-mode exec output by default (#23564)
## Why
Code mode can use nested unified exec calls as data sources. When those
calls omit `max_output_tokens`, code mode should receive raw command
output so the script can parse or summarize it itself. When code mode
does provide `max_output_tokens`, that explicit nested budget should be
respected, including values above the default unified exec limit, rather
than being capped before code mode sees the result.

## What
- Preserve direct unified exec truncation behavior, while letting
code-mode exec/write_stdin keep `max_output_tokens` as `None` unless
explicitly supplied.
- Make code-mode tool results use raw output when no explicit limit is
present, and use the explicit nested limit directly when one is
specified.
- Refactor unified exec output formatting so `truncated_output` takes
the caller-selected token budget.
- Add e2e integration coverage for explicit nested exec limits, omitted
nested exec limits, outer exec limit propagation, omitted-limit outputs
that exceed both the default and a small truncation policy, explicit
nested limits above those caps, and high explicit limits that still
compact larger command output.
- Reuse the code-mode turn setup helper while directly asserting the
exact exec output item in each test.

## Testing
- `just fmt`
- `git diff --check`
- Not run locally per repo guidance; CI should validate the e2e
integration tests.
2026-05-20 04:02:14 +00:00
Eric Traut
e43a2e297f Fix stale background terminal poll events (#23231)
## Why

Issue #23214 reports `/ps` showing no background terminals while the
status line still says it is waiting for a background terminal. The race
is in core: `write_stdin` can poll a process that exits before the
response returns. The process manager correctly returns `process_id:
None`, but the handler still emitted a `TerminalInteraction` event using
the requested session id, causing clients to believe a dead process was
still being polled.

Fixes #23214.

## What changed

- Suppress `TerminalInteraction` events for empty `write_stdin` polls
once `response.process_id` is `None`.
- Continue emitting interactions for non-empty stdin, even if that input
causes the process to exit before the response returns.
- Extend the unified exec integration test to assert completed empty
polls do not emit terminal interactions.

## Verification

- `cargo test -p codex-core --test all
unified_exec_emits_one_begin_and_one_end_event`
- `cargo test -p codex-core --test all
unified_exec_emits_terminal_interaction_for_write_stdin`

`cargo test -p codex-core` currently aborts in unrelated
`agent::control::tests::resume_agent_from_rollout_uses_edge_data_when_descendant_metadata_source_is_stale`
with a reproducible stack overflow.
2026-05-19 20:48:37 -07:00
Ahmed Ibrahim
532b9c83ae Move plugin and skill warmup into session startup (#23535)
## Why

Plugin and skill loading is useful as warmup and early validation, but
session startup does not need to wait for that work before it can
continue building the session. Keeping it on the serial startup path
adds avoidable latency to every fresh thread start.

We still want invalid skill configurations to show up quickly, and we
want the warmup to exercise the same plugin and skill manager caches
that the normal turn path uses.

## What changed

- moved plugin and skill warmup into the session startup async path
instead of eagerly awaiting it on the serial setup path
- kept the warmup using the session's resolved filesystem/environment
context so skill loading still sees the right roots
- preserved early skill-load error logging so broken skill
configurations still surface during startup
- left the per-turn plugin and skill loading path unchanged, so turns
still use the normal cached managers

## Testing

- Not run locally; relying on CI for validation.
2026-05-19 20:05:52 -07:00
viyatb-oai
c3faea0b09 feat: add permission profile list api (#23412)
## Why

Clients need a typed permission-profile catalog instead of
reconstructing that state from config internals.

## What changed

- Added `permissionProfile/list` to the app-server v2 protocol with
cursor pagination and optional `cwd`.
- The list response includes built-in permission profiles plus
config-defined `[permissions.<id>]` profiles from the effective config
for the request context.
- Permission profiles keep optional `description` metadata for display
purposes.
- App-server docs and schema fixtures are updated for the new RPC.
2026-05-20 02:42:56 +00:00
Michael Bolin
1495302347 feat: expose codex-app-server version flag (#23593)
## Why

`codex-app-server` is published as a standalone release binary, so it
should support the same basic version inspection behavior users expect
from command-line tools. This is independent of package assembly:
package metadata now comes from `codex-rs/Cargo.toml`, but the
standalone app-server binary should still answer `--version` directly.

## What changed

- Enables Clap's generated `--version` flag for the `codex-app-server`
binary by adding `#[command(version)]` to its top-level parser.

## Verification

- Ran `cargo run -p codex-app-server --bin codex-app-server --
--version` and verified it prints `codex-app-server 0.0.0`.
2026-05-19 19:01:05 -07:00
starr-openai
64ef6cd1e4 Fan out rust-ci-full nextest by platform (#23358)
## Why

`rust-ci-full` was paying the full Cargo nextest build-and-run cost once
per platform, with Windows ARM64 as the long pole. This change moves the
heavy work into one reusable per-platform flow: build a nextest archive
once, then replay it across four shards so the platform lane spends less
time running tests serially. For Windows ARM64, the archive is
cross-compiled on Windows x64 and replayed on native Windows ARM64
shards so the slow ARM64 machine is used for execution rather than
compilation.

## What changed

- split the `rust-ci-full` nextest matrix into five explicit
per-platform reusable-workflow calls
- add `.github/workflows/rust-ci-full-nextest-platform.yml` to build one
archive, upload timings/helpers, replay four nextest shards, upload
per-shard JUnit, and roll the shard status back up per platform
- add Windows CI helpers for Dev Drive setup and MSVC ARM64 linker
environment export so the Windows ARM64 archive can be produced on
Windows x64
- keep the existing Cargo git CLI fetch hardening inside the reusable
workflow, since caller workflow-level `env` does not flow through
`workflow_call`
- document the archive-backed shard shape in
`.github/workflows/README.md`
- raise the default nextest slow timeout to 30s so the sharded full-CI
path does not treat every >15s test as stuck

## Verification

- validated the archive/shard flow with live GitHub Actions runs on this
PR branch
- Windows ARM64 cross-compile latency on completed runs:
- https://github.com/openai/codex/actions/runs/26118759651: `34m30s`
lane e2e, `17m16s` archive build, `9m55s` shard phase
- https://github.com/openai/codex/actions/runs/26120777976: `30m36s`
lane e2e, `17m21s` archive build, `6m50s` shard phase
- comparable pre-cross-compile sharded Windows ARM64 runs were `55m01s`,
`50m21s`, and `46m42s`, so the completed cross-compile runs improved the
lane by roughly `12m` to `24m` versus the prior range
- latest corrected cross-compile run:
https://github.com/openai/codex/actions/runs/26120777976
  - Windows ARM64 archive built successfully on Windows x64
- native Windows ARM64 shards started immediately after the archive
upload
- 3/4 Windows ARM64 shards passed; the failing shard hit the same
existing `code_mode` test failure seen outside this lane
- downloaded failed-shard JUnit XML from the validation runs and
confirmed the remaining red is from known test failures, not
archive/shard wiring
- no local Codex tests run per repo guidance

## Notes

- this PR does not change developers.openai.com documentation
2026-05-19 17:54:41 -07:00
923 changed files with 48911 additions and 15051 deletions

View File

@@ -8,4 +8,4 @@ script = ""
[[actions]]
name = "Run"
icon = "run"
command = "cargo +1.93.0 run --manifest-path=codex-rs/Cargo.toml --bin codex -- -c mcp_oauth_credentials_store=file"
command = "cargo +1.95.0 run --manifest-path=codex-rs/Cargo.toml --bin codex -- -c mcp_oauth_credentials_store=file"

View File

@@ -3,7 +3,7 @@ FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04
ARG TZ
ARG DEBIAN_FRONTEND=noninteractive
ARG NODE_MAJOR=22
ARG RUST_TOOLCHAIN=1.92.0
ARG RUST_TOOLCHAIN=1.95.0
# Keep this in sync with .devcontainer/codex-install/package.json and pnpm-lock.yaml.
ARG CODEX_NPM_VERSION=0.121.0

View File

@@ -7,7 +7,7 @@
"args": {
"TZ": "${localEnv:TZ:UTC}",
"NODE_MAJOR": "22",
"RUST_TOOLCHAIN": "1.92.0",
"RUST_TOOLCHAIN": "1.95.0",
"CODEX_NPM_VERSION": "0.121.0"
}
},

View File

@@ -0,0 +1,17 @@
name: setup-msvc-env
description: Expose an MSVC developer environment for the requested Windows target.
inputs:
target:
description: Rust target triple that will be built on this Windows runner.
required: true
host-arch:
description: Optional Visual Studio host architecture override.
required: false
default: ""
runs:
using: composite
steps:
- name: Expose MSVC SDK environment
shell: pwsh
run: '& "$env:GITHUB_ACTION_PATH/setup-msvc-env.ps1" -Target "${{ inputs.target }}" -HostArch "${{ inputs.host-arch }}"'

View File

@@ -0,0 +1,257 @@
param(
[Parameter(Mandatory = $true)]
[string]$Target,
[string]$HostArch = ""
)
# Cargo can cross-compile the Rust code for Windows ARM64 on a Windows x64
# runner, but rustup alone does not expose the matching MSVC/UCRT include and
# library paths. Ask Visual Studio for the target-specific developer
# environment, then persist the relevant variables through GITHUB_ENV so the
# later Cargo step sees the same environment as a normal VsDevCmd shell.
switch ($Target) {
"x86_64-pc-windows-msvc" {
$TargetArch = "x64"
$RequiredComponent = "Microsoft.VisualStudio.Component.VC.Tools.x86.x64"
}
"aarch64-pc-windows-msvc" {
$TargetArch = "arm64"
$RequiredComponent = "Microsoft.VisualStudio.Component.VC.Tools.ARM64"
}
default {
throw "Unsupported Windows MSVC target: $Target"
}
}
# VsDevCmd needs both sides of the cross compile: the architecture of the
# machine running the tools and the architecture of the binaries being linked.
# Infer the host from the runner unless a caller needs to override it.
if (-not $HostArch) {
$HostArch = if ($env:PROCESSOR_ARCHITEW6432 -eq "ARM64" -or $env:PROCESSOR_ARCHITECTURE -eq "ARM64") {
"arm64"
} else {
"x64"
}
}
$VsWhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
if (-not (Test-Path $VsWhere)) {
throw "vswhere.exe not found"
}
# Require the target VC tools component, not merely any Visual Studio install,
# so an x64 archive producer cannot silently link ARM64 tests with the wrong
# SDK/toolchain layout.
$InstallPath = & $VsWhere -latest -products * -requires $RequiredComponent -property installationPath 2>$null
if (-not $InstallPath) {
throw "Could not locate a Visual Studio installation with component $RequiredComponent"
}
$VsDevCmd = Join-Path $InstallPath "Common7\Tools\VsDevCmd.bat"
if (-not (Test-Path $VsDevCmd)) {
throw "VsDevCmd.bat not found at $VsDevCmd"
}
$VarsToExport = @(
"INCLUDE",
"LIB",
"LIBPATH",
"PATH",
"UCRTVersion",
"UniversalCRTSdkDir",
"VCINSTALLDIR",
"VCToolsInstallDir",
"WindowsLibPath",
"WindowsSdkBinPath",
"WindowsSdkDir",
"WindowsSDKLibVersion",
"WindowsSDKVersion"
)
# Run VsDevCmd inside cmd.exe because it is a batch file, then copy just the
# variables Cargo/rustc need into the GitHub Actions environment file. PowerShell
# cannot mutate the parent composite-action environment directly.
$EnvLines = & cmd.exe /c ('"{0}" -no_logo -arch={1} -host_arch={2} >nul && set' -f $VsDevCmd, $TargetArch, $HostArch)
$VcToolsInstallDir = $null
foreach ($Line in $EnvLines) {
if ($Line -notmatch "^(.*?)=(.*)$") {
continue
}
$Name = $Matches[1]
$Value = $Matches[2]
if ($VarsToExport -contains $Name) {
if ($Name -ieq "Path") {
$Name = "PATH"
}
if ($Name -eq "VCToolsInstallDir") {
$VcToolsInstallDir = $Value
}
"$Name=$Value" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
}
}
if (-not $VcToolsInstallDir) {
throw "VCToolsInstallDir was not exported by VsDevCmd.bat"
}
# Prefer Rust's bundled linker when rustup provides one, then Visual Studio's
# LLVM linker, and finally MSVC link.exe. This keeps the cross-compile path close
# to Rust's normal Windows MSVC behavior while still working on runner images
# where one of those linkers is absent.
$Linker = $null
$Rustc = Get-Command rustc -ErrorAction SilentlyContinue
if ($Rustc) {
$Sysroot = (& rustc --print sysroot 2>$null).Trim()
$RustHost = & rustc -vV 2>$null | Select-String "^host: " | ForEach-Object { $_.Line.Substring(6) }
if ($RustHost) {
$RustHost = $RustHost.Trim()
}
if ($Sysroot -and $RustHost) {
$RustLld = Join-Path $Sysroot "lib\rustlib\$RustHost\bin\rust-lld.exe"
if (Test-Path $RustLld) {
$Linker = $RustLld
}
}
}
if (-not $Linker) {
$Linker = Join-Path $InstallPath "VC\Tools\Llvm\x64\bin\lld-link.exe"
}
if (-not (Test-Path $Linker)) {
$Linker = Join-Path $VcToolsInstallDir "bin\Host${HostArch}\${TargetArch}\link.exe"
}
if (-not (Test-Path $Linker)) {
throw "Windows linker not found at $Linker"
}
# rustc passes `/arm64hazardfree` for ARM64 MSVC links. The lld variants on our
# Windows x64 archive producers reject that flag, including when rustc places it
# inside a response file. Compile a tiny forwarding wrapper that strips only
# that unsupported flag, then delegate every other argument to the real linker.
if ($TargetArch -eq "arm64" -and (Split-Path -Leaf $Linker) -match "lld") {
$WrapperDir = Join-Path $env:RUNNER_TEMP "msvc-lld-wrapper"
New-Item -Path $WrapperDir -ItemType Directory -Force | Out-Null
$WrapperPath = Join-Path $WrapperDir "lld-link-wrapper.exe"
$WrapperSource = @'
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
internal static class Program
{
private static int Main(string[] args)
{
var linker = Environment.GetEnvironmentVariable("MSVC_REAL_LINKER");
if (string.IsNullOrEmpty(linker))
{
Console.Error.WriteLine("MSVC_REAL_LINKER is not set");
return 1;
}
var startInfo = new ProcessStartInfo(linker)
{
UseShellExecute = false,
};
var filteredArgs = new List<string> { "-flavor", "link", "/defaultlib:ucrt", "/nodefaultlib:libucrt" };
foreach (var arg in args)
{
if (!string.Equals(arg, "/arm64hazardfree", StringComparison.OrdinalIgnoreCase))
{
filteredArgs.Add(QuoteArgument(FilterResponseFile(arg)));
}
}
startInfo.Arguments = string.Join(" ", filteredArgs);
using var process = Process.Start(startInfo);
if (process is null)
{
Console.Error.WriteLine($"Failed to start linker: {linker}");
return 1;
}
process.WaitForExit();
return process.ExitCode;
}
private static string FilterResponseFile(string argument)
{
if (argument.Length < 2 || argument[0] != '@')
{
return argument;
}
var responsePath = argument.Substring(1);
if (!File.Exists(responsePath))
{
return argument;
}
var filteredResponsePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".rsp");
var responseContents = Regex.Replace(
File.ReadAllText(responsePath),
"/arm64hazardfree",
string.Empty,
RegexOptions.IgnoreCase);
File.WriteAllText(filteredResponsePath, responseContents);
return "@" + filteredResponsePath;
}
private static string QuoteArgument(string argument)
{
if (argument.Length == 0)
{
return "\"\"";
}
if (argument.IndexOfAny(new[] { ' ', '\t', '"' }) < 0)
{
return argument;
}
var quoted = new StringBuilder("\"");
var backslashes = 0;
foreach (var character in argument)
{
if (character == '\\')
{
backslashes++;
continue;
}
if (character == '"')
{
quoted.Append('\\', (backslashes * 2) + 1);
quoted.Append(character);
backslashes = 0;
continue;
}
quoted.Append('\\', backslashes);
backslashes = 0;
quoted.Append(character);
}
quoted.Append('\\', backslashes * 2);
quoted.Append('"');
return quoted.ToString();
}
}
'@
$WrapperSourcePath = Join-Path $WrapperDir "lld-link-wrapper.cs"
$WrapperSource | Out-File -FilePath $WrapperSourcePath -Encoding utf8
$Csc = Join-Path $InstallPath "MSBuild\Current\Bin\Roslyn\csc.exe"
if (-not (Test-Path $Csc)) {
throw "csc.exe not found at $Csc"
}
& $Csc /nologo /target:exe /out:$WrapperPath $WrapperSourcePath
if ($LASTEXITCODE -ne 0) {
throw "Failed to compile lld-link wrapper"
}
"MSVC_REAL_LINKER=$Linker" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
$Linker = $WrapperPath
}
Write-Output "Using Windows linker: $Linker"
$CargoTarget = $Target.ToUpperInvariant().Replace("-", "_")
"CARGO_TARGET_${CargoTarget}_LINKER=$Linker" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append

View File

@@ -1,29 +1,20 @@
name: setup-rusty-v8-musl
description: Download and verify musl rusty_v8 artifacts for Cargo builds.
name: setup-rusty-v8
description: Download and verify Codex-built rusty_v8 artifacts for Cargo builds.
inputs:
target:
description: Rust musl target triple.
description: Rust target triple with Codex-built V8 release artifacts.
required: true
runs:
using: composite
steps:
- name: Configure musl rusty_v8 artifact overrides and verify checksums
- name: Configure rusty_v8 artifact overrides and verify checksums
shell: bash
env:
TARGET: ${{ inputs.target }}
run: |
set -euo pipefail
case "${TARGET}" in
x86_64-unknown-linux-musl|aarch64-unknown-linux-musl)
;;
*)
echo "Unsupported musl rusty_v8 target: ${TARGET}" >&2
exit 1
;;
esac
version="$(python3 "${GITHUB_WORKSPACE}/.github/scripts/rusty_v8_bazel.py" resolved-v8-crate-version)"
release_tag="rusty-v8-v${version}"
base_url="https://github.com/openai/codex/releases/download/${release_tag}"
@@ -42,6 +33,10 @@ runs:
exit 1
fi
(cd "${binding_dir}" && sha256sum -c "${checksums_path}")
if command -v sha256sum >/dev/null 2>&1; then
(cd "${binding_dir}" && sha256sum -c "${checksums_path}")
else
(cd "${binding_dir}" && shasum -a 256 -c "${checksums_path}")
fi
echo "RUSTY_V8_ARCHIVE=${archive_path}" >> "${GITHUB_ENV}"
echo "RUSTY_V8_SRC_BINDING_PATH=${binding_path}" >> "${GITHUB_ENV}"

View File

@@ -3,56 +3,56 @@
"codex": {
"platforms": {
"macos-aarch64": {
"regex": "^codex-aarch64-apple-darwin\\.zst$",
"path": "codex"
"regex": "^codex-package-aarch64-apple-darwin\\.tar\\.zst$",
"path": "bin/codex"
},
"macos-x86_64": {
"regex": "^codex-x86_64-apple-darwin\\.zst$",
"path": "codex"
"regex": "^codex-package-x86_64-apple-darwin\\.tar\\.zst$",
"path": "bin/codex"
},
"linux-x86_64": {
"regex": "^codex-x86_64-unknown-linux-musl-bundle\\.tar\\.zst$",
"path": "codex"
"regex": "^codex-package-x86_64-unknown-linux-musl\\.tar\\.zst$",
"path": "bin/codex"
},
"linux-aarch64": {
"regex": "^codex-aarch64-unknown-linux-musl-bundle\\.tar\\.zst$",
"path": "codex"
"regex": "^codex-package-aarch64-unknown-linux-musl\\.tar\\.zst$",
"path": "bin/codex"
},
"windows-x86_64": {
"regex": "^codex-x86_64-pc-windows-msvc\\.exe\\.zst$",
"path": "codex.exe"
"regex": "^codex-package-x86_64-pc-windows-msvc\\.tar\\.zst$",
"path": "bin/codex.exe"
},
"windows-aarch64": {
"regex": "^codex-aarch64-pc-windows-msvc\\.exe\\.zst$",
"path": "codex.exe"
"regex": "^codex-package-aarch64-pc-windows-msvc\\.tar\\.zst$",
"path": "bin/codex.exe"
}
}
},
"codex-app-server": {
"platforms": {
"macos-aarch64": {
"regex": "^codex-app-server-aarch64-apple-darwin\\.zst$",
"path": "codex-app-server"
"regex": "^codex-app-server-package-aarch64-apple-darwin\\.tar\\.zst$",
"path": "bin/codex-app-server"
},
"macos-x86_64": {
"regex": "^codex-app-server-x86_64-apple-darwin\\.zst$",
"path": "codex-app-server"
"regex": "^codex-app-server-package-x86_64-apple-darwin\\.tar\\.zst$",
"path": "bin/codex-app-server"
},
"linux-x86_64": {
"regex": "^codex-app-server-x86_64-unknown-linux-musl\\.zst$",
"path": "codex-app-server"
"regex": "^codex-app-server-package-x86_64-unknown-linux-musl\\.tar\\.zst$",
"path": "bin/codex-app-server"
},
"linux-aarch64": {
"regex": "^codex-app-server-aarch64-unknown-linux-musl\\.zst$",
"path": "codex-app-server"
"regex": "^codex-app-server-package-aarch64-unknown-linux-musl\\.tar\\.zst$",
"path": "bin/codex-app-server"
},
"windows-x86_64": {
"regex": "^codex-app-server-x86_64-pc-windows-msvc\\.exe\\.zst$",
"path": "codex-app-server.exe"
"regex": "^codex-app-server-package-x86_64-pc-windows-msvc\\.tar\\.zst$",
"path": "bin/codex-app-server.exe"
},
"windows-aarch64": {
"regex": "^codex-app-server-aarch64-pc-windows-msvc\\.exe\\.zst$",
"path": "codex-app-server.exe"
"regex": "^codex-app-server-package-aarch64-pc-windows-msvc\\.tar\\.zst$",
"path": "bin/codex-app-server.exe"
}
}
},

View File

@@ -7,6 +7,11 @@
"format": "tar.gz",
"path": "codex-zsh/bin/zsh"
},
"macos-x86_64": {
"name": "codex-zsh-x86_64-apple-darwin.tar.gz",
"format": "tar.gz",
"path": "codex-zsh/bin/zsh"
},
"linux-x86_64": {
"name": "codex-zsh-x86_64-unknown-linux-musl.tar.gz",
"format": "tar.gz",

View File

@@ -0,0 +1,172 @@
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'EOF'
Usage: build-codex-package-archive.sh \
--target <rust-target> \
--bundle <primary|app-server> \
--entrypoint-dir <dir> \
--archive-dir <dir> \
[--bwrap-bin <path>] \
[--codex-command-runner-bin <path>] \
[--codex-windows-sandbox-setup-bin <path>] \
[--target-suffixed-entrypoint]
EOF
}
target=""
bundle=""
entrypoint_dir=""
archive_dir=""
target_suffixed_entrypoint="false"
resource_args=()
bwrap_bin_provided="false"
command_runner_bin_provided="false"
sandbox_setup_bin_provided="false"
while [[ $# -gt 0 ]]; do
case "$1" in
--target)
target="${2:?--target requires a value}"
shift 2
;;
--bundle)
bundle="${2:?--bundle requires a value}"
shift 2
;;
--entrypoint-dir)
entrypoint_dir="${2:?--entrypoint-dir requires a value}"
shift 2
;;
--archive-dir)
archive_dir="${2:?--archive-dir requires a value}"
shift 2
;;
--bwrap-bin)
resource_args+=(--bwrap-bin "${2:?--bwrap-bin requires a value}")
bwrap_bin_provided="true"
shift 2
;;
--codex-command-runner-bin)
resource_args+=(
--codex-command-runner-bin
"${2:?--codex-command-runner-bin requires a value}"
)
command_runner_bin_provided="true"
shift 2
;;
--codex-windows-sandbox-setup-bin)
resource_args+=(
--codex-windows-sandbox-setup-bin
"${2:?--codex-windows-sandbox-setup-bin requires a value}"
)
sandbox_setup_bin_provided="true"
shift 2
;;
--target-suffixed-entrypoint)
target_suffixed_entrypoint="true"
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unexpected argument: $1" >&2
usage >&2
exit 1
;;
esac
done
if [[ -z "$target" || -z "$bundle" || -z "$entrypoint_dir" || -z "$archive_dir" ]]; then
usage >&2
exit 1
fi
case "$bundle" in
primary)
variant="codex"
entrypoint="codex"
archive_stem="codex-package"
;;
app-server)
variant="codex-app-server"
entrypoint="codex-app-server"
archive_stem="codex-app-server-package"
;;
*)
echo "No Codex package variant for bundle: $bundle" >&2
exit 1
;;
esac
exe_suffix=""
case "$target" in
*windows*)
exe_suffix=".exe"
;;
esac
entrypoint_name="$entrypoint"
if [[ "$target_suffixed_entrypoint" == "true" ]]; then
entrypoint_name="${entrypoint_name}-${target}"
fi
case "$target" in
*linux*)
bwrap_bin="${entrypoint_dir%/}/bwrap"
if [[ "$bwrap_bin_provided" == "false" && -f "$bwrap_bin" ]]; then
resource_args+=(--bwrap-bin "$bwrap_bin")
fi
;;
*windows*)
command_runner_bin="${entrypoint_dir%/}/codex-command-runner.exe"
sandbox_setup_bin="${entrypoint_dir%/}/codex-windows-sandbox-setup.exe"
if [[ "$command_runner_bin_provided" == "false" && -f "$command_runner_bin" ]]; then
resource_args+=(--codex-command-runner-bin "$command_runner_bin")
fi
if [[ "$sandbox_setup_bin_provided" == "false" && -f "$sandbox_setup_bin" ]]; then
resource_args+=(--codex-windows-sandbox-setup-bin "$sandbox_setup_bin")
fi
;;
esac
repo_root="${GITHUB_WORKSPACE:-}"
if [[ -z "$repo_root" ]]; then
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
fi
if command -v python3 >/dev/null 2>&1; then
python_bin="python3"
else
python_bin="python"
fi
if ! command -v zstd >/dev/null 2>&1 && [[ -x "${repo_root}/.github/workflows/zstd" ]]; then
export PATH="${repo_root}/.github/workflows:${PATH}"
fi
mkdir -p "$archive_dir"
package_dir="${RUNNER_TEMP:-/tmp}/${archive_stem}-${target}"
gzip_archive_path="${archive_dir}/${archive_stem}-${target}.tar.gz"
zstd_archive_path="${archive_dir}/${archive_stem}-${target}.tar.zst"
rm -rf "$package_dir"
python_args=(
"${repo_root}/scripts/build_codex_package.py"
--target "$target"
--variant "$variant"
--entrypoint-bin "${entrypoint_dir%/}/${entrypoint_name}${exe_suffix}"
--cargo-profile release
--package-dir "$package_dir"
--archive-output "$gzip_archive_path"
--archive-output "$zstd_archive_path"
)
if ((${#resource_args[@]} > 0)); then
python_args+=("${resource_args[@]}")
fi
python_args+=(--force)
"$python_bin" "${python_args[@]}"

62
.github/scripts/setup-dev-drive.ps1 vendored Normal file
View File

@@ -0,0 +1,62 @@
# Configure a fast drive for Windows CI jobs.
#
# GitHub-hosted Windows runners do not always expose a secondary D: volume. When
# they do not, try to create a Dev Drive VHD and fall back to C: if the runner
# image does not allow that provisioning path.
function Use-FallbackDrive {
param([string]$Reason)
Write-Warning "$Reason Falling back to C:"
return "C:"
}
function Invoke-BestEffort {
param([scriptblock]$Script, [string]$Description)
try {
& $Script
} catch {
Write-Warning "$Description failed: $($_.Exception.Message)"
}
}
if (Test-Path "D:\") {
Write-Output "Using existing drive at D:"
$Drive = "D:"
} else {
try {
$VhdPath = Join-Path $env:RUNNER_TEMP "codex-dev-drive.vhdx"
$SizeBytes = 64GB
if (Test-Path $VhdPath) {
Remove-Item -Path $VhdPath -Force
}
New-VHD -Path $VhdPath -SizeBytes $SizeBytes -Dynamic -ErrorAction Stop | Out-Null
$Mounted = Mount-VHD -Path $VhdPath -Passthru -ErrorAction Stop
$Disk = $Mounted | Get-Disk -ErrorAction Stop
$Disk | Initialize-Disk -PartitionStyle GPT -ErrorAction Stop
$Partition = $Disk | New-Partition -AssignDriveLetter -UseMaximumSize -ErrorAction Stop
$Volume = $Partition | Format-Volume -FileSystem ReFS -NewFileSystemLabel "CodexDevDrive" -DevDrive -Confirm:$false -Force -ErrorAction Stop
$Drive = "$($Volume.DriveLetter):"
Invoke-BestEffort { fsutil devdrv trust $Drive } "Trusting Dev Drive $Drive"
Invoke-BestEffort { fsutil devdrv enable /disallowAv } "Disabling AV filter attachment for Dev Drives"
Invoke-BestEffort { fsutil devdrv query $Drive } "Querying Dev Drive $Drive"
Write-Output "Using Dev Drive at $Drive"
} catch {
$Drive = Use-FallbackDrive "Failed to create Dev Drive: $($_.Exception.Message)"
}
}
$Tmp = "$Drive\codex-tmp"
New-Item -Path $Tmp -ItemType Directory -Force | Out-Null
@(
"DEV_DRIVE=$Drive"
"TMP=$Tmp"
"TEMP=$Tmp"
) | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append

View File

@@ -21,7 +21,8 @@ The workflows in this directory are split so that pull requests get fast, review
- `rust-ci-full.yml` is the full Cargo-native verification workflow.
It keeps the heavier checks off the PR path while still validating them after merge:
- the full Cargo `clippy` matrix
- the full Cargo `nextest` matrix
- the full Cargo `nextest` matrix via per-platform archive-backed shards
- Windows ARM64 nextest archives cross-compiled on Windows x64, then replayed on native Windows ARM64 shards
- release-profile Cargo builds
- cross-platform `argument-comment-lint`
- Linux remote-env tests

View File

@@ -20,10 +20,10 @@ jobs:
persist-credentials: false
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
- name: Run cargo-deny
uses: EmbarkStudios/cargo-deny-action@82eb9f621fbc699dd0918f3ea06864c14cc84246 # v2
with:
rust-version: 1.93.0
rust-version: 1.95.0
manifest-path: ./codex-rs/Cargo.toml

View File

@@ -26,6 +26,9 @@ jobs:
- name: Verify Bazel clippy flags match Cargo workspace lints
run: python3 .github/scripts/verify_bazel_clippy_lints.py
- name: Test Codex package builder
run: python3 -m unittest discover -s scripts/codex_package -p 'test_*.py'
- name: Setup pnpm
uses: pnpm/action-setup@a8198c4bff370c8506180b035930dea56dbd5288 # v5
with:
@@ -39,9 +42,6 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile
# stage_npm_packages.py requires DotSlash when staging releases.
- uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2
- name: Stage npm package
id: stage_npm_package
env:
@@ -52,15 +52,13 @@ jobs:
# cross-platform native payload required by the npm package layout.
# Passing the workflow URL directly avoids relying on old rust-v*
# branches remaining discoverable via `gh run list --branch ...`.
CODEX_VERSION=0.125.0
WORKFLOW_URL="https://github.com/openai/codex/actions/runs/24901475298"
CODEX_VERSION=0.133.0-alpha.4
WORKFLOW_URL="https://github.com/openai/codex/actions/runs/26201494185"
OUTPUT_DIR="${RUNNER_TEMP}"
# This reused workflow predates the standalone bwrap artifact.
python3 ./scripts/stage_npm_packages.py \
--release-version "$CODEX_VERSION" \
--workflow-url "$WORKFLOW_URL" \
--package codex \
--allow-missing-native-component bwrap \
--output-dir "$OUTPUT_DIR"
PACK_OUTPUT="${OUTPUT_DIR}/codex-npm-${CODEX_VERSION}.tgz"
echo "pack_output=$PACK_OUTPUT" >> "$GITHUB_OUTPUT"

View File

@@ -0,0 +1,464 @@
name: rust-ci-full nextest platform
on:
workflow_call:
inputs:
runner:
required: true
type: string
runner_group:
required: false
default: ""
type: string
runner_labels:
required: false
default: ""
type: string
archive_runner:
required: false
default: ""
type: string
archive_runner_group:
required: false
default: ""
type: string
archive_runner_labels:
required: false
default: ""
type: string
target:
required: true
type: string
profile:
required: true
type: string
artifact_id:
required: true
type: string
remote_env:
required: false
default: false
type: boolean
test_threads:
required: false
default: 0
type: number
use_sccache:
required: false
default: false
type: boolean
# Caller workflow-level env does not flow through workflow_call, so keep the
# Cargo git transport hardening on the archive and shard jobs directly here.
env:
CARGO_NET_GIT_FETCH_WITH_CLI: "true"
jobs:
archive:
name: Build nextest archive
runs-on: ${{ inputs.archive_runner_group != '' && fromJSON(format('{{"group":"{0}","labels":"{1}"}}', inputs.archive_runner_group, inputs.archive_runner_labels)) || inputs.archive_runner != '' && inputs.archive_runner || inputs.runner_group != '' && fromJSON(format('{{"group":"{0}","labels":"{1}"}}', inputs.runner_group, inputs.runner_labels)) || inputs.runner }}
timeout-minutes: 60
defaults:
run:
working-directory: codex-rs
env:
# Windows ARM64 archives are built on Windows x64, while their shards run
# on native Windows ARM64. Key producer-side caches by the archive runner
# so the cross-compile build reuses the Windows x64 cache lineage.
ARCHIVE_CACHE_RUNNER: ${{ inputs.archive_runner != '' && inputs.archive_runner || inputs.runner }}
USE_SCCACHE: ${{ inputs.use_sccache && 'true' || 'false' }}
CARGO_INCREMENTAL: "0"
SCCACHE_CACHE_SIZE: 10G
NEXTEST_ARCHIVE_FILE: nextest-${{ inputs.artifact_id }}.tar.zst
TEST_HELPERS_ARTIFACT: nextest-test-helpers-${{ inputs.artifact_id }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Configure Dev Drive (Windows)
if: ${{ runner.os == 'Windows' }}
shell: pwsh
run: ../.github/scripts/setup-dev-drive.ps1
- name: Install Linux build dependencies
if: ${{ runner.os == 'Linux' }}
shell: bash
run: |
set -euo pipefail
if command -v apt-get >/dev/null 2>&1; then
sudo apt-get update -y
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends pkg-config libcap-dev bubblewrap
fi
- name: Install DotSlash
uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2
- uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
with:
targets: ${{ inputs.target }}
- name: Expose MSVC SDK environment (Windows)
if: ${{ runner.os == 'Windows' && inputs.target == 'aarch64-pc-windows-msvc' }}
uses: ./.github/actions/setup-msvc-env
with:
target: ${{ inputs.target }}
- name: Compute lockfile hash
id: lockhash
shell: bash
run: |
set -euo pipefail
echo "hash=$(sha256sum Cargo.lock | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
echo "toolchain_hash=$(sha256sum rust-toolchain.toml | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
- name: Restore cargo home cache
id: cache_cargo_home_restore
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
key: cargo-home-${{ env.ARCHIVE_CACHE_RUNNER }}-${{ inputs.target }}-${{ inputs.profile }}-${{ steps.lockhash.outputs.hash }}-${{ steps.lockhash.outputs.toolchain_hash }}
restore-keys: |
cargo-home-${{ env.ARCHIVE_CACHE_RUNNER }}-${{ inputs.target }}-${{ inputs.profile }}-
- name: Install sccache
if: ${{ env.USE_SCCACHE == 'true' }}
uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2.62.49
with:
tool: sccache
version: 0.7.5
- name: Configure sccache backend
if: ${{ env.USE_SCCACHE == 'true' }}
shell: bash
run: |
set -euo pipefail
if [[ -n "${ACTIONS_CACHE_URL:-}" && -n "${ACTIONS_RUNTIME_TOKEN:-}" ]]; then
echo "SCCACHE_GHA_ENABLED=true" >> "$GITHUB_ENV"
echo "Using sccache GitHub backend"
else
echo "SCCACHE_GHA_ENABLED=false" >> "$GITHUB_ENV"
if [[ -n "${DEV_DRIVE:-}" ]]; then
echo "SCCACHE_DIR=${DEV_DRIVE}\\.sccache" >> "$GITHUB_ENV"
else
echo "SCCACHE_DIR=${{ github.workspace }}/.sccache" >> "$GITHUB_ENV"
fi
echo "Using sccache local disk + actions/cache fallback"
fi
- name: Enable sccache wrapper
if: ${{ env.USE_SCCACHE == 'true' }}
shell: bash
run: |
set -euo pipefail
wrapper="$(command -v sccache)"
if [[ "${RUNNER_OS}" == "Windows" ]] && command -v cygpath >/dev/null 2>&1; then
wrapper="$(cygpath -w "${wrapper}")"
fi
echo "RUSTC_WRAPPER=${wrapper}" >> "$GITHUB_ENV"
echo "CARGO_BUILD_RUSTC_WRAPPER=${wrapper}" >> "$GITHUB_ENV"
- name: Restore sccache cache (fallback)
if: ${{ env.USE_SCCACHE == 'true' && env.SCCACHE_GHA_ENABLED != 'true' }}
id: cache_sccache_restore
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: ${{ env.SCCACHE_DIR }}
key: sccache-${{ env.ARCHIVE_CACHE_RUNNER }}-${{ inputs.target }}-${{ inputs.profile }}-${{ steps.lockhash.outputs.hash }}-${{ github.run_id }}
restore-keys: |
sccache-${{ env.ARCHIVE_CACHE_RUNNER }}-${{ inputs.target }}-${{ inputs.profile }}-${{ steps.lockhash.outputs.hash }}-
sccache-${{ env.ARCHIVE_CACHE_RUNNER }}-${{ inputs.target }}-${{ inputs.profile }}-
- uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2.62.49
with:
tool: nextest
version: 0.9.103
- name: Enable unprivileged user namespaces (Linux)
if: runner.os == 'Linux'
run: |
sudo sysctl -w kernel.unprivileged_userns_clone=1
if sudo sysctl -a 2>/dev/null | grep -q '^kernel.apparmor_restrict_unprivileged_userns'; then
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
fi
- name: Build nextest archive
shell: bash
run: |
set -euo pipefail
archive_dir="${RUNNER_TEMP}/nextest-archive"
mkdir -p "${archive_dir}"
cargo nextest archive \
--target ${{ inputs.target }} \
--cargo-profile ${{ inputs.profile }} \
--timings \
--archive-file "${archive_dir}/${NEXTEST_ARCHIVE_FILE}"
- name: Build runtime test helpers
if: ${{ runner.os == 'Linux' || runner.os == 'Windows' }}
shell: bash
run: |
set -euo pipefail
helper_dir="${RUNNER_TEMP}/${TEST_HELPERS_ARTIFACT}"
mkdir -p "${helper_dir}"
if [[ "${RUNNER_OS}" == "Linux" ]]; then
cargo build \
--target ${{ inputs.target }} \
--profile ${{ inputs.profile }} \
-p codex-linux-sandbox \
--bin codex-linux-sandbox
cp "target/${{ inputs.target }}/${{ inputs.profile }}/codex-linux-sandbox" "${helper_dir}/"
else
cargo build \
--target ${{ inputs.target }} \
--profile ${{ inputs.profile }} \
-p codex-windows-sandbox \
--bin codex-windows-sandbox-setup \
--bin codex-command-runner
cp "target/${{ inputs.target }}/${{ inputs.profile }}/codex-windows-sandbox-setup.exe" "${helper_dir}/"
cp "target/${{ inputs.target }}/${{ inputs.profile }}/codex-command-runner.exe" "${helper_dir}/"
fi
- name: Upload Cargo timings (nextest)
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: cargo-timings-rust-ci-nextest-${{ inputs.target }}-${{ inputs.profile }}
path: codex-rs/target/**/cargo-timings/cargo-timing.html
if-no-files-found: warn
- name: Upload nextest archive
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: nextest-archive-${{ inputs.artifact_id }}
path: ${{ runner.temp }}/nextest-archive/${{ env.NEXTEST_ARCHIVE_FILE }}
if-no-files-found: error
retention-days: 1
- name: Upload runtime test helpers
if: ${{ runner.os == 'Linux' || runner.os == 'Windows' }}
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: ${{ env.TEST_HELPERS_ARTIFACT }}
path: ${{ runner.temp }}/${{ env.TEST_HELPERS_ARTIFACT }}/*
if-no-files-found: error
retention-days: 1
- name: Save cargo home cache
if: always() && !cancelled() && steps.cache_cargo_home_restore.outputs.cache-hit != 'true'
continue-on-error: true
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
key: cargo-home-${{ env.ARCHIVE_CACHE_RUNNER }}-${{ inputs.target }}-${{ inputs.profile }}-${{ steps.lockhash.outputs.hash }}-${{ steps.lockhash.outputs.toolchain_hash }}
- name: Save sccache cache (fallback)
if: always() && !cancelled() && env.USE_SCCACHE == 'true' && env.SCCACHE_GHA_ENABLED != 'true'
continue-on-error: true
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: ${{ env.SCCACHE_DIR }}
key: sccache-${{ env.ARCHIVE_CACHE_RUNNER }}-${{ inputs.target }}-${{ inputs.profile }}-${{ steps.lockhash.outputs.hash }}-${{ github.run_id }}
- name: sccache stats
if: always() && env.USE_SCCACHE == 'true'
continue-on-error: true
run: sccache --show-stats || true
- name: sccache summary
if: always() && env.USE_SCCACHE == 'true'
shell: bash
run: |
{
echo "### sccache stats — ${{ inputs.target }} (tests)";
echo;
echo '```';
sccache --show-stats || true;
echo '```';
} >> "$GITHUB_STEP_SUMMARY"
shard:
name: Tests shard ${{ matrix.shard }}/4
needs: archive
runs-on: ${{ inputs.runner_group != '' && fromJSON(format('{{"group":"{0}","labels":"{1}"}}', inputs.runner_group, inputs.runner_labels)) || inputs.runner }}
timeout-minutes: 60
defaults:
run:
working-directory: codex-rs
env:
NEXTEST_ARCHIVE_FILE: nextest-${{ inputs.artifact_id }}.tar.zst
TEST_HELPERS_ARTIFACT: nextest-test-helpers-${{ inputs.artifact_id }}
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install Linux build dependencies
if: ${{ runner.os == 'Linux' }}
shell: bash
run: |
set -euo pipefail
if command -v apt-get >/dev/null 2>&1; then
sudo apt-get update -y
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends pkg-config libcap-dev bubblewrap
fi
- name: Install DotSlash
uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2
- uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
with:
targets: ${{ inputs.target }}
- uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2.62.49
with:
tool: nextest
version: 0.9.103
- name: Enable unprivileged user namespaces (Linux)
if: runner.os == 'Linux'
run: |
sudo sysctl -w kernel.unprivileged_userns_clone=1
if sudo sysctl -a 2>/dev/null | grep -q '^kernel.apparmor_restrict_unprivileged_userns'; then
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
fi
- name: Set up remote test env (Docker)
if: ${{ runner.os == 'Linux' && inputs.remote_env }}
shell: bash
run: |
set -euo pipefail
export CODEX_TEST_REMOTE_ENV_CONTAINER_NAME="codex-remote-test-env-${{ github.run_id }}-${{ matrix.shard }}"
source "${GITHUB_WORKSPACE}/scripts/test-remote-env.sh"
echo "CODEX_TEST_REMOTE_ENV=${CODEX_TEST_REMOTE_ENV}" >> "$GITHUB_ENV"
echo "CODEX_TEST_REMOTE_EXEC_SERVER_URL=${CODEX_TEST_REMOTE_EXEC_SERVER_URL}" >> "$GITHUB_ENV"
- name: Download nextest archive
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: nextest-archive-${{ inputs.artifact_id }}
path: ${{ runner.temp }}/nextest-archive
- name: Download runtime test helpers
if: ${{ runner.os == 'Linux' || runner.os == 'Windows' }}
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: ${{ env.TEST_HELPERS_ARTIFACT }}
path: ${{ runner.temp }}/${{ env.TEST_HELPERS_ARTIFACT }}
- name: tests
id: test
shell: bash
run: |
set -euo pipefail
archive_file="${RUNNER_TEMP}/nextest-archive/${NEXTEST_ARCHIVE_FILE}"
workspace_root="$(pwd)"
if [[ "${RUNNER_OS}" == "Windows" ]]; then
archive_file="$(cygpath -w "${archive_file}")"
workspace_root="$(cygpath -w "${workspace_root}")"
fi
if [[ "${RUNNER_OS}" == "Linux" ]]; then
helper_dir="${RUNNER_TEMP}/${TEST_HELPERS_ARTIFACT}"
helper_target_dir="$(pwd)/target/${{ inputs.target }}/${{ inputs.profile }}"
mkdir -p "${helper_target_dir}"
cp "${helper_dir}/codex-linux-sandbox" "${helper_target_dir}/"
chmod +x "${helper_target_dir}/codex-linux-sandbox"
elif [[ "${RUNNER_OS}" == "Windows" ]]; then
helper_dir="${RUNNER_TEMP}/${TEST_HELPERS_ARTIFACT}"
helper_target_dir="$(pwd)/target/${{ inputs.target }}/${{ inputs.profile }}"
mkdir -p "${helper_target_dir}"
cp "${helper_dir}/codex-windows-sandbox-setup.exe" "${helper_target_dir}/"
cp "${helper_dir}/codex-command-runner.exe" "${helper_target_dir}/"
fi
nextest_args=(
run
--no-fail-fast
--archive-file "${archive_file}"
--workspace-remap "${workspace_root}"
--partition "hash:${{ matrix.shard }}/4"
)
if [[ "${{ inputs.test_threads }}" != "0" ]]; then
nextest_args+=(--test-threads "${{ inputs.test_threads }}")
fi
test_command=(cargo nextest "${nextest_args[@]}")
if [[ "${RUNNER_OS}" == "Linux" ]]; then
sandbox_helper="${helper_target_dir}/codex-linux-sandbox"
test_command=(
env
"CARGO_BIN_EXE_codex-linux-sandbox=${sandbox_helper}"
"CARGO_BIN_EXE_codex_linux_sandbox=${sandbox_helper}"
cargo nextest "${nextest_args[@]}"
)
elif [[ "${RUNNER_OS}" == "Windows" ]]; then
setup_helper="$(cygpath -w "${helper_target_dir}/codex-windows-sandbox-setup.exe")"
command_runner="$(cygpath -w "${helper_target_dir}/codex-command-runner.exe")"
test_command=(
env
"CARGO_BIN_EXE_codex_windows_sandbox_setup=${setup_helper}"
"CARGO_BIN_EXE_codex_command_runner=${command_runner}"
cargo nextest "${nextest_args[@]}"
)
fi
"${test_command[@]}"
env:
RUST_BACKTRACE: 1
RUST_MIN_STACK: "8388608" # 8 MiB
NEXTEST_STATUS_LEVEL: leak
- name: Upload nextest JUnit report
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: nextest-junit-rust-ci-${{ inputs.artifact_id }}-shard-${{ matrix.shard }}
path: codex-rs/target/nextest/default/junit.xml
if-no-files-found: warn
- name: Tear down remote test env
if: ${{ always() && runner.os == 'Linux' && inputs.remote_env }}
shell: bash
run: |
set +e
if [[ "${STEPS_TEST_OUTCOME}" != "success" ]]; then
docker logs "${CODEX_TEST_REMOTE_ENV}" || true
fi
docker rm -f "${CODEX_TEST_REMOTE_ENV}" >/dev/null 2>&1 || true
env:
STEPS_TEST_OUTCOME: ${{ steps.test.outcome }}
- name: verify tests passed
if: steps.test.outcome == 'failure'
run: |
echo "Tests failed. See logs for details."
exit 1
result:
name: Platform result
needs: shard
if: always()
runs-on: ubuntu-24.04
steps:
- name: Confirm test shards passed
shell: bash
run: |
if [[ "${{ needs.shard.result }}" != "success" ]]; then
echo "Nextest shards finished with result: ${{ needs.shard.result }}" >&2
exit 1
fi

View File

@@ -25,11 +25,16 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
- uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
with:
components: rustfmt
- uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2.62.49
with:
tool: just
- name: cargo fmt
run: cargo fmt -- --config imports_granularity=Item --check
- name: Rust benchmark smoke test
run: just bench-smoke
cargo_shear:
name: cargo shear
@@ -41,7 +46,7 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
- uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
- uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2.62.49
with:
tool: cargo-shear@1.11.2
@@ -58,7 +63,7 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
- uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
with:
toolchain: nightly-2025-09-18
components: llvm-tools-preview, rustc-dev, rust-src
@@ -261,7 +266,7 @@ jobs:
fi
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "${packages[@]}"
fi
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
- uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
with:
targets: ${{ matrix.target }}
components: clippy
@@ -436,9 +441,9 @@ jobs:
echo "CFLAGS=${cflags}" >> "$GITHUB_ENV"
echo "CXXFLAGS=${cxxflags}" >> "$GITHUB_ENV"
- if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl' }}
name: Configure musl rusty_v8 artifact overrides and verify checksums
uses: ./.github/actions/setup-rusty-v8-musl
- if: ${{ !contains(matrix.target, 'windows') }}
name: Configure rusty_v8 artifact overrides and verify checksums
uses: ./.github/actions/setup-rusty-v8
with:
target: ${{ matrix.target }}
@@ -521,242 +526,73 @@ jobs:
/var/cache/apt
key: apt-${{ matrix.runner }}-${{ matrix.target }}-v1
tests:
name: Tests — ${{ matrix.runner }} - ${{ matrix.target }}${{ matrix.remote_env == 'true' && ' (remote)' || '' }}
runs-on: ${{ matrix.runs_on || matrix.runner }}
# Windows ARM64 is the long pole here, and nextest retries plus targeted
# Windows timeout headroom need more than 45m to finish reliably.
timeout-minutes: 60
defaults:
run:
working-directory: codex-rs
env:
# Speed up repeated builds across CI runs by caching compiled objects, except on
# arm64 macOS runners cross-targeting x86_64 where ring/cc-rs can produce
# mixed-architecture archives under sccache.
USE_SCCACHE: ${{ (startsWith(matrix.runner, 'windows') || (matrix.runner == 'macos-15-xlarge' && matrix.target == 'x86_64-apple-darwin')) && 'false' || 'true' }}
CARGO_INCREMENTAL: "0"
SCCACHE_CACHE_SIZE: 10G
tests_macos_aarch64:
name: Tests — macos-15-xlarge - aarch64-apple-darwin
uses: ./.github/workflows/rust-ci-full-nextest-platform.yml
with:
runner: macos-15-xlarge
target: aarch64-apple-darwin
profile: ci-test
artifact_id: macos-aarch64
use_sccache: true
secrets: inherit
strategy:
fail-fast: false
matrix:
include:
- runner: macos-15-xlarge
target: aarch64-apple-darwin
profile: dev
- runner: ubuntu-24.04
target: x86_64-unknown-linux-gnu
profile: dev
remote_env: "true"
runs_on:
group: codex-runners
labels: codex-linux-x64
- runner: ubuntu-24.04-arm
target: aarch64-unknown-linux-gnu
profile: dev
runs_on:
group: codex-runners
labels: codex-linux-arm64
- runner: windows-x64
target: x86_64-pc-windows-msvc
profile: dev
runs_on:
group: codex-runners
labels: codex-windows-x64
- runner: windows-arm64
target: aarch64-pc-windows-msvc
profile: dev
runs_on:
group: codex-runners
labels: codex-windows-arm64
tests_linux_x64_remote:
name: Tests — ubuntu-24.04 - x86_64-unknown-linux-gnu (remote)
uses: ./.github/workflows/rust-ci-full-nextest-platform.yml
with:
runner: ubuntu-24.04
runner_group: codex-runners
runner_labels: codex-linux-x64
target: x86_64-unknown-linux-gnu
profile: ci-test
artifact_id: linux-x64-remote
remote_env: true
use_sccache: true
secrets: inherit
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install Linux build dependencies
if: ${{ runner.os == 'Linux' }}
shell: bash
run: |
set -euo pipefail
if command -v apt-get >/dev/null 2>&1; then
sudo apt-get update -y
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends pkg-config libcap-dev bubblewrap
fi
tests_linux_arm64:
name: Tests — ubuntu-24.04-arm - aarch64-unknown-linux-gnu
uses: ./.github/workflows/rust-ci-full-nextest-platform.yml
with:
runner: ubuntu-24.04-arm
runner_group: codex-runners
runner_labels: codex-linux-arm64
target: aarch64-unknown-linux-gnu
profile: ci-test
artifact_id: linux-arm64
use_sccache: true
secrets: inherit
# Some integration tests rely on DotSlash being installed.
# See https://github.com/openai/codex/pull/7617.
- name: Install DotSlash
uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2
tests_windows_x64:
name: Tests — windows-x64 - x86_64-pc-windows-msvc
uses: ./.github/workflows/rust-ci-full-nextest-platform.yml
with:
runner: windows-x64
runner_group: codex-runners
runner_labels: codex-windows-x64
target: x86_64-pc-windows-msvc
profile: ci-test
artifact_id: windows-x64
test_threads: 8
secrets: inherit
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
with:
targets: ${{ matrix.target }}
- name: Compute lockfile hash
id: lockhash
working-directory: codex-rs
shell: bash
run: |
set -euo pipefail
echo "hash=$(sha256sum Cargo.lock | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
echo "toolchain_hash=$(sha256sum rust-toolchain.toml | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
- name: Restore cargo home cache
id: cache_cargo_home_restore
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
key: cargo-home-${{ matrix.runner }}-${{ matrix.target }}-${{ matrix.profile }}-${{ steps.lockhash.outputs.hash }}-${{ steps.lockhash.outputs.toolchain_hash }}
restore-keys: |
cargo-home-${{ matrix.runner }}-${{ matrix.target }}-${{ matrix.profile }}-
- name: Install sccache
if: ${{ env.USE_SCCACHE == 'true' }}
uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2.62.49
with:
tool: sccache
version: 0.7.5
- name: Configure sccache backend
if: ${{ env.USE_SCCACHE == 'true' }}
shell: bash
run: |
set -euo pipefail
if [[ -n "${ACTIONS_CACHE_URL:-}" && -n "${ACTIONS_RUNTIME_TOKEN:-}" ]]; then
echo "SCCACHE_GHA_ENABLED=true" >> "$GITHUB_ENV"
echo "Using sccache GitHub backend"
else
echo "SCCACHE_GHA_ENABLED=false" >> "$GITHUB_ENV"
echo "SCCACHE_DIR=${{ github.workspace }}/.sccache" >> "$GITHUB_ENV"
echo "Using sccache local disk + actions/cache fallback"
fi
- name: Enable sccache wrapper
if: ${{ env.USE_SCCACHE == 'true' }}
shell: bash
run: echo "RUSTC_WRAPPER=sccache" >> "$GITHUB_ENV"
- name: Restore sccache cache (fallback)
if: ${{ env.USE_SCCACHE == 'true' && env.SCCACHE_GHA_ENABLED != 'true' }}
id: cache_sccache_restore
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: ${{ github.workspace }}/.sccache/
key: sccache-${{ matrix.runner }}-${{ matrix.target }}-${{ matrix.profile }}-${{ steps.lockhash.outputs.hash }}-${{ github.run_id }}
restore-keys: |
sccache-${{ matrix.runner }}-${{ matrix.target }}-${{ matrix.profile }}-${{ steps.lockhash.outputs.hash }}-
sccache-${{ matrix.runner }}-${{ matrix.target }}-${{ matrix.profile }}-
- uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2.62.49
with:
tool: nextest
version: 0.9.103
- name: Enable unprivileged user namespaces (Linux)
if: runner.os == 'Linux'
run: |
# Required for bubblewrap to work on Linux CI runners.
sudo sysctl -w kernel.unprivileged_userns_clone=1
# Ubuntu 24.04+ can additionally gate unprivileged user namespaces
# behind AppArmor.
if sudo sysctl -a 2>/dev/null | grep -q '^kernel.apparmor_restrict_unprivileged_userns'; then
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
fi
- name: Set up remote test env (Docker)
if: ${{ runner.os == 'Linux' && matrix.remote_env == 'true' }}
shell: bash
run: |
set -euo pipefail
export CODEX_TEST_REMOTE_ENV_CONTAINER_NAME=codex-remote-test-env
source "${GITHUB_WORKSPACE}/scripts/test-remote-env.sh"
echo "CODEX_TEST_REMOTE_ENV=${CODEX_TEST_REMOTE_ENV}" >> "$GITHUB_ENV"
echo "CODEX_TEST_REMOTE_EXEC_SERVER_URL=${CODEX_TEST_REMOTE_EXEC_SERVER_URL}" >> "$GITHUB_ENV"
- name: tests
id: test
run: cargo nextest run --no-fail-fast --target ${{ matrix.target }} --cargo-profile ci-test --timings
env:
RUST_BACKTRACE: 1
RUST_MIN_STACK: "8388608" # 8 MiB
NEXTEST_STATUS_LEVEL: leak
- name: Upload nextest JUnit report
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: nextest-junit-rust-ci-${{ matrix.runner }}-${{ matrix.target }}-${{ matrix.profile }}
path: codex-rs/target/nextest/default/junit.xml
if-no-files-found: warn
- name: Upload Cargo timings (nextest)
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: cargo-timings-rust-ci-nextest-${{ matrix.target }}-${{ matrix.profile }}
path: codex-rs/target/**/cargo-timings/cargo-timing.html
if-no-files-found: warn
- name: Save cargo home cache
if: always() && !cancelled() && steps.cache_cargo_home_restore.outputs.cache-hit != 'true'
continue-on-error: true
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
key: cargo-home-${{ matrix.runner }}-${{ matrix.target }}-${{ matrix.profile }}-${{ steps.lockhash.outputs.hash }}-${{ steps.lockhash.outputs.toolchain_hash }}
- name: Save sccache cache (fallback)
if: always() && !cancelled() && env.USE_SCCACHE == 'true' && env.SCCACHE_GHA_ENABLED != 'true'
continue-on-error: true
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: ${{ github.workspace }}/.sccache/
key: sccache-${{ matrix.runner }}-${{ matrix.target }}-${{ matrix.profile }}-${{ steps.lockhash.outputs.hash }}-${{ github.run_id }}
- name: sccache stats
if: always() && env.USE_SCCACHE == 'true'
continue-on-error: true
run: sccache --show-stats || true
- name: sccache summary
if: always() && env.USE_SCCACHE == 'true'
shell: bash
run: |
{
echo "### sccache stats — ${{ matrix.target }} (tests)";
echo;
echo '```';
sccache --show-stats || true;
echo '```';
} >> "$GITHUB_STEP_SUMMARY"
- name: Tear down remote test env
if: ${{ always() && runner.os == 'Linux' && matrix.remote_env == 'true' }}
shell: bash
run: |
set +e
if [[ "${STEPS_TEST_OUTCOME}" != "success" ]]; then
docker logs codex-remote-test-env || true
fi
docker rm -f codex-remote-test-env >/dev/null 2>&1 || true
env:
STEPS_TEST_OUTCOME: ${{ steps.test.outcome }}
- name: verify tests passed
if: steps.test.outcome == 'failure'
run: |
echo "Tests failed. See logs for details."
exit 1
tests_windows_arm64:
name: Tests — windows-arm64 - aarch64-pc-windows-msvc
uses: ./.github/workflows/rust-ci-full-nextest-platform.yml
with:
runner: windows-arm64
runner_group: codex-runners
runner_labels: codex-windows-arm64
archive_runner: windows-x64
archive_runner_group: codex-runners
archive_runner_labels: codex-windows-x64
target: aarch64-pc-windows-msvc
profile: ci-test
artifact_id: windows-arm64
test_threads: 8
use_sccache: true
secrets: inherit
# --- Gatherer job for the full post-merge workflow --------------------------
results:
@@ -768,7 +604,11 @@ jobs:
argument_comment_lint_package,
argument_comment_lint_prebuilt,
lint_build,
tests,
tests_macos_aarch64,
tests_linux_x64_remote,
tests_linux_arm64,
tests_windows_x64,
tests_windows_arm64,
]
if: always()
runs-on: ubuntu-24.04
@@ -781,13 +621,21 @@ jobs:
echo "general: ${{ needs.general.result }}"
echo "shear : ${{ needs.cargo_shear.result }}"
echo "lint : ${{ needs.lint_build.result }}"
echo "tests : ${{ needs.tests.result }}"
echo "test macos : ${{ needs.tests_macos_aarch64.result }}"
echo "test linux : ${{ needs.tests_linux_x64_remote.result }}"
echo "test arm64 : ${{ needs.tests_linux_arm64.result }}"
echo "test winx64: ${{ needs.tests_windows_x64.result }}"
echo "test winarm: ${{ needs.tests_windows_arm64.result }}"
[[ '${{ needs.argument_comment_lint_package.result }}' == 'success' ]] || { echo 'argument_comment_lint_package failed'; exit 1; }
[[ '${{ needs.argument_comment_lint_prebuilt.result }}' == 'success' ]] || { echo 'argument_comment_lint_prebuilt failed'; exit 1; }
[[ '${{ needs.general.result }}' == 'success' ]] || { echo 'general failed'; exit 1; }
[[ '${{ needs.cargo_shear.result }}' == 'success' ]] || { echo 'cargo_shear failed'; exit 1; }
[[ '${{ needs.lint_build.result }}' == 'success' ]] || { echo 'lint_build failed'; exit 1; }
[[ '${{ needs.tests.result }}' == 'success' ]] || { echo 'tests failed'; exit 1; }
[[ '${{ needs.tests_macos_aarch64.result }}' == 'success' ]] || { echo 'tests_macos_aarch64 failed'; exit 1; }
[[ '${{ needs.tests_linux_x64_remote.result }}' == 'success' ]] || { echo 'tests_linux_x64_remote failed'; exit 1; }
[[ '${{ needs.tests_linux_arm64.result }}' == 'success' ]] || { echo 'tests_linux_arm64 failed'; exit 1; }
[[ '${{ needs.tests_windows_x64.result }}' == 'success' ]] || { echo 'tests_windows_x64 failed'; exit 1; }
[[ '${{ needs.tests_windows_arm64.result }}' == 'success' ]] || { echo 'tests_windows_arm64 failed'; exit 1; }
- name: sccache summary note
if: always()

View File

@@ -67,11 +67,16 @@ jobs:
with:
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
persist-credentials: false
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
- uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
with:
components: rustfmt
- uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2.62.49
with:
tool: just
- name: cargo fmt
run: cargo fmt -- --config imports_granularity=Item --check
- name: Rust benchmark smoke test
run: just bench-smoke
cargo_shear:
name: cargo shear
@@ -86,7 +91,7 @@ jobs:
with:
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
persist-credentials: false
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
- uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
- uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2.62.49
with:
tool: cargo-shear@1.11.2
@@ -106,7 +111,7 @@ jobs:
with:
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
persist-credentials: false
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
- uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
- name: Install nightly argument-comment-lint toolchain
shell: bash
run: |

View File

@@ -60,7 +60,7 @@ jobs:
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
- uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
with:
toolchain: nightly-2025-09-18
targets: ${{ matrix.target }}

View File

@@ -100,7 +100,7 @@ jobs:
Write-Host "Total RAM: $ramGiB GiB"
Write-Host "Disk usage:"
Get-PSDrive -PSProvider FileSystem | Format-Table -AutoSize Name, @{Name='Size(GB)';Expression={[math]::Round(($_.Used + $_.Free) / 1GB, 1)}}, @{Name='Free(GB)';Expression={[math]::Round($_.Free / 1GB, 1)}}
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
- uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
with:
targets: ${{ matrix.target }}
@@ -220,6 +220,21 @@ jobs:
"$dest/${binary}-${{ matrix.target }}.exe"
done
- name: Install DotSlash
uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2
- name: Build Codex package archives
shell: bash
run: |
set -euo pipefail
for bundle in primary app-server; do
bash "${GITHUB_WORKSPACE}/.github/scripts/build-codex-package-archive.sh" \
--target "${{ matrix.target }}" \
--bundle "$bundle" \
--entrypoint-dir "target/${{ matrix.target }}/release" \
--archive-dir "dist/${{ matrix.target }}"
done
- name: Build Python runtime wheel
shell: bash
run: |
@@ -243,16 +258,12 @@ jobs:
stage_dir="${RUNNER_TEMP}/openai-codex-cli-bin-${{ matrix.target }}"
wheel_dir="${GITHUB_WORKSPACE}/python-runtime-dist/${{ matrix.target }}"
# Keep the helpers next to codex.exe in the runtime wheel so Windows
# sandbox/elevation lookup matches the standalone release zip.
python "${GITHUB_WORKSPACE}/sdk/python/scripts/update_sdk_artifacts.py" \
stage-runtime \
"$stage_dir" \
"${GITHUB_WORKSPACE}/codex-rs/target/${{ matrix.target }}/release/codex.exe" \
"dist/${{ matrix.target }}/codex-package-${{ matrix.target }}.tar.gz" \
--codex-version "${GITHUB_REF_NAME}" \
--platform-tag "$platform_tag" \
--resource-binary "${GITHUB_WORKSPACE}/codex-rs/target/${{ matrix.target }}/release/codex-command-runner.exe" \
--resource-binary "${GITHUB_WORKSPACE}/codex-rs/target/${{ matrix.target }}/release/codex-windows-sandbox-setup.exe"
--platform-tag "$platform_tag"
"${RUNNER_TEMP}/python-runtime-build-venv/Scripts/python.exe" -m build --wheel --outdir "$wheel_dir" "$stage_dir"
- name: Upload Python runtime wheel
@@ -262,9 +273,6 @@ jobs:
path: python-runtime-dist/${{ matrix.target }}/*.whl
if-no-files-found: error
- name: Install DotSlash
uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2
- name: Compress artifacts
shell: bash
run: |
@@ -283,7 +291,7 @@ jobs:
base="$(basename "$f")"
# Skip files that are already archives (shouldn't happen, but be
# safe).
if [[ "$base" == *.tar.gz || "$base" == *.zip || "$base" == *.dmg ]]; then
if [[ "$base" == *.tar.gz || "$base" == *.tar.zst || "$base" == *.zip || "$base" == *.dmg ]]; then
continue
fi

View File

@@ -69,6 +69,10 @@ jobs:
fail-fast: false
matrix:
include:
- runner: macos-15-large
target: x86_64-apple-darwin
variant: macos-15
archive_name: codex-zsh-x86_64-apple-darwin.tar.gz
- runner: macos-15-xlarge
target: aarch64-apple-darwin
variant: macos-15

View File

@@ -56,7 +56,7 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
- uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
- name: Validate tag matches Cargo.toml version
shell: bash
env:
@@ -180,25 +180,25 @@ jobs:
binaries: "codex-app-server"
build_dmg: "false"
# Release artifacts intentionally ship MUSL-linked Linux binaries.
- runner: ubuntu-24.04
- runner: codex-linux-x64-xl
target: x86_64-unknown-linux-musl
bundle: primary
artifact_name: x86_64-unknown-linux-musl
binaries: "codex codex-responses-api-proxy bwrap"
build_dmg: "false"
- runner: ubuntu-24.04
- runner: codex-linux-x64-xl
target: x86_64-unknown-linux-musl
bundle: app-server
artifact_name: x86_64-unknown-linux-musl-app-server
binaries: "codex-app-server"
build_dmg: "false"
- runner: ubuntu-24.04-arm
- runner: codex-linux-arm64
target: aarch64-unknown-linux-musl
bundle: primary
artifact_name: aarch64-unknown-linux-musl
binaries: "codex codex-responses-api-proxy bwrap"
build_dmg: "false"
- runner: ubuntu-24.04-arm
- runner: codex-linux-arm64
target: aarch64-unknown-linux-musl
bundle: app-server
artifact_name: aarch64-unknown-linux-musl-app-server
@@ -254,7 +254,7 @@ jobs:
sudo apt-get update -y
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y libubsan1
fi
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
- uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
with:
targets: ${{ matrix.target }}
@@ -340,13 +340,12 @@ jobs:
echo "CFLAGS=${cflags}" >> "$GITHUB_ENV"
echo "CXXFLAGS=${cxxflags}" >> "$GITHUB_ENV"
- if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl' }}
name: Configure musl rusty_v8 artifact overrides and verify checksums
uses: ./.github/actions/setup-rusty-v8-musl
- name: Configure rusty_v8 artifact overrides and verify checksums
uses: ./.github/actions/setup-rusty-v8
with:
target: ${{ matrix.target }}
- if: ${{ contains(matrix.target, 'linux') && matrix.bundle == 'primary' }}
- if: ${{ contains(matrix.target, 'linux') }}
name: Build bwrap and export digest
shell: bash
run: |
@@ -519,6 +518,20 @@ jobs:
cp target/${{ matrix.target }}/release/codex-${{ matrix.target }}.dmg "$dest/codex-${{ matrix.target }}.dmg"
fi
- name: Build Codex package archive
if: ${{ runner.os != 'macOS' || env.SIGN_MACOS == 'true' }}
shell: bash
env:
TARGET: ${{ matrix.target }}
BUNDLE: ${{ matrix.bundle }}
run: |
set -euo pipefail
bash "${GITHUB_WORKSPACE}/.github/scripts/build-codex-package-archive.sh" \
--target "$TARGET" \
--bundle "$BUNDLE" \
--entrypoint-dir "target/${TARGET}/release" \
--archive-dir "dist/${TARGET}"
- name: Build Python runtime wheel
if: ${{ matrix.bundle == 'primary' && (runner.os != 'macOS' || env.SIGN_MACOS == 'true') }}
shell: bash
@@ -555,18 +568,10 @@ jobs:
"${GITHUB_WORKSPACE}/sdk/python/scripts/update_sdk_artifacts.py"
stage-runtime
"$stage_dir"
"${GITHUB_WORKSPACE}/codex-rs/target/${{ matrix.target }}/release/codex"
"dist/${{ matrix.target }}/codex-package-${{ matrix.target }}.tar.gz"
--codex-version "${GITHUB_REF_NAME}"
--platform-tag "$platform_tag"
)
if [[ "${{ matrix.target }}" == *linux* ]]; then
# Keep bwrap in the runtime wheel so Linux sandbox fallback behavior
# matches the standalone release bundle on hosts without system bwrap.
stage_runtime_args+=(
--resource-binary
"${GITHUB_WORKSPACE}/codex-rs/target/${{ matrix.target }}/release/bwrap"
)
fi
python3 "${stage_runtime_args[@]}"
"${RUNNER_TEMP}/python-runtime-build-venv/bin/python" -m build --wheel --outdir "$wheel_dir" "$stage_dir"
@@ -786,6 +791,20 @@ jobs:
cp "$dmg_source" "$dest/$dmg_name"
fi
- name: Build Codex package archive
shell: bash
env:
TARGET: ${{ matrix.target }}
BUNDLE: ${{ matrix.bundle }}
run: |
set -euo pipefail
bash "${GITHUB_WORKSPACE}/.github/scripts/build-codex-package-archive.sh" \
--target "$TARGET" \
--bundle "$BUNDLE" \
--entrypoint-dir "dist/${TARGET}" \
--archive-dir "dist/${TARGET}" \
--target-suffixed-entrypoint
- name: Build Python runtime wheel
if: ${{ matrix.bundle == 'primary' }}
shell: bash
@@ -814,7 +833,7 @@ jobs:
"${GITHUB_WORKSPACE}/sdk/python/scripts/update_sdk_artifacts.py" \
stage-runtime \
"$stage_dir" \
"${GITHUB_WORKSPACE}/codex-rs/dist/${{ matrix.target }}/codex-${{ matrix.target }}" \
"dist/${{ matrix.target }}/codex-package-${{ matrix.target }}.tar.gz" \
--codex-version "${GITHUB_REF_NAME}" \
--platform-tag "$platform_tag"
"${RUNNER_TEMP}/python-runtime-build-venv/bin/python" -m build --wheel --outdir "$wheel_dir" "$stage_dir"
@@ -1083,6 +1102,29 @@ jobs:
ls -R dist/
- name: Add Codex package checksum manifest
run: |
set -euo pipefail
manifest="dist/codex-package_SHA256SUMS"
tmp_manifest="$(mktemp)"
find dist -type f \
\( -name 'codex-package-*.tar.gz' -o -name 'codex-app-server-package-*.tar.gz' \) \
-print |
sort |
while IFS= read -r archive; do
sha256sum "$archive" |
awk -v name="$(basename "$archive")" '{ print $1 " " name }'
done > "$tmp_manifest"
if [[ ! -s "$tmp_manifest" ]]; then
echo "No Codex package archives found for checksum manifest"
exit 1
fi
mv "$tmp_manifest" "$manifest"
cat "$manifest"
- name: Add config schema release asset
run: |
cp codex-rs/core/config.schema.json dist/config-schema.json
@@ -1157,8 +1199,6 @@ jobs:
if: ${{ env.SIGN_MACOS == 'true' }}
run: pnpm install --frozen-lockfile
# stage_npm_packages.py requires DotSlash when staging releases.
- uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2
- name: Stage npm packages
if: ${{ env.SIGN_MACOS == 'true' }}
env:

View File

@@ -152,9 +152,9 @@ jobs:
python-version: "3.12"
- name: Set up Rust toolchain for Cargo smoke
uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
with:
toolchain: "1.93.0"
toolchain: "1.95.0"
- name: Build Bazel V8 release pair
env:

View File

@@ -166,9 +166,9 @@ jobs:
python-version: "3.12"
- name: Set up Rust toolchain for Cargo smoke
uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
with:
toolchain: "1.93.0"
toolchain: "1.95.0"
- name: Build Bazel V8 release pair
env:
@@ -310,9 +310,9 @@ jobs:
architecture: x64
- name: Set up Codex Rust toolchain for Cargo smoke
uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
with:
toolchain: "1.93.0"
toolchain: "1.95.0"
targets: ${{ matrix.target }}
- name: Install rusty_v8 Rust toolchain
@@ -401,7 +401,7 @@ jobs:
cd codex-rs
RUSTY_V8_ARCHIVE="${GITHUB_WORKSPACE}/${archive}" \
RUSTY_V8_SRC_BINDING_PATH="${GITHUB_WORKSPACE}/${binding}" \
cargo +1.93.0 test -p codex-v8-poc --target "${TARGET}" --features sandbox --no-run
cargo +1.95.0 test -p codex-v8-poc --target "${TARGET}" --features sandbox --no-run
)
- name: Upload staged artifacts

View File

@@ -30,6 +30,7 @@ In the codex-rs folder where the rust code lives:
- Prefer private modules and explicitly exported public crate API.
- If you change `ConfigToml` or nested config types, run `just write-config-schema` to update `codex-rs/core/config.schema.json`.
- When working with MCP tool calls, prefer using `codex-rs/codex-mcp/src/mcp_connection_manager.rs` to handle mutation of tools and tool calls. Aim to minimize the footprint of changes and leverage existing abstractions rather than plumbing code through multiple levels of function calls.
- Do not call `reset_client_session` unnecessarily; let the incremental check logic decide whether to reuse the previous request.
- If you change Rust dependencies (`Cargo.toml` or `Cargo.lock`), run `just bazel-lock-update` from the
repo root to refresh `MODULE.bazel.lock`, and include that lockfile update in the same change.
- After dependency changes, run `just bazel-lock-check` from the repo root so lockfile drift is caught
@@ -52,12 +53,13 @@ In the codex-rs folder where the rust code lives:
the new implementation so the invariants stay close to the code that owns them.
- Avoid adding new standalone methods to `codex-rs/tui/src/chatwidget.rs` unless the change is
trivial; prefer new modules/files and keep `chatwidget.rs` focused on orchestration.
- When running Rust commands (e.g. `just fix` or `cargo test`) be patient with the command and never try to kill them using the PID. Rust lock can make the execution slow, this is expected.
- When running Rust commands (e.g. `just fix` or `just test`) be patient with the command and never try to kill them using the PID. Rust lock can make the execution slow, this is expected.
Run `just fmt` (in `codex-rs` directory) automatically after you have finished making Rust code changes; do not ask for approval to run it. Additionally, run the tests:
1. Run the test for the specific project that was changed. For example, if changes were made in `codex-rs/tui`, run `cargo test -p codex-tui`.
2. Once those pass, if any changes were made in common, core, or protocol, run the complete test suite with `cargo test` (or `just test` if `cargo-nextest` is installed). Avoid `--all-features` for routine local runs because it expands the build matrix and can significantly increase `target/` disk usage; use it only when you specifically need full feature coverage. project-specific or individual tests can be run without asking the user, but do ask the user before running the complete test suite.
1. Do not run `cargo test` directly. Use `just test` so test execution follows the repo defaults.
2. Run the test for the specific project that was changed. For example, if changes were made in `codex-rs/tui`, run `just test -p codex-tui`.
3. Once those pass, if any changes were made in common, core, or protocol, run the complete test suite with `just test`. Avoid `--all-features` for routine local runs because it expands the build matrix and can significantly increase `target/` disk usage; use it only when you specifically need full feature coverage. project-specific or individual tests can be run without asking the user, but do ask the user before running the complete test suite.
Before finalizing a large change to `codex-rs`, run `just fix -p <project>` (in `codex-rs` directory) to fix any linter issues in the code. Prefer scoping with `-p` to avoid slow workspacewide Clippy builds; only run `just fix` without `-p` if you changed shared crates. Do not re-run tests after running `fix` or `fmt`.
@@ -120,7 +122,7 @@ is easy to review and future diffs stay visual.
When UI or text output changes intentionally, update the snapshots as follows:
- Run tests to generate any updated snapshots:
- `cargo test -p codex-tui`
- `just test -p codex-tui`
- Check whats pending:
- `cargo insta pending-snapshots -p codex-tui`
- Review changes by reading the generated `*.snap.new` files directly in the repo, or preview a specific file:
@@ -214,6 +216,6 @@ These guidelines apply to app-server protocol work in `codex-rs`, especially:
- Regenerate schema fixtures when API shapes change:
`just write-app-server-schema`
(and `just write-app-server-schema --experimental` when experimental API fixtures are affected).
- Validate with `cargo test -p codex-app-server-protocol`.
- Validate with `just test -p codex-app-server-protocol`.
- Avoid boilerplate tests that only assert experimental field markers for individual
request fields in `common.rs`; rely on schema generation/tests and behavioral coverage instead.

View File

@@ -163,7 +163,7 @@ use_repo(nightly_rust, "rust_toolchains")
toolchains = use_extension("@rules_rs//rs/experimental/toolchains:module_extension.bzl", "toolchains")
toolchains.toolchain(
edition = "2024",
version = "1.93.0",
version = "1.95.0",
)
use_repo(toolchains, "default_rust_toolchains")

217
MODULE.bazel.lock generated
View File

@@ -768,6 +768,7 @@
"compact_str_0.8.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"borsh\",\"optional\":true,\"req\":\"^1\"},{\"name\":\"bytes\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"castaway\",\"req\":\"^0.2.3\"},{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"cfg-if\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"diesel\",\"optional\":true,\"req\":\"^2\"},{\"name\":\"itoa\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"markup\",\"optional\":true,\"req\":\"^0.13\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"proptest\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"std\"],\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"quickcheck\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"quickcheck_macros\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rayon\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"size_32\"],\"name\":\"rkyv\",\"optional\":true,\"req\":\"^0.7\"},{\"default_features\":false,\"features\":[\"alloc\",\"size_32\"],\"kind\":\"dev\",\"name\":\"rkyv\",\"req\":\"^0.7\"},{\"name\":\"rustversion\",\"req\":\"^1\"},{\"name\":\"ryu\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"derive\",\"alloc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1\"},{\"features\":[\"union\"],\"name\":\"smallvec\",\"optional\":true,\"req\":\"^1\"},{\"default_features\":false,\"name\":\"sqlx\",\"optional\":true,\"req\":\"^0.7\"},{\"name\":\"static_assertions\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"test-case\",\"req\":\"^3\"},{\"kind\":\"dev\",\"name\":\"test-strategy\",\"req\":\"^0.3\"}],\"features\":{\"arbitrary\":[\"dep:arbitrary\"],\"borsh\":[\"dep:borsh\"],\"bytes\":[\"dep:bytes\"],\"default\":[\"std\"],\"diesel\":[\"dep:diesel\"],\"markup\":[\"dep:markup\"],\"proptest\":[\"dep:proptest\"],\"quickcheck\":[\"dep:quickcheck\"],\"rkyv\":[\"dep:rkyv\"],\"serde\":[\"dep:serde\"],\"smallvec\":[\"dep:smallvec\"],\"sqlx\":[\"dep:sqlx\",\"std\"],\"sqlx-mysql\":[\"sqlx\",\"sqlx/mysql\"],\"sqlx-postgres\":[\"sqlx\",\"sqlx/postgres\"],\"sqlx-sqlite\":[\"sqlx\",\"sqlx/sqlite\"],\"std\":[]}}",
"compiletest_rs_0.11.2": "{\"dependencies\":[{\"name\":\"diff\",\"req\":\"^0.1.10\"},{\"name\":\"filetime\",\"req\":\"^0.2\"},{\"name\":\"getopts\",\"req\":\"^0.2\"},{\"name\":\"lazy_static\",\"req\":\"^1.4\"},{\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(unix)\"},{\"name\":\"log\",\"req\":\"^0.4\"},{\"name\":\"miow\",\"req\":\"^0.6\",\"target\":\"cfg(windows)\"},{\"name\":\"regex\",\"req\":\"^1.0\"},{\"name\":\"rustfix\",\"req\":\"^0.8\"},{\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_derive\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"tempfile\",\"optional\":true,\"req\":\"^3.0\"},{\"name\":\"tester\",\"req\":\"^0.9\"},{\"features\":[\"Win32\"],\"name\":\"windows-sys\",\"req\":\"^0.59\",\"target\":\"cfg(windows)\"}],\"features\":{\"rustc\":[],\"stable\":[],\"tmp\":[\"tempfile\"]}}",
"concurrent-queue_2.5.0": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"cargo_bench_support\"],\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"default_features\":false,\"name\":\"crossbeam-utils\",\"req\":\"^0.8.11\"},{\"kind\":\"dev\",\"name\":\"easy-parallel\",\"req\":\"^3.1.0\"},{\"kind\":\"dev\",\"name\":\"fastrand\",\"req\":\"^2.0.0\"},{\"name\":\"loom\",\"optional\":true,\"req\":\"^0.7\",\"target\":\"cfg(loom)\"},{\"default_features\":false,\"name\":\"portable-atomic\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3\",\"target\":\"cfg(target_family = \\\"wasm\\\")\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}",
"condtype_1.3.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"cfg-if\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.141\"}],\"features\":{}}",
"console_0.15.11": "{\"dependencies\":[{\"name\":\"encode_unicode\",\"req\":\"^1\",\"target\":\"cfg(windows)\"},{\"name\":\"libc\",\"req\":\"^0.2.99\"},{\"name\":\"once_cell\",\"req\":\"^1.8\"},{\"default_features\":false,\"features\":[\"std\",\"bit-set\",\"break-dead-code\"],\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.4.2\"},{\"name\":\"unicode-width\",\"optional\":true,\"req\":\"^0.2\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_Console\",\"Win32_Storage_FileSystem\",\"Win32_UI_Input_KeyboardAndMouse\"],\"name\":\"windows-sys\",\"req\":\"^0.59\",\"target\":\"cfg(windows)\"}],\"features\":{\"ansi-parsing\":[],\"default\":[\"unicode-width\",\"ansi-parsing\"],\"windows-console-colors\":[\"ansi-parsing\"]}}",
"const-hex_1.17.0": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"name\":\"cpufeatures\",\"req\":\"^0.2\",\"target\":\"cfg(any(target_arch = \\\"x86\\\", target_arch = \\\"x86_64\\\"))\"},{\"kind\":\"dev\",\"name\":\"divan\",\"package\":\"codspeed-divan-compat\",\"req\":\"^3\"},{\"default_features\":false,\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"faster-hex\",\"req\":\"^0.10.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"hex\",\"req\":\"~0.4.2\"},{\"default_features\":false,\"name\":\"proptest\",\"optional\":true,\"req\":\"^1.4\"},{\"kind\":\"dev\",\"name\":\"rustc-hex\",\"req\":\"^2.1\"},{\"default_features\":false,\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"}],\"features\":{\"__fuzzing\":[\"dep:proptest\",\"std\"],\"alloc\":[\"serde_core?/alloc\",\"proptest?/alloc\"],\"core-error\":[],\"default\":[\"std\"],\"force-generic\":[],\"hex\":[],\"nightly\":[],\"portable-simd\":[],\"serde\":[\"dep:serde_core\"],\"std\":[\"serde_core?/std\",\"proptest?/std\",\"alloc\"]}}",
"const-oid_0.9.6": "{\"dependencies\":[{\"features\":[\"derive\"],\"name\":\"arbitrary\",\"optional\":true,\"req\":\"^1.2\"},{\"kind\":\"dev\",\"name\":\"hex-literal\",\"req\":\"^0.3\"}],\"features\":{\"db\":[],\"std\":[]}}",
@@ -859,6 +860,8 @@
"dispatch2_0.3.0": "{\"dependencies\":[{\"default_features\":false,\"features\":[\"std\"],\"name\":\"bitflags\",\"req\":\"^2.5.0\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"block2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"default_features\":false,\"name\":\"libc\",\"optional\":true,\"req\":\"^0.2.80\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"objc2\",\"optional\":true,\"req\":\">=0.6.1, <0.8.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"}],\"features\":{\"alloc\":[],\"block2\":[\"dep:block2\"],\"default\":[\"std\",\"block2\",\"libc\",\"objc2\"],\"libc\":[\"dep:libc\"],\"objc2\":[\"dep:objc2\"],\"std\":[\"alloc\"]}}",
"display_container_0.9.0": "{\"dependencies\":[{\"name\":\"either\",\"req\":\"^1.8\"},{\"name\":\"indenter\",\"req\":\"^0.3.3\"}],\"features\":{}}",
"displaydoc_0.2.5": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^0.6.1\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1\"},{\"name\":\"syn\",\"req\":\"^2.0\"},{\"kind\":\"dev\",\"name\":\"thiserror\",\"req\":\"^1.0.24\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}",
"divan-macros_0.1.21": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1\"},{\"default_features\":false,\"name\":\"quote\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"full\",\"clone-impls\",\"parsing\",\"printing\",\"proc-macro\"],\"name\":\"syn\",\"req\":\"^2.0.18\"}],\"features\":{}}",
"divan_0.1.21": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1\"},{\"default_features\":false,\"features\":[\"std\",\"env\"],\"name\":\"clap\",\"req\":\"^4\"},{\"name\":\"condtype\",\"req\":\"^1.3\"},{\"name\":\"divan-macros\",\"req\":\"=0.1.21\"},{\"name\":\"libc\",\"req\":\"^0.2.148\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"mimalloc\",\"req\":\"^0.1\"},{\"default_features\":false,\"features\":[\"std\",\"string\"],\"name\":\"regex\",\"package\":\"regex-lite\",\"req\":\"^0.1\"}],\"features\":{\"default\":[\"wrap_help\"],\"dyn_thread_local\":[],\"help\":[\"clap/help\"],\"internal_benches\":[],\"wrap_help\":[\"help\",\"clap/wrap_help\"]}}",
"dns-lookup_3.0.1": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(unix)\"},{\"name\":\"socket2\",\"req\":\"^0.6.0\"},{\"features\":[\"Win32_Networking_WinSock\",\"Win32_Foundation\"],\"name\":\"windows-sys\",\"req\":\"^0.60\",\"target\":\"cfg(windows)\"}],\"features\":{}}",
"document-features_0.2.12": "{\"dependencies\":[{\"name\":\"litrs\",\"req\":\"^1.0.0\"}],\"features\":{\"default\":[],\"self-test\":[]}}",
"dotenvy_0.15.7": "{\"dependencies\":[{\"name\":\"clap\",\"optional\":true,\"req\":\"^3.2\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1.16.0\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.3.0\"}],\"features\":{\"cli\":[\"clap\"]}}",
@@ -1150,6 +1153,7 @@
"jni_0.21.1": "{\"dependencies\":[{\"name\":\"cesu8\",\"req\":\"^1.1.0\"},{\"name\":\"cfg-if\",\"req\":\"^1.0.0\"},{\"name\":\"combine\",\"req\":\"^4.1.0\"},{\"name\":\"java-locator\",\"optional\":true,\"req\":\"^0.1\"},{\"name\":\"jni-sys\",\"req\":\"^0.3.0\"},{\"name\":\"libloading\",\"optional\":true,\"req\":\"^0.7\"},{\"name\":\"log\",\"req\":\"^0.4.4\"},{\"name\":\"thiserror\",\"req\":\"^1.0.20\"},{\"kind\":\"dev\",\"name\":\"assert_matches\",\"req\":\"^1.5.0\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"rusty-fork\",\"req\":\"^0.3.0\"},{\"kind\":\"build\",\"name\":\"walkdir\",\"req\":\"^2\"},{\"features\":[\"Win32_Globalization\"],\"name\":\"windows-sys\",\"req\":\"^0.45.0\",\"target\":\"cfg(windows)\"},{\"kind\":\"dev\",\"name\":\"bytemuck\",\"req\":\"^1.13.0\",\"target\":\"cfg(windows)\"}],\"features\":{\"default\":[],\"invocation\":[\"java-locator\",\"libloading\"]}}",
"jobserver_0.1.34": "{\"dependencies\":[{\"features\":[\"std\"],\"name\":\"getrandom\",\"req\":\"^0.3.2\",\"target\":\"cfg(windows)\"},{\"name\":\"libc\",\"req\":\"^0.2.171\",\"target\":\"cfg(unix)\"},{\"features\":[\"fs\"],\"kind\":\"dev\",\"name\":\"nix\",\"req\":\"^0.28.0\",\"target\":\"cfg(unix)\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.10.1\"}],\"features\":{}}",
"js-sys_0.3.85": "{\"dependencies\":[{\"default_features\":false,\"name\":\"once_cell\",\"req\":\"^1.12\"},{\"default_features\":false,\"name\":\"wasm-bindgen\",\"req\":\"=0.2.108\"}],\"features\":{\"default\":[\"std\"],\"std\":[\"wasm-bindgen/std\"]}}",
"jsonptr_0.7.1": "{\"dependencies\":[{\"features\":[\"fancy\"],\"name\":\"miette\",\"optional\":true,\"req\":\"^7.4.0\"},{\"kind\":\"dev\",\"name\":\"quickcheck\",\"req\":\"^1.0.3\"},{\"kind\":\"dev\",\"name\":\"quickcheck_macros\",\"req\":\"^1.0.0\"},{\"features\":[\"alloc\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.203\"},{\"features\":[\"alloc\"],\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0.119\"},{\"name\":\"syn\",\"optional\":true,\"req\":\"^1.0.109\",\"target\":\"cfg(any())\"},{\"name\":\"toml\",\"optional\":true,\"req\":\"^0.8\"}],\"features\":{\"assign\":[],\"default\":[\"std\",\"serde\",\"json\",\"resolve\",\"assign\",\"delete\"],\"delete\":[\"resolve\"],\"json\":[\"dep:serde_json\",\"serde\"],\"miette\":[\"dep:miette\",\"std\"],\"resolve\":[],\"std\":[\"serde/std\",\"serde_json?/std\"],\"toml\":[\"dep:toml\",\"serde\",\"std\"]}}",
"jsonwebtoken_9.3.1": "{\"dependencies\":[{\"name\":\"base64\",\"req\":\"^0.22\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.4\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.4\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\")))))\"},{\"name\":\"js-sys\",\"req\":\"^0.3\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"name\":\"pem\",\"optional\":true,\"req\":\"^3\"},{\"features\":[\"std\"],\"name\":\"ring\",\"req\":\"^0.17.4\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"features\":[\"std\",\"wasm32_unknown_unknown_js\"],\"name\":\"ring\",\"req\":\"^0.17.4\",\"target\":\"cfg(target_arch = \\\"wasm32\\\")\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"req\":\"^1.0\"},{\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"name\":\"simple_asn1\",\"optional\":true,\"req\":\"^0.6\"},{\"features\":[\"wasm-bindgen\"],\"kind\":\"dev\",\"name\":\"time\",\"req\":\"^0.3\",\"target\":\"cfg(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\"))))\"},{\"kind\":\"dev\",\"name\":\"time\",\"req\":\"^0.3\",\"target\":\"cfg(not(all(target_arch = \\\"wasm32\\\", not(any(target_os = \\\"emscripten\\\", target_os = \\\"wasi\\\")))))\"},{\"kind\":\"dev\",\"name\":\"wasm-bindgen-test\",\"req\":\"^0.3.1\"}],\"features\":{\"default\":[\"use_pem\"],\"use_pem\":[\"pem\",\"simple_asn1\"]}}",
"keyring_3.6.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"base64\",\"req\":\"^0.22\"},{\"name\":\"byteorder\",\"optional\":true,\"req\":\"^1.2\",\"target\":\"cfg(target_os = \\\"windows\\\")\"},{\"features\":[\"derive\",\"wrap_help\"],\"kind\":\"dev\",\"name\":\"clap\",\"req\":\"^4\"},{\"name\":\"dbus-secret-service\",\"optional\":true,\"req\":\"^4.0.0-rc.1\",\"target\":\"cfg(target_os = \\\"openbsd\\\")\"},{\"name\":\"dbus-secret-service\",\"optional\":true,\"req\":\"^4.0.0-rc.2\",\"target\":\"cfg(target_os = \\\"linux\\\")\"},{\"name\":\"dbus-secret-service\",\"optional\":true,\"req\":\"^4.0.1\",\"target\":\"cfg(target_os = \\\"freebsd\\\")\"},{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3\"},{\"kind\":\"dev\",\"name\":\"env_logger\",\"req\":\"^0.11.5\"},{\"kind\":\"dev\",\"name\":\"fastrand\",\"req\":\"^2\"},{\"features\":[\"std\"],\"name\":\"linux-keyutils\",\"optional\":true,\"req\":\"^0.2\",\"target\":\"cfg(target_os = \\\"linux\\\")\"},{\"name\":\"log\",\"req\":\"^0.4.22\"},{\"name\":\"openssl\",\"optional\":true,\"req\":\"^0.10.66\"},{\"kind\":\"dev\",\"name\":\"rpassword\",\"req\":\"^7\"},{\"kind\":\"dev\",\"name\":\"rprompt\",\"req\":\"^2\"},{\"name\":\"secret-service\",\"optional\":true,\"req\":\"^4\",\"target\":\"cfg(target_os = \\\"freebsd\\\")\"},{\"name\":\"secret-service\",\"optional\":true,\"req\":\"^4\",\"target\":\"cfg(target_os = \\\"linux\\\")\"},{\"name\":\"secret-service\",\"optional\":true,\"req\":\"^4\",\"target\":\"cfg(target_os = \\\"openbsd\\\")\"},{\"name\":\"security-framework\",\"optional\":true,\"req\":\"^2\",\"target\":\"cfg(target_os = \\\"ios\\\")\"},{\"name\":\"security-framework\",\"optional\":true,\"req\":\"^3\",\"target\":\"cfg(target_os = \\\"macos\\\")\"},{\"kind\":\"dev\",\"name\":\"whoami\",\"req\":\"^1.5\"},{\"features\":[\"Win32_Foundation\",\"Win32_Security_Credentials\"],\"name\":\"windows-sys\",\"optional\":true,\"req\":\"^0.60\",\"target\":\"cfg(target_os = \\\"windows\\\")\"},{\"name\":\"zbus\",\"optional\":true,\"req\":\"^4\",\"target\":\"cfg(target_os = \\\"freebsd\\\")\"},{\"name\":\"zbus\",\"optional\":true,\"req\":\"^4\",\"target\":\"cfg(target_os = \\\"linux\\\")\"},{\"name\":\"zbus\",\"optional\":true,\"req\":\"^4\",\"target\":\"cfg(target_os = \\\"openbsd\\\")\"},{\"name\":\"zeroize\",\"req\":\"^1.8.1\",\"target\":\"cfg(target_os = \\\"windows\\\")\"}],\"features\":{\"apple-native\":[\"dep:security-framework\"],\"async-io\":[\"zbus?/async-io\"],\"async-secret-service\":[\"dep:secret-service\",\"dep:zbus\"],\"crypto-openssl\":[\"dbus-secret-service?/crypto-openssl\",\"secret-service?/crypto-openssl\"],\"crypto-rust\":[\"dbus-secret-service?/crypto-rust\",\"secret-service?/crypto-rust\"],\"linux-native\":[\"dep:linux-keyutils\"],\"linux-native-async-persistent\":[\"linux-native\",\"async-secret-service\"],\"linux-native-sync-persistent\":[\"linux-native\",\"sync-secret-service\"],\"sync-secret-service\":[\"dep:dbus-secret-service\"],\"tokio\":[\"zbus?/tokio\"],\"vendored\":[\"dbus-secret-service?/vendored\",\"openssl?/vendored\"],\"windows-native\":[\"dep:windows-sys\",\"dep:byteorder\"]}}",
"kqueue-sys_1.0.4": "{\"dependencies\":[{\"name\":\"bitflags\",\"req\":\"^1.2.1\"},{\"name\":\"libc\",\"req\":\"^0.2.74\"}],\"features\":{}}",
@@ -1799,7 +1803,6 @@
"winnow_0.7.15": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"annotate-snippets\",\"req\":\"^0.11.4\"},{\"name\":\"anstream\",\"optional\":true,\"req\":\"^0.6.15\"},{\"name\":\"anstyle\",\"optional\":true,\"req\":\"^1.0.8\"},{\"kind\":\"dev\",\"name\":\"anyhow\",\"req\":\"^1.0.100\"},{\"kind\":\"dev\",\"name\":\"automod\",\"req\":\"^1.0.15\"},{\"kind\":\"dev\",\"name\":\"circular\",\"req\":\"^0.3.0\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.1\"},{\"name\":\"is_terminal_polyfill\",\"optional\":true,\"req\":\"^1.48.1\"},{\"kind\":\"dev\",\"name\":\"lexopt\",\"req\":\"^0.3.1\"},{\"default_features\":false,\"name\":\"memchr\",\"optional\":true,\"req\":\"^2.7\"},{\"kind\":\"dev\",\"name\":\"proptest\",\"req\":\"^1.6.0\"},{\"kind\":\"dev\",\"name\":\"rustc-hash\",\"req\":\"^2.1.1\"},{\"features\":[\"examples\"],\"kind\":\"dev\",\"name\":\"snapbox\",\"req\":\"^0.6.21\"},{\"kind\":\"dev\",\"name\":\"term-transcript\",\"req\":\"^0.2.0\"},{\"name\":\"terminal_size\",\"optional\":true,\"req\":\"^0.4.3\"}],\"features\":{\"alloc\":[],\"debug\":[\"std\",\"dep:anstream\",\"dep:anstyle\",\"dep:is_terminal_polyfill\",\"dep:terminal_size\"],\"default\":[\"std\"],\"simd\":[\"dep:memchr\"],\"std\":[\"alloc\",\"memchr?/std\"],\"unstable-doc\":[\"alloc\",\"std\",\"simd\",\"unstable-recover\"],\"unstable-recover\":[]}}",
"winreg_0.10.1": "{\"dependencies\":[{\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4.6\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.3\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"~3.0\"},{\"features\":[\"impl-default\",\"impl-debug\",\"minwindef\",\"minwinbase\",\"timezoneapi\",\"winerror\",\"winnt\",\"winreg\",\"handleapi\"],\"name\":\"winapi\",\"req\":\"^0.3.9\"}],\"features\":{\"serialization-serde\":[\"transactions\",\"serde\"],\"transactions\":[\"winapi/ktmw32\"]}}",
"winreg_0.50.0": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4.6\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.3\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"serde_bytes\",\"req\":\"^0.11\"},{\"kind\":\"dev\",\"name\":\"serde_derive\",\"req\":\"^1\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"~3.0\"},{\"features\":[\"Win32_Foundation\",\"Win32_System_Time\",\"Win32_System_Registry\",\"Win32_Security\",\"Win32_Storage_FileSystem\",\"Win32_System_Diagnostics_Debug\"],\"name\":\"windows-sys\",\"req\":\"^0.48.0\"}],\"features\":{\"serialization-serde\":[\"transactions\",\"serde\"],\"transactions\":[]}}",
"winres_0.1.12": "{\"dependencies\":[{\"name\":\"toml\",\"req\":\"^0.5\"},{\"features\":[\"winnt\"],\"kind\":\"dev\",\"name\":\"winapi\",\"req\":\"^0.3\"}],\"features\":{}}",
"winsafe_0.0.19": "{\"dependencies\":[],\"features\":{\"comctl\":[\"ole\"],\"dshow\":[\"oleaut\"],\"dwm\":[\"uxtheme\"],\"dxgi\":[\"ole\"],\"gdi\":[\"user\"],\"gui\":[\"comctl\",\"shell\",\"uxtheme\"],\"kernel\":[],\"mf\":[\"oleaut\"],\"ole\":[\"user\"],\"oleaut\":[\"ole\"],\"shell\":[\"oleaut\"],\"taskschd\":[\"oleaut\"],\"user\":[\"kernel\"],\"uxtheme\":[\"gdi\",\"ole\"],\"version\":[\"kernel\"]}}",
"winsplit_0.1.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"doc-comment\",\"req\":\"^0.3.3\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}",
"wiremock_0.6.5": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"actix-rt\",\"req\":\"^2.10.0\"},{\"name\":\"assert-json-diff\",\"req\":\"^2.0.2\"},{\"features\":[\"attributes\",\"tokio1\"],\"kind\":\"dev\",\"name\":\"async-std\",\"req\":\"^1.13.2\"},{\"name\":\"base64\",\"req\":\"^0.22\"},{\"name\":\"deadpool\",\"req\":\"^0.12.2\"},{\"name\":\"futures\",\"req\":\"^0.3.31\"},{\"name\":\"http\",\"req\":\"^1.3\"},{\"name\":\"http-body-util\",\"req\":\"^0.1\"},{\"features\":[\"full\"],\"name\":\"hyper\",\"req\":\"^1.7\"},{\"features\":[\"tokio\",\"server\",\"http1\",\"http2\"],\"name\":\"hyper-util\",\"req\":\"^0.1\"},{\"name\":\"log\",\"req\":\"^0.4\"},{\"name\":\"once_cell\",\"req\":\"^1\"},{\"name\":\"regex\",\"req\":\"^1\"},{\"features\":[\"json\"],\"kind\":\"dev\",\"name\":\"reqwest\",\"req\":\"^0.12.23\"},{\"name\":\"serde\",\"req\":\"^1\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1\"},{\"name\":\"serde_json\",\"req\":\"^1\"},{\"features\":[\"rt\",\"macros\",\"net\"],\"name\":\"tokio\",\"req\":\"^1.47.1\"},{\"features\":[\"macros\",\"rt-multi-thread\"],\"kind\":\"dev\",\"name\":\"tokio\",\"req\":\"^1.47.1\"},{\"name\":\"url\",\"req\":\"^2.5\"}],\"features\":{}}",
@@ -1864,112 +1867,112 @@
"zvariant_utils_2.1.0": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"req\":\"^1.0.81\"},{\"name\":\"quote\",\"req\":\"^1.0.36\"},{\"features\":[\"extra-traits\",\"full\"],\"name\":\"syn\",\"req\":\"^2.0.64\"}],\"features\":{}}"
},
"@@rules_rs+//rs/experimental/toolchains:module_extension.bzl%toolchains": {
"cargo-1.93.0-aarch64-apple-darwin.tar.xz": "6443909350322ad07f09bb5edfd9ff29268e6fe88c7d78bfba7a5e254248dc25",
"cargo-1.93.0-aarch64-pc-windows-gnullvm.tar.xz": "387832b989c8eb96c9ebd66402a87962167633bc6d91a49ffb8c7903c45f3476",
"cargo-1.93.0-aarch64-pc-windows-msvc.tar.xz": "155bff7a16aa7054e7ed7c3a82e362d4b302b3882d751b823e06ff63ae3f103d",
"cargo-1.93.0-aarch64-unknown-linux-gnu.tar.xz": "5998940b8b97286bb67facb1a85535eeb3d4d7a61e36a85e386e5c0c5cfe5266",
"cargo-1.93.0-x86_64-apple-darwin.tar.xz": "95a47c5ed797c35419908f04188d8b7de09946e71073c4b72632b16f5b10dfae",
"cargo-1.93.0-x86_64-pc-windows-gnullvm.tar.xz": "f19766837559f90476508140cb95cc708220012ec00a854fa9f99187b1f246b6",
"cargo-1.93.0-x86_64-pc-windows-msvc.tar.xz": "e59c5e2baa9ec17261f2cda6676ebf7b68b21a860e3f7451c4d964728951da75",
"cargo-1.93.0-x86_64-unknown-linux-gnu.tar.xz": "c23de3ae709ff33eed5e4ae59d1f9bcd75fa4dbaa9fb92f7b06bfb534b8db880",
"clippy-1.93.0-aarch64-apple-darwin.tar.xz": "0b6e943a8d12be0e68575acf59c9ea102daf795055fcbbf862b0bfd35ec40039",
"clippy-1.93.0-aarch64-pc-windows-gnullvm.tar.xz": "296949f49be2de77ce9d1c5023a5f0a58e28e329eec03642a3da0e175a67beeb",
"clippy-1.93.0-aarch64-pc-windows-msvc.tar.xz": "07bcf2edb88cdf5ead2f02e4a8493e9b0ef935a31253fac6f9f3378d8023f113",
"clippy-1.93.0-aarch64-unknown-linux-gnu.tar.xz": "872ae6d68d625946d281b91d928332e6b74f6ab269b6af842338df4338805a60",
"clippy-1.93.0-x86_64-apple-darwin.tar.xz": "e6d0b1afb9607c14a1172d09ee194a032bbb3e48af913d55c5a473e0559eddde",
"clippy-1.93.0-x86_64-pc-windows-gnullvm.tar.xz": "b6f1f7264ed6943c59dedfb9531fbadcc3c0fcf273c940a63d58898b14a1060f",
"clippy-1.93.0-x86_64-pc-windows-msvc.tar.xz": "25fb103390bf392980b4689ac09b2ec2ab4beefb7022a983215b613ad05eab57",
"clippy-1.93.0-x86_64-unknown-linux-gnu.tar.xz": "793108977514b15c0f45ade28ae35c58b05370cb0f22e89bd98fdfa61eabf55d",
"rust-analyzer-1.93.0-aarch64-apple-darwin.tar.xz": "8a09a46d45277678b2d112eef61736e03e78dfa6c506e187df176b904659e5a7",
"rust-analyzer-1.93.0-aarch64-pc-windows-gnullvm.tar.xz": "1fb48fadbaea6da36cfcd6b72122eeaa6ab025c9c82441777cf913108f73c115",
"rust-analyzer-1.93.0-aarch64-pc-windows-msvc.tar.xz": "558100ab62fe22d552be43c1bf166fef057ff2f3eb0d786cf5fe509a08701f37",
"rust-analyzer-1.93.0-aarch64-unknown-linux-gnu.tar.xz": "b2e7890cf5953f572eb05d7328c086fa60d4f16e5d301d35c52b3f58b88a8316",
"rust-analyzer-1.93.0-x86_64-apple-darwin.tar.xz": "3cb7975d4fd17840e2980c9d864755f801ee9e7594de886a5816c73b07594508",
"rust-analyzer-1.93.0-x86_64-pc-windows-gnullvm.tar.xz": "4471db6393b7380624899482aae99612fd1cf509ae36d0c68292077c2104127d",
"rust-analyzer-1.93.0-x86_64-pc-windows-msvc.tar.xz": "f4c1f8f120f48974cb1e0cea01a955ce52cdfa0f1db0355c5fb5a2deeca8188a",
"rust-analyzer-1.93.0-x86_64-unknown-linux-gnu.tar.xz": "33fcd377be3b5ffdd95977c3d0219f63725c18b6c8b53fb5be0418962a84738c",
"rust-src-1.93.0.tar.xz": "0e7b9acd5debfeffef3741dc8f6edf137d70426a0027d6b190cc8cfb0a1ac23c",
"rust-std-1.93.0-aarch64-apple-darwin.tar.xz": "8603c63715349636ed85b4fe716c4e827a727918c840e54aff5b243cedadf19b",
"rust-std-1.93.0-aarch64-apple-ios-macabi.tar.xz": "24d47e615ce101869ff452a572a6b77ed7cf70f2454d0b50892ac849e8c7ac4d",
"rust-std-1.93.0-aarch64-apple-ios-sim.tar.xz": "d1d5e2d1b79206f2cc9fb7f6a2958cfe0f4bbc9147fda8dbc3608aa4be5e6816",
"rust-std-1.93.0-aarch64-apple-ios.tar.xz": "49228e70387773a71cf144509baf39979ab2cdb604340fff64b483ab41f61617",
"rust-std-1.93.0-aarch64-linux-android.tar.xz": "59c16648d9a29c07f63a1749cae6b73078f20fef1206c5e0f81c784ae8796cdb",
"rust-std-1.93.0-aarch64-pc-windows-gnullvm.tar.xz": "9a270d50eaaacc7cb1925565a8b65ff831686aa1592b7034bb9848d7f2a9738d",
"rust-std-1.93.0-aarch64-pc-windows-msvc.tar.xz": "f7bd3d25baf3643c8769b8c4d2e6cde45bb25042fac698e0daf19fc9f58f8568",
"rust-std-1.93.0-aarch64-unknown-fuchsia.tar.xz": "d1e46c443a9607603c810942e99a95a1acfb105d1312426b468ff68febaabf77",
"rust-std-1.93.0-aarch64-unknown-linux-gnu.tar.xz": "84e82ff52c39c64dfd0e1c2d58fd3d5309d1d2502378131544c0d486b44af20a",
"rust-std-1.93.0-aarch64-unknown-linux-musl.tar.xz": "bab885a87da586040064064bd1c314d707164d8dc0fefee39d59be7f15ce6f7d",
"rust-std-1.93.0-aarch64-unknown-none-softfloat.tar.xz": "0f6305daf658a7d6c0efd075859cb60432c13b82e7ecee0d097074e4e1873391",
"rust-std-1.93.0-aarch64-unknown-none.tar.xz": "3cf1aa3309a8903e89bb20537113155ca4e88844c8cc9c34c43865d5ce5a6192",
"rust-std-1.93.0-aarch64-unknown-uefi.tar.xz": "317b0af124e0e124bd76b8e5a2fb0c600279177d0bed9c841a3202df2d0f7f8e",
"rust-std-1.93.0-arm-linux-androideabi.tar.xz": "d010b26fc88e28a93cc94ea6ca5d2c90efed7f9846fae1e40db7c183b50575e2",
"rust-std-1.93.0-arm-unknown-linux-gnueabi.tar.xz": "deedc54ffce099781986eed4aec0843866f1bf72904ab0f5cdb115b9c7af540e",
"rust-std-1.93.0-arm-unknown-linux-gnueabihf.tar.xz": "89e44e042bc1241b3999191c385fec8433d60a5a9fc373583cd3b2d9408d5074",
"rust-std-1.93.0-arm-unknown-linux-musleabi.tar.xz": "641a17acb5104637d4dc9c4be022a7927ae721eb08759fea96ecfaf5c60be4dc",
"rust-std-1.93.0-arm-unknown-linux-musleabihf.tar.xz": "94a92b454bf3b0aab046b257f555ccb08f16dc2dc281bea6a4ef17ea8f58cbdc",
"rust-std-1.93.0-armv7-linux-androideabi.tar.xz": "e295f26bb219a7a4ebb5c2e07fedfebb075be6830aaf910c742a57cd21018b6d",
"rust-std-1.93.0-armv7-unknown-linux-gnueabi.tar.xz": "8a7bd5227c78294864095edb07837ff32ff6c07cd1a4a418f9bcc3ebd7e79874",
"rust-std-1.93.0-armv7-unknown-linux-gnueabihf.tar.xz": "f015f9b2d588454a9dc62942ab2e800d82c602e4eab6f89f8213419491bcd203",
"rust-std-1.93.0-armv7-unknown-linux-musleabi.tar.xz": "d15d24c9fb7c15243e1341cea53590002df271060118914bd0efcda8ccbd0731",
"rust-std-1.93.0-armv7-unknown-linux-musleabihf.tar.xz": "a2e5ec22ed35fb51a503d1e10b37447b0fa7333f079585bc0b6a2eb599de43f3",
"rust-std-1.93.0-i686-linux-android.tar.xz": "68fd86f62dd63221717d1741210f0f5cf75da7a1e32eed5a46b1e67c9d9430e1",
"rust-std-1.93.0-i686-pc-windows-gnu.tar.xz": "cb613d5d1eb245e8a1f4c0b25f93c2997cd06c1cc3fc202155f2997aebf44d4d",
"rust-std-1.93.0-i686-pc-windows-gnullvm.tar.xz": "0f713dc252a6de706519fe6cdaab6d66aaf1b555133b536cc0ab28061aa4269c",
"rust-std-1.93.0-i686-pc-windows-msvc.tar.xz": "33dc1951e2dc21bd05361160d52f496eecf48e0b95df5083172698b1cd5b9a3f",
"rust-std-1.93.0-i686-unknown-freebsd.tar.xz": "67718aae1381879fdcca5699051eb87e0cda3d2fd0fe75e306ba0948b79df7db",
"rust-std-1.93.0-i686-unknown-linux-gnu.tar.xz": "b8b7020a61418b95c1ea26badaf8db6979778e28dbadddc81fb5010fe27c935b",
"rust-std-1.93.0-i686-unknown-linux-musl.tar.xz": "867e54b3e89dc0b6d2b7a538a80443add6c3990bb4bd2260dea2ed98a0dc9870",
"rust-std-1.93.0-i686-unknown-uefi.tar.xz": "929fd484b08d5b2077ff864f5f2d24b51a78f1b6e837b9eab9d7e8fb7f31adce",
"rust-std-1.93.0-powerpc-unknown-linux-gnu.tar.xz": "e851c0fa3e726ce3f7139c5803198a1aa9723594394734ac9e373c93d92e5ea3",
"rust-std-1.93.0-powerpc64-unknown-linux-gnu.tar.xz": "f729bb7d95705e12a92eb072e494b93d8822ca40aa4802ca780b0cf33b56d401",
"rust-std-1.93.0-powerpc64le-unknown-linux-gnu.tar.xz": "d209ac698a69ca9b9035adb97a0ed8e60a08db52960198c3e03b9ee714c1a46b",
"rust-std-1.93.0-powerpc64le-unknown-linux-musl.tar.xz": "34b98d5eca2fdbd6ba41b0faf14160ef1ebd038f6ecaa264d318ad33263e1cf1",
"rust-std-1.93.0-riscv32imc-unknown-none-elf.tar.xz": "71af84c81241cbc7811b267927990be025f30d7d3dc55df4b56da7ac250f7c78",
"rust-std-1.93.0-riscv64gc-unknown-linux-gnu.tar.xz": "b769fb6c9f3e0419a6bd0b7b79f9191bbd7a48a9f243b23eb7d135711aa6de1b",
"rust-std-1.93.0-riscv64gc-unknown-linux-musl.tar.xz": "a7ced602573d814d875d69022e026c1ccb520b4b2de9d430ddfd0966ec6c9643",
"rust-std-1.93.0-riscv64gc-unknown-none-elf.tar.xz": "842f72913f288a0c76601438e67ccd88c816dbf187587928e48bf8b9ce74cbf3",
"rust-std-1.93.0-s390x-unknown-linux-gnu.tar.xz": "41a65db45a288eb3eedb187b366f132d5b3615767de7ce994b123b342ac7a848",
"rust-std-1.93.0-thumbv6m-none-eabi.tar.xz": "be3f8aad5680dabb203300847dcbbabc15729170ba5c3a9c499efae4df410a9e",
"rust-std-1.93.0-thumbv7em-none-eabi.tar.xz": "8f93eefca39c0da417feddab64775f862c72bbe80da11914dcf47babef8f1644",
"rust-std-1.93.0-thumbv7em-none-eabihf.tar.xz": "a3b6914b966ac93dbe7531016d5f570b189445603c43614a60e0b9ea12674bd3",
"rust-std-1.93.0-thumbv7m-none-eabi.tar.xz": "cfa6227214f3ae58c06b36547c5bd6f0f6787764afa48cfa4ff3488264deab6c",
"rust-std-1.93.0-thumbv8m.main-none-eabi.tar.xz": "5f24df0aa8322561125575e365be7ad13a5bb26cf73c7fc9a3f4bcfa58e0febc",
"rust-std-1.93.0-thumbv8m.main-none-eabihf.tar.xz": "906b07580be2df277cced2b56bc03cb565b758c382bf3e82cbd8375b459815dd",
"rust-std-1.93.0-wasm32-unknown-emscripten.tar.xz": "63cdbb1ea7f353060539c00f7346f4f5fb0d6f09899cacddc1f172ef07c4af8b",
"rust-std-1.93.0-wasm32-unknown-unknown.tar.xz": "3100cb920ddac646943243f0eddd331128836b9161dd5f7b0a6c76375d39cc5e",
"rust-std-1.93.0-wasm32-wasip1-threads.tar.xz": "439c65dea31e855f0258632b6d19435ba8a80561297fa6dc6be48048c5cd1871",
"rust-std-1.93.0-wasm32-wasip1.tar.xz": "075de970ef865678dad258f1566d7cfe76a594698e9bf93dd69fa5cfdfcf1a6f",
"rust-std-1.93.0-wasm32-wasip2.tar.xz": "0ef01bb552036ab44456f5505015b13c88d3694629804d7af46452c8b0a48f8c",
"rust-std-1.93.0-x86_64-apple-darwin.tar.xz": "f112d41c8a31794f0f561d37fe77010ed0b405fa70284a2910891869d8c52418",
"rust-std-1.93.0-x86_64-apple-ios-macabi.tar.xz": "a543dd545747d372d973ace8b485a13603ce96c110c7ae734d605e45f6e162c5",
"rust-std-1.93.0-x86_64-apple-ios.tar.xz": "e151013b9bc5990e178285a33e62bae7700d8c48c06e97771abb1643aa907d75",
"rust-std-1.93.0-x86_64-linux-android.tar.xz": "dc05ca79d9fecc5ce3643adb9c6f89fd35c8e1d7146bf9b62e30bad41f9fb6a7",
"rust-std-1.93.0-x86_64-pc-windows-gnu.tar.xz": "a07c6ab596fad15ca7acd63ee7f2a5fea93fd421179252067e309c2aa0b2021b",
"rust-std-1.93.0-x86_64-pc-windows-gnullvm.tar.xz": "ef6cf0977bc5aa4bbd594afb9df4ba76fdd4f0fc5685cddbefff49ceed202a91",
"rust-std-1.93.0-x86_64-pc-windows-msvc.tar.xz": "2593e29af0b8def34ceb1185b8e85bd93a9e0d3b0c108d704c1b31370c50a48c",
"rust-std-1.93.0-x86_64-unknown-freebsd.tar.xz": "51b2feaff7c2d28633504ed92ab442a55d112e6a2bf09c91188f00dbaf03378a",
"rust-std-1.93.0-x86_64-unknown-fuchsia.tar.xz": "41f0f3eb96cedfc13ab5fd4f15065063f262d035c1f71d96c42587acdacdbabe",
"rust-std-1.93.0-x86_64-unknown-linux-gnu.tar.xz": "a849a418d0f27e69573e41763c395e924a0b98c16fcdc55599c1c79c27c1c777",
"rust-std-1.93.0-x86_64-unknown-linux-musl.tar.xz": "874658d2ced1ed2b9bf66c148b78a2e10cad475d0a4db32e68a08900905b89b8",
"rust-std-1.93.0-x86_64-unknown-netbsd.tar.xz": "aad63193af89772031f9a85f193afc0b15f8e6d4a9a4983c5f5d3802f69a89e8",
"rust-std-1.93.0-x86_64-unknown-none.tar.xz": "01dcca7ae4b7e82fbfa399adb5e160afaa13143e5a17e1e0737c38cf07365fb3",
"rust-std-1.93.0-x86_64-unknown-uefi.tar.xz": "ec4e439d9485ce752b56999e8e41ed82373fc833a005cf2531c6f7ef7e785392",
"rustc-1.93.0-aarch64-apple-darwin.tar.xz": "092be03c02b44c405dab1232541c84f32b2d9e8295747568c3d531dd137221dc",
"rustc-1.93.0-aarch64-pc-windows-gnullvm.tar.xz": "d3bc0cdaf157e20b1f23e510b5e3c4c6e9117d08f5284c04dee60aecff1bc851",
"rustc-1.93.0-aarch64-pc-windows-msvc.tar.xz": "a3ac1a8e411de8470f71b366f89d187718c431526912b181692ed0a18c56c7ad",
"rustc-1.93.0-aarch64-unknown-linux-gnu.tar.xz": "1a9045695892ec08d8e9751bf7cf7db71fe27a6202dd12ce13aca48d0602dbde",
"rustc-1.93.0-x86_64-apple-darwin.tar.xz": "594bb293f0a4f444656cf8dec2149fcb979c606260efee9e09bcf8c9c6ed6ae7",
"rustc-1.93.0-x86_64-pc-windows-gnullvm.tar.xz": "0cdaa8de66f5ce21d1ea73917efc5c64f408bda49f678ddde19465ced9d5ec63",
"rustc-1.93.0-x86_64-pc-windows-msvc.tar.xz": "fa17677eee0d83eb055b309953184bf87ba634923d8897f860cda65d55c6e350",
"rustc-1.93.0-x86_64-unknown-linux-gnu.tar.xz": "00c6e6740ea6a795e33568cd7514855d58408a1180cd820284a7bbf7c46af715",
"rustfmt-1.93.0-aarch64-apple-darwin.tar.xz": "0dd1faedf0768ef362f4aae4424b34e8266f2b9cf5e76ea4fcaf780220b363a0",
"rustfmt-1.93.0-aarch64-pc-windows-gnullvm.tar.xz": "5888827e7fbd7d59930870b4856fce8d6d8fca5e02f6535f8ae3d7ad0ccf2d4a",
"rustfmt-1.93.0-aarch64-pc-windows-msvc.tar.xz": "24eed108489567133bbfe40c8eacda1567be55fae4c526911b39eb33eb27a6cb",
"rustfmt-1.93.0-aarch64-unknown-linux-gnu.tar.xz": "92e1acb45ae642136258b4dabb39302af2d53c83e56ebd5858bc969f9e5c141a",
"rustfmt-1.93.0-x86_64-apple-darwin.tar.xz": "c8453b4c5758eb39423042ffa9c23ed6128cbed2b15b581e5e1192c9cc0b1d4e",
"rustfmt-1.93.0-x86_64-pc-windows-gnullvm.tar.xz": "47167e9e78db9be4503a060dee02f4df2cda252da32175dbf44331f965a747b9",
"rustfmt-1.93.0-x86_64-pc-windows-msvc.tar.xz": "5becc7c2dba4b9ab5199012cad30829235a7f7fb5d85a238697e8f0e44cbd9af",
"rustfmt-1.93.0-x86_64-unknown-linux-gnu.tar.xz": "7f81f6c17d11a7fda5b4e1b111942fb3b23d30dcec767e13e340ebfb762a5e33"
"cargo-1.95.0-aarch64-apple-darwin.tar.xz": "6c2ffed8e1ac9cf4dc9e80f282a869a6b237a153e7c55cca039d33de29d80aaf",
"cargo-1.95.0-aarch64-pc-windows-gnullvm.tar.xz": "14683a8e0b0ee8afdd7e2896fc8e91a7ff0ba55c2e20912e639eea498f1e1d10",
"cargo-1.95.0-aarch64-pc-windows-msvc.tar.xz": "e645b30fa035a18aa12d28b699052014c7efa9dd4a33dabd223f0d16b5fa28e8",
"cargo-1.95.0-aarch64-unknown-linux-gnu.tar.xz": "7c070aeba9bbf12073646995a03f36c346bb5f541d0078ba6d9dc2a7adaaf6af",
"cargo-1.95.0-x86_64-apple-darwin.tar.xz": "e2e1131ade2dddc0d779e0ab3a6a990085c7a654951235742823c3a1ce0f190f",
"cargo-1.95.0-x86_64-pc-windows-gnullvm.tar.xz": "239c098b9878ad01ad5e0feeee377e6ee3311bc1534b1fb5eb630489d463bab2",
"cargo-1.95.0-x86_64-pc-windows-msvc.tar.xz": "cab2606cb2d0aa31c55d50512fe07a9f15e893227566fbeb448306760cd0d2bf",
"cargo-1.95.0-x86_64-unknown-linux-gnu.tar.xz": "e74edd2cf7d0f1f1383b4f00eb90c843750bc489e2ccf7214e6476678a907425",
"clippy-1.95.0-aarch64-apple-darwin.tar.xz": "fd183baa023d0c4e0c5b8184226e2d4c85126adf156cb1f3a726ec593bba8d62",
"clippy-1.95.0-aarch64-pc-windows-gnullvm.tar.xz": "b81d0fe05c4ec514aefaffdf0649b175a2f82572163202c17531358f196b6168",
"clippy-1.95.0-aarch64-pc-windows-msvc.tar.xz": "44c1b7ada72aa8f3fcaceb37a3899665bc9b160c2fea77879c8ecb65a9e97eba",
"clippy-1.95.0-aarch64-unknown-linux-gnu.tar.xz": "fb021e0c0fc2238be9266d7614f4a26bc372544c4cba3528d729ab24ad229fc9",
"clippy-1.95.0-x86_64-apple-darwin.tar.xz": "e47367f6b1489d74cbba93b387310adcb82e27a51e44b2c6ff543eb4f199fe32",
"clippy-1.95.0-x86_64-pc-windows-gnullvm.tar.xz": "9358dfc3b831a5f4b3a3a0016da734b4ef5e78c84ca8f148f56a9c126515ebfa",
"clippy-1.95.0-x86_64-pc-windows-msvc.tar.xz": "ddc151d6f58c6658b7380292ecaef36e62d063bbdbf7f5802669810575bb5b75",
"clippy-1.95.0-x86_64-unknown-linux-gnu.tar.xz": "ac779bc9839dd47180806b133e4e2563c4a34716284cd5b8fede8ef289f452ca",
"rust-analyzer-1.95.0-aarch64-apple-darwin.tar.xz": "11231fc6574301b94bd379af4ef409caef7c65b877bcecf2b227dc0d74aa0ec7",
"rust-analyzer-1.95.0-aarch64-pc-windows-gnullvm.tar.xz": "6b2c0820957fdf0e3026f51fe85ad3cef94d5948e29cc83d2221f65dafc7a16b",
"rust-analyzer-1.95.0-aarch64-pc-windows-msvc.tar.xz": "92958624f23d4b0980748ac9e6d67f6f67a868f8224e8c6240e3f84145e2d805",
"rust-analyzer-1.95.0-aarch64-unknown-linux-gnu.tar.xz": "b37e5b9aad624e54228254f98a710ee19ad464fe7ada93ef12e20c87886a0047",
"rust-analyzer-1.95.0-x86_64-apple-darwin.tar.xz": "6cd111900e13fd19b188c5d8844b34136af3967066c0ea2914ce5c3508296c85",
"rust-analyzer-1.95.0-x86_64-pc-windows-gnullvm.tar.xz": "01bd05a6b990ad37907ff26e8c285c5ba8b7e674fabd0e264fc7f7aa04d963c5",
"rust-analyzer-1.95.0-x86_64-pc-windows-msvc.tar.xz": "ba58e349f5e8b0ef13735c48d4ad8d8c7664472f8403f3c9d97b291bd54a7638",
"rust-analyzer-1.95.0-x86_64-unknown-linux-gnu.tar.xz": "a9d71c6e7427c45afcd846a8b34a3e3301ae7a0e91a2bcf929326af77a7dc68e",
"rust-src-1.95.0.tar.xz": "67b09138c8db96afc4bbfc69ea771ac9a091fd777698acb43f6dfd9fb7dea363",
"rust-std-1.95.0-aarch64-apple-darwin.tar.xz": "9b30089b0f767cb91b2190ffec55a9beeb2a21a1405d8da0f664d7e09d08e6d8",
"rust-std-1.95.0-aarch64-apple-ios-macabi.tar.xz": "0e1760828f4e0fa1cde0061ba5680619dcc1cdcafec9242cc18dc4547c73b1cd",
"rust-std-1.95.0-aarch64-apple-ios-sim.tar.xz": "4bfe5b0c74c10d121a8ac60f1833c7714b963f9130f6256ca313d94405267deb",
"rust-std-1.95.0-aarch64-apple-ios.tar.xz": "6fcc42d8dbba4910a128ffa32d62a730339a7e3882a90341a881f2edf66ff55a",
"rust-std-1.95.0-aarch64-linux-android.tar.xz": "de5e8fa5d955809891eea77682811fc90be705f78883bd94071e98f5a738d05b",
"rust-std-1.95.0-aarch64-pc-windows-gnullvm.tar.xz": "93d810b8872771afe04f66aa30a4eee48736aca693186e4c7cc2766e1a82e340",
"rust-std-1.95.0-aarch64-pc-windows-msvc.tar.xz": "be21b5a8a71c49b4dcbc19956233b0de7bfda3ee3c8a199148299f867e95cb42",
"rust-std-1.95.0-aarch64-unknown-fuchsia.tar.xz": "b311a0523f75e031d683d983515edb9baaf22a843dbd44ce2a30a3204752f592",
"rust-std-1.95.0-aarch64-unknown-linux-gnu.tar.xz": "3a21b271b1ff973b94d69b25e7a39992f9fbcae1ab6d9475844a23e6ad3908ac",
"rust-std-1.95.0-aarch64-unknown-linux-musl.tar.xz": "f6710416ed9a7d5cf2a15efa761eb79a1deeb43f9961bbe05cc97bec4ef9064a",
"rust-std-1.95.0-aarch64-unknown-none-softfloat.tar.xz": "54d691468e25e7989b022a171337beadf78b5202877b312b75182b7f93efbb8b",
"rust-std-1.95.0-aarch64-unknown-none.tar.xz": "2b0c986dc9902866311f1fe2d44bc2bd84479d2ac84ed7ada76a5eb7ba37080f",
"rust-std-1.95.0-aarch64-unknown-uefi.tar.xz": "ca657564103024d345ca32e8e4ade7ebb395a51163d2897d9e5f8373c025e49e",
"rust-std-1.95.0-arm-linux-androideabi.tar.xz": "12a9c5fa24608159c2b2bd50abc0c6d0add407c0258cee894c2f61c07051a9c4",
"rust-std-1.95.0-arm-unknown-linux-gnueabi.tar.xz": "f6cd592dacdf41f724ee90a2f34db028e37ca2a7fb26fa86e93e8fd68e24d066",
"rust-std-1.95.0-arm-unknown-linux-gnueabihf.tar.xz": "fda8408ea17881c6529e27e58672d6c628f786cad557fac92856077e7a610239",
"rust-std-1.95.0-arm-unknown-linux-musleabi.tar.xz": "62f21fabc209fd0de53156764fc426da74c59525bb60cb4c1c3ffd1be0bbe00b",
"rust-std-1.95.0-arm-unknown-linux-musleabihf.tar.xz": "2842ce67f7a4c68c6e8b30ad3bb36484fb745edcc2694b2a36bf0609cf758044",
"rust-std-1.95.0-armv7-linux-androideabi.tar.xz": "61e95e144986c52ff9fa2fe3c249a68b2bf268adb2a2eeb81d80c180e43027f1",
"rust-std-1.95.0-armv7-unknown-linux-gnueabi.tar.xz": "e23454d6ca7fc3f5eb7cf9241572765176d86e9f45d4f394de31a5fd794e523f",
"rust-std-1.95.0-armv7-unknown-linux-gnueabihf.tar.xz": "bd319e18ca2dad0450f76277874d56356330da536be8cf271509f8e6f28ac6de",
"rust-std-1.95.0-armv7-unknown-linux-musleabi.tar.xz": "a49bc987e0531800f92825ce61402b5734ea747caa8970d3373506742ed7daa6",
"rust-std-1.95.0-armv7-unknown-linux-musleabihf.tar.xz": "77f9aaff4669c076edc96cdb99dc21749d2c692c862232b52176572c289fe671",
"rust-std-1.95.0-i686-linux-android.tar.xz": "e7b5e18d1d4119c7c1454ee8427933f27aeff9e81a22248c2829f5f299d3a937",
"rust-std-1.95.0-i686-pc-windows-gnu.tar.xz": "2e98fb94fc500690d7e72e071ce29dd98790ff518163963f5c82c32afba9231d",
"rust-std-1.95.0-i686-pc-windows-gnullvm.tar.xz": "b429a8c456d6a1815ece064102bd6a25f67d46db4f6d7b79dbe7d1b9d1f78e0f",
"rust-std-1.95.0-i686-pc-windows-msvc.tar.xz": "6206fd7e8bd119e9d1ba1c425ad25c282512eb0d5659f2dcb4be224b84715706",
"rust-std-1.95.0-i686-unknown-freebsd.tar.xz": "3b13b3ccecf482c0da3e94f19e0aa5e8e375622f4e09b30e6dae2b7638bee63f",
"rust-std-1.95.0-i686-unknown-linux-gnu.tar.xz": "527c5d5249a7f77b48d3c9da3ac512d27b47f43d08dbe3c6f82a3d5b35d8aa27",
"rust-std-1.95.0-i686-unknown-linux-musl.tar.xz": "af4d3e7aabb63d39a7a2ff5435cc993b65ff38a2d2e23f1967e519037a1b0455",
"rust-std-1.95.0-i686-unknown-uefi.tar.xz": "3233985273616ec36861f2d50b4a025c903b2bb8c45b171c0ae9e2de8342125b",
"rust-std-1.95.0-powerpc-unknown-linux-gnu.tar.xz": "59e0abbaa246502521e37c55b8d6cf88d5b8a697b0c70c61ec189937308f7246",
"rust-std-1.95.0-powerpc64-unknown-linux-gnu.tar.xz": "cc7fb9aa289ff1756502ae16a05e2885289165f01ed94a7c2db6576b3dae74a6",
"rust-std-1.95.0-powerpc64le-unknown-linux-gnu.tar.xz": "2370d9266051a0b23346d42e43a00f91b2daff22a963fb03e28ae50cb0b76c50",
"rust-std-1.95.0-powerpc64le-unknown-linux-musl.tar.xz": "0362ba4ecfe0bb508f0d2b064c6fbbfe72604d5ae1989d6a8a7b4fe5ff1889f1",
"rust-std-1.95.0-riscv32imc-unknown-none-elf.tar.xz": "04befebf3b15372dacb7e6c0fcdab842c17a63ea58bf2f4cdccd7182ae9195c6",
"rust-std-1.95.0-riscv64gc-unknown-linux-gnu.tar.xz": "50fe7869e166bb4c990a0e1664366b1ffdbe669664b7663cd03c079bd0efdcac",
"rust-std-1.95.0-riscv64gc-unknown-linux-musl.tar.xz": "e01bdbf5d6fa3e529671d49e87ba81dc9612101144f3ee5a0e1de3c48f27b47c",
"rust-std-1.95.0-riscv64gc-unknown-none-elf.tar.xz": "a4cb7a1527f3b56a39464e5ca2b174a27b708b26d78e604735eb5ffd9ee4d20b",
"rust-std-1.95.0-s390x-unknown-linux-gnu.tar.xz": "31978c1286afff9a0bb7f01c2ae4a39f40727b6100a82b6d934f146b06cde510",
"rust-std-1.95.0-thumbv6m-none-eabi.tar.xz": "602ec023c4615fc1c2d78b688554d42fa525e07e861c052f406fd7a607e5d5ee",
"rust-std-1.95.0-thumbv7em-none-eabi.tar.xz": "fb671966ba9aede333956ed43fcfe114ec890ca6e70369c9f3219871ee3ae8ae",
"rust-std-1.95.0-thumbv7em-none-eabihf.tar.xz": "fa3d189c09b64d818ad65a3fec1ce1c7d7b3908aea6fc4607a3fcc05067cad81",
"rust-std-1.95.0-thumbv7m-none-eabi.tar.xz": "e9e39f483ad4c1ce55fa4f508009e8d7d7ef25efe6592f7bfbabc159a24658ff",
"rust-std-1.95.0-thumbv8m.main-none-eabi.tar.xz": "3b64bffb193b37e83dc7b169add55f0ef3298220f8475468be4bb87276a68105",
"rust-std-1.95.0-thumbv8m.main-none-eabihf.tar.xz": "25ec92187d3a45fb1e397b0ba6000341d872523925a99a48b1978bdf1e6038cb",
"rust-std-1.95.0-wasm32-unknown-emscripten.tar.xz": "967b92a8682e8b8a1a459d776b36e41c906cdcac1008fef70e4800fec40d6864",
"rust-std-1.95.0-wasm32-unknown-unknown.tar.xz": "5587b89ff69623d09e476439d44a24453b4e4ea3d5e0b53a5c0a935151ff3fd1",
"rust-std-1.95.0-wasm32-wasip1-threads.tar.xz": "9079935a00a3c3aaf284957bbe82972983ce2708019687cec1f4988c30c1e0f3",
"rust-std-1.95.0-wasm32-wasip1.tar.xz": "86e5b6d98c7520bb9c3ad4f8cbbbf14beaf230b0f06b437db398e3c4f7dae43e",
"rust-std-1.95.0-wasm32-wasip2.tar.xz": "68146eb4c887431379966efa21d75dc957f18bd1166239c1deaa53fe38cb9ab4",
"rust-std-1.95.0-x86_64-apple-darwin.tar.xz": "2be13c14122b8d4d09b7f7c434fca9ae7215ec72049944189c88c4d9128ce504",
"rust-std-1.95.0-x86_64-apple-ios-macabi.tar.xz": "60b92e51e87f84046e0b19ccedc88c45b4b62c3ab10351f8473453f341c894f1",
"rust-std-1.95.0-x86_64-apple-ios.tar.xz": "11abb7b1c92b5a88b8c3ba21ee596a1be7dee5e817b336f26b4968d8ed5513ad",
"rust-std-1.95.0-x86_64-linux-android.tar.xz": "77b8e2be4a6e784a63cd77de944864c8044ddf4d5c7d56f663ada8a38a8319c4",
"rust-std-1.95.0-x86_64-pc-windows-gnu.tar.xz": "f57e045016a04130125fb43295d95f9ad2bebc296150eadb031dbf5167ad12bd",
"rust-std-1.95.0-x86_64-pc-windows-gnullvm.tar.xz": "3c52a0e34e0b4abe4439cabd77383408f5f9c80b6e249fbc0855df424b45008d",
"rust-std-1.95.0-x86_64-pc-windows-msvc.tar.xz": "7c659bdc88646e7e1befa370881bd311be87b26f006933a28b40dcab2f7cc832",
"rust-std-1.95.0-x86_64-unknown-freebsd.tar.xz": "dfe913c2f477172db10d3723d9f5c536d8b6f42776c979cc855820d8249adec3",
"rust-std-1.95.0-x86_64-unknown-fuchsia.tar.xz": "faa2bc09a3992f1d81b538121d64a3a396f4ec666e665c79d2ab47461c2d3206",
"rust-std-1.95.0-x86_64-unknown-linux-gnu.tar.xz": "047ea7098803d3500fa1072e9cee5392697e21525559e4458128a2bf874aa382",
"rust-std-1.95.0-x86_64-unknown-linux-musl.tar.xz": "aee540abf132920f791ef781489851a078d69dff493fb628d49c1d573f92bb3a",
"rust-std-1.95.0-x86_64-unknown-netbsd.tar.xz": "7a82b71c53f20cb147a340819fcab645da220b312a96194531e631ee99783a7d",
"rust-std-1.95.0-x86_64-unknown-none.tar.xz": "7c151c0e7bf3b0b4d7136774cd3686e5f691b761b648b17e83af58e7669d3e01",
"rust-std-1.95.0-x86_64-unknown-uefi.tar.xz": "4cc55629480aa8ab5b39eb6b7458433b48461d6626fdea0330fb88e23af818ea",
"rustc-1.95.0-aarch64-apple-darwin.tar.xz": "149e85a285b6eba58eb6c8bdf7deb1b93763890598e62cb635a712e3a8454f04",
"rustc-1.95.0-aarch64-pc-windows-gnullvm.tar.xz": "758e729faabd8dabfab584d2bead59ca4fbcf1125ffc3c43a69332d6d9f2316b",
"rustc-1.95.0-aarch64-pc-windows-msvc.tar.xz": "0dbec9739b93427ccdd3948c3b1f83cec42e4c9545d930a8d1e1464ff4092c5f",
"rustc-1.95.0-aarch64-unknown-linux-gnu.tar.xz": "0fe3689eeaed603e5ef24572d11597d3edadaefd2cb181674ad621260f2501d2",
"rustc-1.95.0-x86_64-apple-darwin.tar.xz": "33db457715446a69ed6f69f78f5fbb9ca8e17a16585d1d7a0060479bfe4c7afc",
"rustc-1.95.0-x86_64-pc-windows-gnullvm.tar.xz": "44ffbe057bb8f967087a1ad549a7139e9e5017d3aab396f42a6393f897e39531",
"rustc-1.95.0-x86_64-pc-windows-msvc.tar.xz": "4cb1f3b578adc6541cbe13a6f85f1fd8c0ce643d90b506a36dee24c680864c67",
"rustc-1.95.0-x86_64-unknown-linux-gnu.tar.xz": "8426a3d170a5879f5682f5fbdd024a1779b3951e7baba685af2d6dc32a6dfc15",
"rustfmt-1.95.0-aarch64-apple-darwin.tar.xz": "c54af79adfdc790d27fc56e24407e370c80be5a89ad537eef9fbd45d3c3e28e8",
"rustfmt-1.95.0-aarch64-pc-windows-gnullvm.tar.xz": "85b690b664ee4f03521c6bc3f1af8fd7e6efae5851a8a7ff18965594484688e2",
"rustfmt-1.95.0-aarch64-pc-windows-msvc.tar.xz": "a873c048743e6da29e09a8b55c774ee113f8f6ae4fd57d988d304a6453801b34",
"rustfmt-1.95.0-aarch64-unknown-linux-gnu.tar.xz": "64cce868f0f3d29f1524e11e9bab01ac9d538a31665fea1cd6b78af46a1c0a41",
"rustfmt-1.95.0-x86_64-apple-darwin.tar.xz": "5f7228f40a160e80d260e74e068d6fec8627aa02f1f5ae29d2019b9347076401",
"rustfmt-1.95.0-x86_64-pc-windows-gnullvm.tar.xz": "74eb0bb0227af0df81a5df9cbf8384e5e038c4cd575152ecc27dfa891ff3756a",
"rustfmt-1.95.0-x86_64-pc-windows-msvc.tar.xz": "8bcf91606e36b8a0164efafde50709cd7a3c02143a2edaa81dbf3dccd6ed8f4c",
"rustfmt-1.95.0-x86_64-unknown-linux-gnu.tar.xz": "f1b2a7301513ffdd95ebf22ebbdd932e4d17fc806f748d93924740d0297b1396"
}
}
}

View File

@@ -1,4 +1,3 @@
<p align="center"><code>npm i -g @openai/codex</code><br />or <code>brew install --cask codex</code></p>
<p align="center"><strong>Codex CLI</strong> is a coding agent from OpenAI that runs locally on your computer.
<p align="center">
<img src="https://github.com/openai/codex/blob/main/.github/codex-cli-splash.png" alt="Codex CLI splash" width="80%" />
@@ -14,7 +13,19 @@ If you want Codex in your code editor (VS Code, Cursor, Windsurf), <a href="http
### Installing and running Codex CLI
Install globally with your preferred package manager:
Run the following on Mac or Linux to install Codex CLI:
```shell
curl -fsSL https://chatgpt.com/codex/install.sh | sh
```
Run the following on Windows to install Codex CLI:
```
powershell -ExecutionPolicy ByPass -c "irm https://chatgpt.com/codex/install.ps1 | iex"
```
Codex CLI can also be installed via the following package managers:
```shell
# Install using npm

View File

@@ -77,33 +77,43 @@ if (!platformPackage) {
const codexBinaryName = process.platform === "win32" ? "codex.exe" : "codex";
const localVendorRoot = path.join(__dirname, "..", "vendor");
const localBinaryPath = path.join(
localVendorRoot,
targetTriple,
"codex",
codexBinaryName,
);
const packageBinaryPath = (vendorRoot) =>
path.join(vendorRoot, targetTriple, "bin", codexBinaryName);
const legacyBinaryPath = (vendorRoot) =>
path.join(vendorRoot, targetTriple, "codex", codexBinaryName);
let vendorRoot;
try {
const packageJsonPath = require.resolve(`${platformPackage}/package.json`);
vendorRoot = path.join(path.dirname(packageJsonPath), "vendor");
} catch {
if (existsSync(localBinaryPath)) {
vendorRoot = localVendorRoot;
} else {
const packageManager = detectPackageManager();
const updateCommand =
packageManager === "bun"
? "bun install -g @openai/codex@latest"
: "npm install -g @openai/codex@latest";
throw new Error(
`Missing optional dependency ${platformPackage}. Reinstall Codex: ${updateCommand}`,
);
function resolveNativePackage(vendorRoot) {
const packageRoot = path.join(vendorRoot, targetTriple);
const binaryPath = packageBinaryPath(vendorRoot);
if (existsSync(binaryPath)) {
return {
binaryPath,
pathDir: path.join(packageRoot, "codex-path"),
};
}
const legacyPath = legacyBinaryPath(vendorRoot);
if (existsSync(legacyPath)) {
return {
binaryPath: legacyPath,
pathDir: path.join(packageRoot, "path"),
};
}
return null;
}
if (!vendorRoot) {
let nativePackage;
try {
const packageJsonPath = require.resolve(`${platformPackage}/package.json`);
nativePackage = resolveNativePackage(
path.join(path.dirname(packageJsonPath), "vendor"),
);
} catch {
nativePackage = resolveNativePackage(localVendorRoot);
}
if (!nativePackage) {
const packageManager = detectPackageManager();
const updateCommand =
packageManager === "bun"
@@ -114,8 +124,7 @@ if (!vendorRoot) {
);
}
const archRoot = path.join(vendorRoot, targetTriple);
const binaryPath = path.join(archRoot, "codex", codexBinaryName);
const { binaryPath, pathDir } = nativePackage;
// Use an asynchronous spawn instead of spawnSync so that Node is able to
// respond to signals (e.g. Ctrl-C / SIGINT) while the native binary is
@@ -159,7 +168,6 @@ function detectPackageManager() {
}
const additionalDirs = [];
const pathDir = path.join(archRoot, "path");
if (existsSync(pathDir)) {
additionalDirs.push(pathDir);
}

View File

@@ -1,6 +1,7 @@
{
"name": "@openai/codex",
"version": "0.0.0-dev",
"description": "Codex CLI is a coding agent from OpenAI that runs locally on your computer.",
"license": "Apache-2.0",
"bin": {
"codex": "bin/codex.js"
@@ -10,8 +11,7 @@
"node": ">=16"
},
"files": [
"bin",
"vendor"
"bin/codex.js"
],
"repository": {
"type": "git",

View File

@@ -11,13 +11,13 @@ example, to stage the CLI, responses proxy, and SDK packages for version `0.6.0`
--package codex-sdk
```
This downloads the native artifacts once, hydrates `vendor/` for each package, and writes
tarballs to `dist/npm/`.
This downloads the required native package archive artifacts, hydrates `vendor/` for
each package, and writes tarballs to `dist/npm/`.
When `--package codex` is provided, the staging helper builds the lightweight
`@openai/codex` meta package plus all platform-native `@openai/codex` variants
that are later published under platform-specific dist-tags.
If you need to invoke `build_npm_package.py` directly, run
`codex-cli/scripts/install_native_deps.py` first and pass `--vendor-src` pointing to the
directory that contains the populated `vendor/` tree.
Direct `build_npm_package.py` invocations are still useful for package-specific
debugging, but native packages expect `--vendor-src` to point at a prehydrated
`vendor/` tree. Release packaging should use `scripts/stage_npm_packages.py`.

View File

@@ -3,6 +3,7 @@
import argparse
import json
import os
import shutil
import subprocess
import sys
@@ -15,6 +16,7 @@ REPO_ROOT = CODEX_CLI_ROOT.parent
RESPONSES_API_PROXY_NPM_ROOT = REPO_ROOT / "codex-rs" / "responses-api-proxy" / "npm"
CODEX_SDK_ROOT = REPO_ROOT / "sdk" / "typescript"
CODEX_NPM_NAME = "@openai/codex"
CODEX_PACKAGE_COMPONENT = "codex-package"
# `npm_name` is the local optional-dependency alias consumed by `bin/codex.js`.
# The underlying package published to npm is always `@openai/codex`.
@@ -69,12 +71,12 @@ PACKAGE_EXPANSIONS: dict[str, list[str]] = {
PACKAGE_NATIVE_COMPONENTS: dict[str, list[str]] = {
"codex": [],
"codex-linux-x64": ["bwrap", "codex", "rg"],
"codex-linux-arm64": ["bwrap", "codex", "rg"],
"codex-darwin-x64": ["codex", "rg"],
"codex-darwin-arm64": ["codex", "rg"],
"codex-win32-x64": ["codex", "rg", "codex-windows-sandbox-setup", "codex-command-runner"],
"codex-win32-arm64": ["codex", "rg", "codex-windows-sandbox-setup", "codex-command-runner"],
"codex-linux-x64": [CODEX_PACKAGE_COMPONENT],
"codex-linux-arm64": [CODEX_PACKAGE_COMPONENT],
"codex-darwin-x64": [CODEX_PACKAGE_COMPONENT],
"codex-darwin-arm64": [CODEX_PACKAGE_COMPONENT],
"codex-win32-x64": [CODEX_PACKAGE_COMPONENT],
"codex-win32-arm64": [CODEX_PACKAGE_COMPONENT],
"codex-responses-api-proxy": ["codex-responses-api-proxy"],
"codex-sdk": [],
}
@@ -86,16 +88,6 @@ PACKAGE_TARGET_FILTERS: dict[str, str] = {
PACKAGE_CHOICES = tuple(PACKAGE_NATIVE_COMPONENTS)
COMPONENT_DEST_DIR: dict[str, str] = {
"bwrap": "codex-resources",
"codex": "codex",
"codex-responses-api-proxy": "codex-responses-api-proxy",
"codex-windows-sandbox-setup": "codex",
"codex-command-runner": "codex",
"rg": "path",
}
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Build or stage the Codex CLI npm package.")
parser.add_argument(
@@ -138,16 +130,6 @@ def parse_args() -> argparse.Namespace:
type=Path,
help="Directory containing pre-installed native binaries to bundle (vendor root).",
)
parser.add_argument(
"--allow-missing-native-component",
dest="allow_missing_native_components",
action="append",
default=[],
help=(
"Native component that may be absent from --vendor-src. Intended for CI "
"compatibility with older artifact workflows; releases should not use this."
),
)
return parser.parse_args()
@@ -188,7 +170,6 @@ def main() -> int:
staging_dir,
native_components,
target_filter={target_filter} if target_filter else None,
allow_missing_components=set(args.allow_missing_native_components),
)
if release_version:
@@ -253,9 +234,6 @@ def stage_sources(staging_dir: Path, version: str, package: str) -> None:
bin_dir = staging_dir / "bin"
bin_dir.mkdir(parents=True, exist_ok=True)
shutil.copy2(CODEX_CLI_ROOT / "bin" / "codex.js", bin_dir / "codex.js")
rg_manifest = CODEX_CLI_ROOT / "bin" / "rg"
if rg_manifest.exists():
shutil.copy2(rg_manifest, bin_dir / "rg")
readme_src = REPO_ROOT / "README.md"
if readme_src.exists():
@@ -314,7 +292,7 @@ def stage_sources(staging_dir: Path, version: str, package: str) -> None:
package_json["version"] = version
if package == "codex":
package_json["files"] = ["bin"]
package_json["files"] = ["bin/codex.js"]
package_json["optionalDependencies"] = {
CODEX_PLATFORM_PACKAGES[platform_package]["npm_name"]: (
f"npm:{CODEX_NPM_NAME}@"
@@ -347,7 +325,7 @@ def compute_platform_package_version(version: str, platform_tag: str) -> str:
def run_command(cmd: list[str], cwd: Path | None = None) -> None:
print("+", " ".join(cmd))
print("+", " ".join(cmd), flush=True)
subprocess.run(cmd, cwd=cwd, check=True)
@@ -377,14 +355,12 @@ def copy_native_binaries(
staging_dir: Path,
components: list[str],
target_filter: set[str] | None = None,
allow_missing_components: set[str] | None = None,
) -> None:
vendor_src = vendor_src.resolve()
if not vendor_src.exists():
raise RuntimeError(f"Vendor source directory not found: {vendor_src}")
components_set = {component for component in components if component in COMPONENT_DEST_DIR}
allow_missing_components = allow_missing_components or set()
components_set = set(components)
if not components_set:
return
@@ -402,24 +378,25 @@ def copy_native_binaries(
if target_filter is not None and target_dir.name not in target_filter:
continue
dest_target_dir = vendor_dest / target_dir.name
dest_target_dir.mkdir(parents=True, exist_ok=True)
copied_targets.add(target_dir.name)
for component in components_set:
dest_dir_name = COMPONENT_DEST_DIR.get(component)
if dest_dir_name is None:
continue
dest_target_dir = vendor_dest / target_dir.name
src_component_dir = target_dir / dest_dir_name
if CODEX_PACKAGE_COMPONENT in components_set:
if dest_target_dir.exists():
shutil.rmtree(dest_target_dir)
shutil.copytree(target_dir, dest_target_dir)
else:
dest_target_dir.mkdir(parents=True, exist_ok=True)
for component in sorted(components_set - {CODEX_PACKAGE_COMPONENT}):
src_component_dir = target_dir / component
if not src_component_dir.exists():
if component in allow_missing_components:
continue
raise RuntimeError(
f"Missing native component '{component}' in vendor source: {src_component_dir}"
)
dest_component_dir = dest_target_dir / dest_dir_name
dest_component_dir = dest_target_dir / component
if dest_component_dir.exists():
shutil.rmtree(dest_component_dir)
shutil.copytree(src_component_dir, dest_component_dir)
@@ -430,16 +407,23 @@ def copy_native_binaries(
missing_list = ", ".join(missing_targets)
raise RuntimeError(f"Missing target directories in vendor source: {missing_list}")
def run_npm_pack(staging_dir: Path, output_path: Path) -> Path:
output_path = output_path.resolve()
output_path.parent.mkdir(parents=True, exist_ok=True)
with tempfile.TemporaryDirectory(prefix="codex-npm-pack-") as pack_dir_str:
pack_dir = Path(pack_dir_str)
npm_cache_dir = pack_dir / "npm-cache"
npm_logs_dir = pack_dir / "npm-logs"
npm_cache_dir.mkdir()
npm_logs_dir.mkdir()
env = os.environ.copy()
env["NPM_CONFIG_CACHE"] = str(npm_cache_dir)
env["NPM_CONFIG_LOGS_DIR"] = str(npm_logs_dir)
stdout = subprocess.check_output(
["npm", "pack", "--json", "--pack-destination", str(pack_dir)],
cwd=staging_dir,
env=env,
text=True,
)
try:

View File

@@ -1,483 +0,0 @@
#!/usr/bin/env python3
"""Install Codex native binaries (Rust CLI, bwrap, and ripgrep helpers)."""
import argparse
from contextlib import contextmanager
import json
import os
import shutil
import subprocess
import tarfile
import tempfile
import zipfile
from dataclasses import dataclass
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path
import sys
from typing import Iterable, Sequence
from urllib.parse import urlparse
from urllib.request import urlopen
SCRIPT_DIR = Path(__file__).resolve().parent
CODEX_CLI_ROOT = SCRIPT_DIR.parent
DEFAULT_WORKFLOW_URL = "https://github.com/openai/codex/actions/runs/17952349351" # rust-v0.40.0
VENDOR_DIR_NAME = "vendor"
RG_MANIFEST = CODEX_CLI_ROOT / "bin" / "rg"
BINARY_TARGETS = (
"x86_64-unknown-linux-musl",
"aarch64-unknown-linux-musl",
"x86_64-apple-darwin",
"aarch64-apple-darwin",
"x86_64-pc-windows-msvc",
"aarch64-pc-windows-msvc",
)
@dataclass(frozen=True)
class BinaryComponent:
artifact_prefix: str # matches the artifact filename prefix (e.g. codex-<target>.zst)
dest_dir: str # directory under vendor/<target>/ where the binary is installed
binary_basename: str # executable name inside dest_dir (before optional .exe)
targets: tuple[str, ...] | None = None # limit installation to specific targets
WINDOWS_TARGETS = tuple(target for target in BINARY_TARGETS if "windows" in target)
LINUX_TARGETS = tuple(target for target in BINARY_TARGETS if "linux" in target)
BINARY_COMPONENTS = {
"bwrap": BinaryComponent(
artifact_prefix="bwrap",
dest_dir="codex-resources",
binary_basename="bwrap",
targets=LINUX_TARGETS,
),
"codex": BinaryComponent(
artifact_prefix="codex",
dest_dir="codex",
binary_basename="codex",
),
"codex-responses-api-proxy": BinaryComponent(
artifact_prefix="codex-responses-api-proxy",
dest_dir="codex-responses-api-proxy",
binary_basename="codex-responses-api-proxy",
),
"codex-windows-sandbox-setup": BinaryComponent(
artifact_prefix="codex-windows-sandbox-setup",
dest_dir="codex",
binary_basename="codex-windows-sandbox-setup",
targets=WINDOWS_TARGETS,
),
"codex-command-runner": BinaryComponent(
artifact_prefix="codex-command-runner",
dest_dir="codex",
binary_basename="codex-command-runner",
targets=WINDOWS_TARGETS,
),
}
RG_TARGET_PLATFORM_PAIRS: list[tuple[str, str]] = [
("x86_64-unknown-linux-musl", "linux-x86_64"),
("aarch64-unknown-linux-musl", "linux-aarch64"),
("x86_64-apple-darwin", "macos-x86_64"),
("aarch64-apple-darwin", "macos-aarch64"),
("x86_64-pc-windows-msvc", "windows-x86_64"),
("aarch64-pc-windows-msvc", "windows-aarch64"),
]
RG_TARGET_TO_PLATFORM = {target: platform for target, platform in RG_TARGET_PLATFORM_PAIRS}
DEFAULT_RG_TARGETS = [target for target, _ in RG_TARGET_PLATFORM_PAIRS]
# urllib.request.urlopen() defaults to no timeout (can hang indefinitely), which is painful in CI.
DOWNLOAD_TIMEOUT_SECS = 60
def _gha_enabled() -> bool:
# GitHub Actions supports "workflow commands" (e.g. ::group:: / ::error::) that make logs
# much easier to scan: groups collapse noisy sections and error annotations surface the
# failure in the UI without changing the actual exception/traceback output.
return os.environ.get("GITHUB_ACTIONS") == "true"
def _gha_escape(value: str) -> str:
# Workflow commands require percent/newline escaping.
return value.replace("%", "%25").replace("\r", "%0D").replace("\n", "%0A")
def _gha_error(*, title: str, message: str) -> None:
# Emit a GitHub Actions error annotation. This does not replace stdout/stderr logs; it just
# adds a prominent summary line to the job UI so the root cause is easier to spot.
if not _gha_enabled():
return
print(
f"::error title={_gha_escape(title)}::{_gha_escape(message)}",
flush=True,
)
@contextmanager
def _gha_group(title: str):
# Wrap a block in a collapsible log group on GitHub Actions. Outside of GHA this is a no-op
# so local output remains unchanged.
if _gha_enabled():
print(f"::group::{_gha_escape(title)}", flush=True)
try:
yield
finally:
if _gha_enabled():
print("::endgroup::", flush=True)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Install native Codex binaries.")
parser.add_argument(
"--workflow-url",
help=(
"GitHub Actions workflow URL that produced the artifacts. Defaults to a "
"known good run when omitted."
),
)
parser.add_argument(
"--component",
dest="components",
action="append",
choices=tuple(list(BINARY_COMPONENTS) + ["rg"]),
help=(
"Limit installation to the specified components."
" May be repeated. Defaults to bwrap, codex, codex-windows-sandbox-setup,"
" codex-command-runner, and rg."
),
)
parser.add_argument(
"root",
nargs="?",
type=Path,
help=(
"Directory containing package.json for the staged package. If omitted, the "
"repository checkout is used."
),
)
return parser.parse_args()
def main() -> int:
args = parse_args()
codex_cli_root = (args.root or CODEX_CLI_ROOT).resolve()
vendor_dir = codex_cli_root / VENDOR_DIR_NAME
vendor_dir.mkdir(parents=True, exist_ok=True)
components = args.components or [
"bwrap",
"codex",
"codex-windows-sandbox-setup",
"codex-command-runner",
"rg",
]
workflow_url = (args.workflow_url or DEFAULT_WORKFLOW_URL).strip()
if not workflow_url:
workflow_url = DEFAULT_WORKFLOW_URL
workflow_id = workflow_url.rstrip("/").split("/")[-1]
print(f"Downloading native artifacts from workflow {workflow_id}...")
with _gha_group(f"Download native artifacts from workflow {workflow_id}"):
with tempfile.TemporaryDirectory(prefix="codex-native-artifacts-") as artifacts_dir_str:
artifacts_dir = Path(artifacts_dir_str)
_download_artifacts(workflow_id, artifacts_dir)
install_binary_components(
artifacts_dir,
vendor_dir,
[BINARY_COMPONENTS[name] for name in components if name in BINARY_COMPONENTS],
)
if "rg" in components:
with _gha_group("Fetch ripgrep binaries"):
print("Fetching ripgrep binaries...")
fetch_rg(vendor_dir, DEFAULT_RG_TARGETS, manifest_path=RG_MANIFEST)
print(f"Installed native dependencies into {vendor_dir}")
return 0
def fetch_rg(
vendor_dir: Path,
targets: Sequence[str] | None = None,
*,
manifest_path: Path,
) -> list[Path]:
"""Download ripgrep binaries described by the DotSlash manifest."""
if targets is None:
targets = DEFAULT_RG_TARGETS
if not manifest_path.exists():
raise FileNotFoundError(f"DotSlash manifest not found: {manifest_path}")
manifest = _load_manifest(manifest_path)
platforms = manifest.get("platforms", {})
vendor_dir.mkdir(parents=True, exist_ok=True)
targets = list(targets)
if not targets:
return []
task_configs: list[tuple[str, str, dict]] = []
for target in targets:
platform_key = RG_TARGET_TO_PLATFORM.get(target)
if platform_key is None:
raise ValueError(f"Unsupported ripgrep target '{target}'.")
platform_info = platforms.get(platform_key)
if platform_info is None:
raise RuntimeError(f"Platform '{platform_key}' not found in manifest {manifest_path}.")
task_configs.append((target, platform_key, platform_info))
results: dict[str, Path] = {}
max_workers = min(len(task_configs), max(1, (os.cpu_count() or 1)))
print("Installing ripgrep binaries for targets: " + ", ".join(targets))
with ThreadPoolExecutor(max_workers=max_workers) as executor:
future_map = {
executor.submit(
_fetch_single_rg,
vendor_dir,
target,
platform_key,
platform_info,
manifest_path,
): target
for target, platform_key, platform_info in task_configs
}
for future in as_completed(future_map):
target = future_map[future]
try:
results[target] = future.result()
except Exception as exc:
_gha_error(
title="ripgrep install failed",
message=f"target={target} error={exc!r}",
)
raise RuntimeError(f"Failed to install ripgrep for target {target}.") from exc
print(f" installed ripgrep for {target}")
return [results[target] for target in targets]
def _download_artifacts(workflow_id: str, dest_dir: Path) -> None:
cmd = [
"gh",
"run",
"download",
"--dir",
str(dest_dir),
"--repo",
"openai/codex",
workflow_id,
]
subprocess.check_call(cmd)
def install_binary_components(
artifacts_dir: Path,
vendor_dir: Path,
selected_components: Sequence[BinaryComponent],
) -> None:
if not selected_components:
return
for component in selected_components:
component_targets = list(component.targets or BINARY_TARGETS)
print(
f"Installing {component.binary_basename} binaries for targets: "
+ ", ".join(component_targets)
)
max_workers = min(len(component_targets), max(1, (os.cpu_count() or 1)))
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {
executor.submit(
_install_single_binary,
artifacts_dir,
vendor_dir,
target,
component,
): target
for target in component_targets
}
for future in as_completed(futures):
installed_path = future.result()
print(f" installed {installed_path}")
def _install_single_binary(
artifacts_dir: Path,
vendor_dir: Path,
target: str,
component: BinaryComponent,
) -> Path:
artifact_subdir = artifacts_dir / target
archive_name = _archive_name_for_target(component.artifact_prefix, target)
archive_path = artifact_subdir / archive_name
if not archive_path.exists():
raise FileNotFoundError(f"Expected artifact not found: {archive_path}")
dest_dir = vendor_dir / target / component.dest_dir
dest_dir.mkdir(parents=True, exist_ok=True)
binary_name = (
f"{component.binary_basename}.exe" if "windows" in target else component.binary_basename
)
dest = dest_dir / binary_name
dest.unlink(missing_ok=True)
extract_archive(archive_path, "zst", None, dest)
if "windows" not in target:
dest.chmod(0o755)
return dest
def _archive_name_for_target(artifact_prefix: str, target: str) -> str:
if "windows" in target:
return f"{artifact_prefix}-{target}.exe.zst"
return f"{artifact_prefix}-{target}.zst"
def _fetch_single_rg(
vendor_dir: Path,
target: str,
platform_key: str,
platform_info: dict,
manifest_path: Path,
) -> Path:
providers = platform_info.get("providers", [])
if not providers:
raise RuntimeError(f"No providers listed for platform '{platform_key}' in {manifest_path}.")
url = providers[0]["url"]
archive_format = platform_info.get("format", "zst")
archive_member = platform_info.get("path")
digest = platform_info.get("digest")
expected_size = platform_info.get("size")
dest_dir = vendor_dir / target / "path"
dest_dir.mkdir(parents=True, exist_ok=True)
is_windows = platform_key.startswith("win")
binary_name = "rg.exe" if is_windows else "rg"
dest = dest_dir / binary_name
with tempfile.TemporaryDirectory() as tmp_dir_str:
tmp_dir = Path(tmp_dir_str)
archive_filename = os.path.basename(urlparse(url).path)
download_path = tmp_dir / archive_filename
print(
f" downloading ripgrep for {target} ({platform_key}) from {url}",
flush=True,
)
try:
_download_file(url, download_path)
except Exception as exc:
_gha_error(
title="ripgrep download failed",
message=f"target={target} platform={platform_key} url={url} error={exc!r}",
)
raise RuntimeError(
"Failed to download ripgrep "
f"(target={target}, platform={platform_key}, format={archive_format}, "
f"expected_size={expected_size!r}, digest={digest!r}, url={url}, dest={download_path})."
) from exc
dest.unlink(missing_ok=True)
try:
extract_archive(download_path, archive_format, archive_member, dest)
except Exception as exc:
raise RuntimeError(
"Failed to extract ripgrep "
f"(target={target}, platform={platform_key}, format={archive_format}, "
f"member={archive_member!r}, url={url}, archive={download_path})."
) from exc
if not is_windows:
dest.chmod(0o755)
return dest
def _download_file(url: str, dest: Path) -> None:
dest.parent.mkdir(parents=True, exist_ok=True)
dest.unlink(missing_ok=True)
with urlopen(url, timeout=DOWNLOAD_TIMEOUT_SECS) as response, open(dest, "wb") as out:
shutil.copyfileobj(response, out)
def extract_archive(
archive_path: Path,
archive_format: str,
archive_member: str | None,
dest: Path,
) -> None:
dest.parent.mkdir(parents=True, exist_ok=True)
if archive_format == "zst":
output_path = archive_path.parent / dest.name
subprocess.check_call(
["zstd", "-f", "-d", str(archive_path), "-o", str(output_path)]
)
shutil.move(str(output_path), dest)
return
if archive_format == "tar.gz":
if not archive_member:
raise RuntimeError("Missing 'path' for tar.gz archive in DotSlash manifest.")
with tarfile.open(archive_path, "r:gz") as tar:
try:
member = tar.getmember(archive_member)
except KeyError as exc:
raise RuntimeError(
f"Entry '{archive_member}' not found in archive {archive_path}."
) from exc
tar.extract(member, path=archive_path.parent, filter="data")
extracted = archive_path.parent / archive_member
shutil.move(str(extracted), dest)
return
if archive_format == "zip":
if not archive_member:
raise RuntimeError("Missing 'path' for zip archive in DotSlash manifest.")
with zipfile.ZipFile(archive_path) as archive:
try:
with archive.open(archive_member) as src, open(dest, "wb") as out:
shutil.copyfileobj(src, out)
except KeyError as exc:
raise RuntimeError(
f"Entry '{archive_member}' not found in archive {archive_path}."
) from exc
return
raise RuntimeError(f"Unsupported archive format '{archive_format}'.")
def _load_manifest(manifest_path: Path) -> dict:
cmd = ["dotslash", "--", "parse", str(manifest_path)]
stdout = subprocess.check_output(cmd, text=True)
try:
manifest = json.loads(stdout)
except json.JSONDecodeError as exc:
raise RuntimeError(f"Invalid DotSlash manifest output from {manifest_path}.") from exc
if not isinstance(manifest, dict):
raise RuntimeError(
f"Unexpected DotSlash manifest structure for {manifest_path}: {type(manifest)!r}"
)
return manifest
if __name__ == "__main__":
import sys
sys.exit(main())

View File

@@ -1,6 +1,8 @@
[profile.default]
# Retry once so one transient failure does not fail full-CI outright.
slow-timeout = { period = "15s", terminate-after = 2 }
# Fanout keeps the full-CI shards moving without treating every >30s test as
# stuck. Keep this aligned with the broader timeout budget we give sharded CI.
slow-timeout = { period = "30s", terminate-after = 2 }
retries = 1
[profile.default.junit]

View File

@@ -17,7 +17,7 @@ jobs:
working-directory: codex-rs
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
- uses: dtolnay/rust-toolchain@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
- name: Install cargo-audit
uses: taiki-e/install-action@v2
with:

127
codex-rs/Cargo.lock generated
View File

@@ -1859,6 +1859,7 @@ dependencies = [
"pretty_assertions",
"regex-lite",
"reqwest",
"schemars 0.8.22",
"serde",
"serde_json",
"tempfile",
@@ -1927,6 +1928,8 @@ dependencies = [
"codex-utils-cli",
"codex-utils-json-to-toml",
"codex-utils-pty",
"codex-web-search-extension",
"codex-windows-sandbox",
"core_test_support",
"flate2",
"futures",
@@ -2236,6 +2239,7 @@ dependencies = [
"codex-exec-server",
"codex-execpolicy",
"codex-features",
"codex-git-utils",
"codex-install-context",
"codex-login",
"codex-mcp",
@@ -2247,6 +2251,7 @@ dependencies = [
"codex-protocol",
"codex-responses-api-proxy",
"codex-rmcp-client",
"codex-rollout",
"codex-rollout-trace",
"codex-sandboxing",
"codex-state",
@@ -2260,7 +2265,9 @@ dependencies = [
"codex-windows-sandbox",
"crossterm",
"http 1.4.0",
"insta",
"libc",
"os_info",
"owo-colors",
"predicates",
"pretty_assertions",
@@ -2269,12 +2276,16 @@ dependencies = [
"serde_json",
"sqlx",
"supports-color 3.0.2",
"sys-locale",
"tempfile",
"tokio",
"toml 0.9.11+spec-1.1.0",
"tracing",
"tracing-appender",
"tracing-subscriber",
"unicode-segmentation",
"which 8.0.0",
"windows-sys 0.52.0",
]
[[package]]
@@ -2321,6 +2332,7 @@ dependencies = [
"codex-login",
"codex-otel",
"codex-protocol",
"codex-utils-absolute-path",
"hmac",
"pretty_assertions",
"serde",
@@ -2433,6 +2445,7 @@ dependencies = [
"dunce",
"futures",
"gethostname",
"indexmap 2.13.0",
"libc",
"multimap",
"pretty_assertions",
@@ -2505,6 +2518,7 @@ dependencies = [
"codex-feedback",
"codex-git-utils",
"codex-hooks",
"codex-install-context",
"codex-login",
"codex-mcp",
"codex-memories-read",
@@ -2952,12 +2966,21 @@ dependencies = [
name = "codex-goal-extension"
version = "0.0.0"
dependencies = [
"anyhow",
"async-trait",
"chrono",
"codex-core",
"codex-extension-api",
"codex-otel",
"codex-protocol",
"codex-state",
"codex-tools",
"pretty_assertions",
"serde",
"serde_json",
"tempfile",
"tokio",
"tracing",
]
[[package]]
@@ -2997,6 +3020,7 @@ dependencies = [
name = "codex-install-context"
version = "0.0.0"
dependencies = [
"codex-utils-absolute-path",
"codex-utils-home-dir",
"pretty_assertions",
"tempfile",
@@ -3016,6 +3040,7 @@ version = "0.0.0"
dependencies = [
"clap",
"codex-core",
"codex-install-context",
"codex-process-hardening",
"codex-protocol",
"codex-sandboxing",
@@ -3161,10 +3186,11 @@ dependencies = [
"codex-core",
"codex-extension-api",
"codex-features",
"codex-memories-read",
"codex-otel",
"codex-tools",
"codex-utils-absolute-path",
"codex-utils-output-truncation",
"codex-utils-template",
"pretty_assertions",
"schemars 0.8.22",
"serde",
@@ -3174,23 +3200,6 @@ dependencies = [
"tokio",
]
[[package]]
name = "codex-memories-mcp"
version = "0.0.0"
dependencies = [
"anyhow",
"codex-utils-absolute-path",
"codex-utils-output-truncation",
"pretty_assertions",
"rmcp",
"schemars 0.8.22",
"serde",
"serde_json",
"tempfile",
"thiserror 2.0.18",
"tokio",
]
[[package]]
name = "codex-memories-read"
version = "0.0.0"
@@ -3198,11 +3207,7 @@ dependencies = [
"codex-protocol",
"codex-shell-command",
"codex-utils-absolute-path",
"codex-utils-output-truncation",
"codex-utils-template",
"pretty_assertions",
"tempfile",
"tokio",
]
[[package]]
@@ -3546,6 +3551,7 @@ dependencies = [
"codex-utils-path",
"codex-utils-string",
"pretty_assertions",
"regex",
"serde",
"serde_json",
"tempfile",
@@ -3732,6 +3738,7 @@ dependencies = [
"async-trait",
"chrono",
"codex-git-utils",
"codex-install-context",
"codex-protocol",
"codex-rollout",
"codex-state",
@@ -3756,14 +3763,18 @@ dependencies = [
"codex-features",
"codex-protocol",
"codex-utils-absolute-path",
"codex-utils-cargo-bin",
"codex-utils-output-truncation",
"codex-utils-pty",
"codex-utils-string",
"jsonptr",
"pretty_assertions",
"rmcp",
"serde",
"serde_json",
"thiserror 2.0.18",
"tracing",
"urlencoding",
]
[[package]]
@@ -3780,7 +3791,6 @@ dependencies = [
"codex-app-server-client",
"codex-app-server-protocol",
"codex-arg0",
"codex-chatgpt",
"codex-cli",
"codex-cloud-requirements",
"codex-config",
@@ -3964,6 +3974,7 @@ version = "0.0.0"
dependencies = [
"base64 0.22.1",
"codex-utils-cache",
"divan",
"image",
"mime_guess",
"thiserror 2.0.18",
@@ -4108,6 +4119,26 @@ dependencies = [
"v8",
]
[[package]]
name = "codex-web-search-extension"
version = "0.0.0"
dependencies = [
"async-trait",
"codex-api",
"codex-core",
"codex-extension-api",
"codex-features",
"codex-login",
"codex-model-provider",
"codex-model-provider-info",
"codex-protocol",
"codex-tools",
"http 1.4.0",
"pretty_assertions",
"schemars 0.8.22",
"serde_json",
]
[[package]]
name = "codex-windows-sandbox"
version = "0.0.0"
@@ -4129,9 +4160,9 @@ dependencies = [
"serde_json",
"tempfile",
"tokio",
"tracing-appender",
"windows 0.58.0",
"windows-sys 0.52.0",
"winres",
]
[[package]]
@@ -4206,6 +4237,12 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "condtype"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf0a07a401f374238ab8e2f11a104d2851bf9ce711ec69804834de8af45c7af"
[[package]]
name = "console"
version = "0.15.11"
@@ -5202,6 +5239,31 @@ dependencies = [
"syn 2.0.114",
]
[[package]]
name = "divan"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a405457ec78b8fe08b0e32b4a3570ab5dff6dd16eb9e76a5ee0a9d9cbd898933"
dependencies = [
"cfg-if",
"clap",
"condtype",
"divan-macros",
"libc",
"regex-lite",
]
[[package]]
name = "divan-macros"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9556bc800956545d6420a640173e5ba7dfa82f38d3ea5a167eb555bc69ac3323"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "dns-lookup"
version = "3.0.1"
@@ -5475,7 +5537,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys 0.61.2",
]
[[package]]
@@ -8111,6 +8173,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "jsonptr"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5a3cc660ba5d72bce0b3bb295bf20847ccbb40fd423f3f05b61273672e561fe"
[[package]]
name = "jsonwebtoken"
version = "9.3.1"
@@ -14716,15 +14784,6 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "winres"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c"
dependencies = [
"toml 0.5.11",
]
[[package]]
name = "winsafe"
version = "0.0.19"

View File

@@ -48,6 +48,7 @@ members = [
"ext/goal",
"ext/guardian",
"ext/memories",
"ext/web-search",
"external-agent-migration",
"external-agent-sessions",
"keyring-store",
@@ -58,7 +59,6 @@ members = [
"login",
"codex-mcp",
"mcp-server",
"memories/mcp",
"memories/read",
"memories/write",
"model-provider-info",
@@ -181,6 +181,7 @@ codex-lmstudio = { path = "lmstudio" }
codex-login = { path = "login" }
codex-message-history = { path = "message-history" }
codex-memories-extension = { path = "ext/memories" }
codex-web-search-extension = { path = "ext/web-search" }
codex-memories-read = { path = "memories/read" }
codex-memories-write = { path = "memories/write" }
codex-mcp = { path = "codex-mcp" }
@@ -275,6 +276,7 @@ deno_core_icudata = "0.77.0"
derive_more = "2"
diffy = "0.4.2"
dirs = "6"
divan = "0.1.21"
dns-lookup = "3.0.1"
dotenvy = "0.15.7"
dunce = "1.0.4"
@@ -301,6 +303,7 @@ indexmap = "2.12.0"
insta = "1.46.3"
inventory = "0.3.19"
itertools = "0.14.0"
jsonptr = { version = "0.7.1", default-features = false }
jsonwebtoken = "9.3.1"
keyring = { version = "3.6", default-features = false }
landlock = "0.4.4"

View File

@@ -55,25 +55,20 @@ Use `codex exec --ephemeral ...` to run without persisting session rollout files
### Experimenting with the Codex Sandbox
To test to see what happens when a command is run under the sandbox provided by Codex, we provide the following subcommands in Codex CLI:
To test to see what happens when a command is run under the sandbox provided by Codex, use the `sandbox` subcommand in Codex CLI:
```
# macOS
codex sandbox macos [--log-denials] [COMMAND]...
# Uses the sandbox implementation for the current host OS:
# Seatbelt on macOS, the Linux sandbox on Linux, and Windows restricted token on Windows.
codex sandbox [COMMAND]...
# Linux
codex sandbox linux [COMMAND]...
# Windows
codex sandbox windows [COMMAND]...
# Legacy aliases
codex debug seatbelt [--log-denials] [COMMAND]...
codex debug landlock [COMMAND]...
# macOS-only diagnostic option
codex sandbox --log-denials [COMMAND]...
```
To try a writable legacy sandbox mode with these commands, pass an explicit config override such
as `-c 'sandbox_mode="workspace-write"'`.
`codex sandbox` also accepts `--profile NAME` (`-p NAME`) to layer
`$CODEX_HOME/NAME.config.toml` onto the base user config for the sandboxed
command.
### Selecting a sandbox policy via `--sandbox`
@@ -90,7 +85,6 @@ codex --sandbox workspace-write
codex --sandbox danger-full-access
```
The same setting can be persisted in `~/.codex/config.toml` via the top-level `sandbox_mode = "MODE"` key, e.g. `sandbox_mode = "workspace-write"`.
In `workspace-write`, Codex also includes `~/.codex/memories` in its writable roots so memory maintenance does not require an extra approval.
## Code Organization

View File

@@ -401,6 +401,7 @@ fn sample_turn_steer_request(
},
],
responsesapi_client_metadata: None,
additional_context: None,
},
}
}
@@ -1212,6 +1213,7 @@ fn compaction_event_serializes_expected_shape() {
completed_at: 106,
duration_ms: Some(6543),
},
"session-thread-1".to_string(),
sample_app_server_client_metadata(),
sample_runtime_metadata(),
Some(ThreadSource::User),
@@ -1228,6 +1230,7 @@ fn compaction_event_serializes_expected_shape() {
"event_type": "codex_compaction_event",
"event_params": {
"thread_id": "thread-1",
"session_id": "session-thread-1",
"turn_id": "turn-1",
"app_server_client": {
"product_client_id": DEFAULT_ORIGINATOR,
@@ -1262,6 +1265,14 @@ fn compaction_event_serializes_expected_shape() {
);
}
#[test]
fn compaction_implementation_serializes_remote_v2() {
let payload = serde_json::to_value(CompactionImplementation::ResponsesCompactionV2)
.expect("serialize compaction implementation");
assert_eq!(payload, json!("responses_compaction_v2"));
}
#[test]
fn app_used_dedupe_is_keyed_by_turn_and_connector() {
let (sender, _receiver) = mpsc::channel(1);
@@ -1298,6 +1309,7 @@ fn thread_initialized_event_serializes_expected_shape() {
event_type: "codex_thread_initialized",
event_params: ThreadInitializedEventParams {
thread_id: "thread-0".to_string(),
session_id: "session-thread-0".to_string(),
app_server_client: CodexAppServerClientMetadata {
product_client_id: DEFAULT_ORIGINATOR.to_string(),
client_name: Some("codex-tui".to_string()),
@@ -1329,6 +1341,7 @@ fn thread_initialized_event_serializes_expected_shape() {
"event_type": "codex_thread_initialized",
"event_params": {
"thread_id": "thread-0",
"session_id": "session-thread-0",
"app_server_client": {
"product_client_id": DEFAULT_ORIGINATOR,
"client_name": "codex-tui",
@@ -1596,6 +1609,7 @@ async fn initialize_caches_client_and_thread_lifecycle_publishes_once_initialize
let payload = serde_json::to_value(&events).expect("serialize events");
assert_eq!(payload.as_array().expect("events array").len(), 1);
assert_eq!(payload[0]["event_type"], "codex_thread_initialized");
assert_eq!(payload[0]["event_params"]["session_id"], "session-thread-1");
assert_eq!(
payload[0]["event_params"]["app_server_client"]["product_client_id"],
DEFAULT_ORIGINATOR
@@ -1772,6 +1786,7 @@ async fn compaction_event_ingests_custom_fact() {
let payload = serde_json::to_value(&events).expect("serialize events");
assert_eq!(payload.as_array().expect("events array").len(), 1);
assert_eq!(payload[0]["event_type"], "codex_compaction_event");
assert_eq!(payload[0]["event_params"]["session_id"], "session-thread-1");
assert_eq!(payload[0]["event_params"]["thread_id"], "thread-1");
assert_eq!(payload[0]["event_params"]["turn_id"], "turn-compact");
assert_eq!(
@@ -1896,6 +1911,10 @@ async fn guardian_review_event_ingests_custom_fact_with_optional_target_item() {
let payload = serde_json::to_value(&events).expect("serialize events");
assert_eq!(payload.as_array().expect("events array").len(), 1);
assert_eq!(payload[0]["event_type"], "codex_guardian_review");
assert_eq!(
payload[0]["event_params"]["session_id"],
"session-thread-guardian"
);
assert_eq!(payload[0]["event_params"]["thread_id"], "thread-guardian");
assert_eq!(payload[0]["event_params"]["turn_id"], "turn-guardian");
assert_eq!(payload[0]["event_params"]["review_id"], "review-guardian");
@@ -2388,6 +2407,7 @@ async fn item_review_summaries_do_not_cross_threads_with_reused_item_ids() {
fn subagent_thread_started_review_serializes_expected_shape() {
let event = TrackEventRequest::ThreadInitialized(subagent_thread_started_event_request(
SubAgentThreadStartedInput {
session_id: "session-root".to_string(),
thread_id: "thread-review".to_string(),
parent_thread_id: None,
product_client_id: "codex-tui".to_string(),
@@ -2431,6 +2451,7 @@ fn subagent_thread_started_thread_spawn_serializes_parent_thread_id() {
.expect("valid thread id");
let event = TrackEventRequest::ThreadInitialized(subagent_thread_started_event_request(
SubAgentThreadStartedInput {
session_id: "session-root".to_string(),
thread_id: "thread-spawn".to_string(),
parent_thread_id: None,
product_client_id: "codex-tui".to_string(),
@@ -2450,18 +2471,21 @@ fn subagent_thread_started_thread_spawn_serializes_parent_thread_id() {
));
let payload = serde_json::to_value(&event).expect("serialize thread spawn subagent event");
assert_eq!(payload["event_params"]["thread_id"], "thread-spawn");
assert_eq!(payload["event_params"]["thread_source"], "subagent");
assert_eq!(payload["event_params"]["subagent_source"], "thread_spawn");
assert_eq!(
payload["event_params"]["parent_thread_id"],
"11111111-1111-1111-1111-111111111111"
);
assert_eq!(payload["event_params"]["session_id"], "session-root");
}
#[test]
fn subagent_thread_started_memory_consolidation_serializes_expected_shape() {
let event = TrackEventRequest::ThreadInitialized(subagent_thread_started_event_request(
SubAgentThreadStartedInput {
session_id: "session-root".to_string(),
thread_id: "thread-memory".to_string(),
parent_thread_id: None,
product_client_id: "codex-tui".to_string(),
@@ -2487,6 +2511,7 @@ fn subagent_thread_started_memory_consolidation_serializes_expected_shape() {
fn subagent_thread_started_other_serializes_expected_shape() {
let event = TrackEventRequest::ThreadInitialized(subagent_thread_started_event_request(
SubAgentThreadStartedInput {
session_id: "session-root".to_string(),
thread_id: "thread-guardian".to_string(),
parent_thread_id: None,
product_client_id: "codex-tui".to_string(),
@@ -2508,6 +2533,7 @@ fn subagent_thread_started_other_serializes_expected_shape() {
fn subagent_thread_started_other_serializes_explicit_parent_thread_id() {
let event = TrackEventRequest::ThreadInitialized(subagent_thread_started_event_request(
SubAgentThreadStartedInput {
session_id: "session-root".to_string(),
thread_id: "thread-guardian".to_string(),
parent_thread_id: Some("parent-thread-guardian".to_string()),
product_client_id: "codex-tui".to_string(),
@@ -2537,6 +2563,7 @@ async fn subagent_thread_started_publishes_without_initialize() {
.ingest(
AnalyticsFact::Custom(CustomAnalyticsFact::SubAgentThreadStarted(
SubAgentThreadStartedInput {
session_id: "session-root".to_string(),
thread_id: "thread-review".to_string(),
parent_thread_id: None,
product_client_id: "codex-tui".to_string(),
@@ -2610,6 +2637,7 @@ async fn subagent_thread_started_inherits_parent_connection_for_new_thread() {
.ingest(
AnalyticsFact::Custom(CustomAnalyticsFact::SubAgentThreadStarted(
SubAgentThreadStartedInput {
session_id: "session-root".to_string(),
thread_id: "thread-review".to_string(),
parent_thread_id: None,
product_client_id: "parent-client".to_string(),
@@ -2657,6 +2685,8 @@ async fn subagent_thread_started_inherits_parent_connection_for_new_thread() {
.await;
let payload = serde_json::to_value(&events).expect("serialize events");
assert_eq!(payload[0]["event_params"]["session_id"], "session-root");
assert_eq!(payload[0]["event_params"]["thread_id"], "thread-review");
assert_eq!(
payload[0]["event_params"]["app_server_client"]["product_client_id"],
"parent-client"
@@ -2677,6 +2707,7 @@ async fn subagent_tool_items_inherit_parent_connection_metadata() {
.ingest(
AnalyticsFact::Custom(CustomAnalyticsFact::SubAgentThreadStarted(
SubAgentThreadStartedInput {
session_id: "session-root".to_string(),
thread_id: "thread-subagent".to_string(),
parent_thread_id: Some("thread-1".to_string()),
product_client_id: "codex-tui".to_string(),
@@ -3182,6 +3213,7 @@ fn turn_event_serializes_expected_shape() {
event_type: "codex_turn_event",
event_params: crate::events::CodexTurnEventParams {
thread_id: "thread-2".to_string(),
session_id: "session-thread-2".to_string(),
turn_id: "turn-2".to_string(),
app_server_client: sample_app_server_client_metadata(),
runtime: sample_runtime_metadata(),
@@ -3232,6 +3264,7 @@ fn turn_event_serializes_expected_shape() {
"event_type": "codex_turn_event",
"event_params": {
"thread_id": "thread-2",
"session_id": "session-thread-2",
"turn_id": "turn-2",
"submission_type": null,
"app_server_client": {
@@ -3333,6 +3366,10 @@ async fn accepted_turn_steer_emits_expected_event() {
let payload = serde_json::to_value(&out[0]).expect("serialize turn steer event");
assert_eq!(payload["event_type"], json!("codex_turn_steer_event"));
assert_eq!(payload["event_params"]["thread_id"], json!("thread-2"));
assert_eq!(
payload["event_params"]["session_id"],
json!("session-thread-2")
);
assert_eq!(payload["event_params"]["expected_turn_id"], json!("turn-2"));
assert_eq!(payload["event_params"]["accepted_turn_id"], json!("turn-2"));
assert_eq!(payload["event_params"]["num_input_images"], json!(1));
@@ -3550,6 +3587,10 @@ async fn turn_lifecycle_emits_turn_event() {
let payload = serde_json::to_value(&out[0]).expect("serialize turn event");
assert_eq!(payload["event_type"], json!("codex_turn_event"));
assert_eq!(payload["event_params"]["thread_id"], json!("thread-2"));
assert_eq!(
payload["event_params"]["session_id"],
json!("session-thread-2")
);
assert_eq!(payload["event_params"]["turn_id"], json!("turn-2"));
assert_eq!(
payload["event_params"]["app_server_client"],
@@ -3628,6 +3669,7 @@ async fn turn_event_counts_completed_tool_items() {
status: McpToolCallStatus::Completed,
arguments: json!({}),
mcp_app_resource_uri: None,
plugin_id: None,
result: None,
error: None,
duration_ms: Some(2),

View File

@@ -103,6 +103,7 @@ fn sample_turn_steer_request() -> ClientRequest {
expected_turn_id: "turn-1".to_string(),
input: Vec::new(),
responsesapi_client_metadata: None,
additional_context: None,
},
}
}

View File

@@ -147,6 +147,7 @@ pub(crate) struct CodexRuntimeMetadata {
#[derive(Serialize)]
pub(crate) struct ThreadInitializedEventParams {
pub(crate) thread_id: String,
pub(crate) session_id: String,
pub(crate) app_server_client: CodexAppServerClientMetadata,
pub(crate) runtime: CodexRuntimeMetadata,
pub(crate) model: String,
@@ -420,6 +421,7 @@ impl GuardianReviewAnalyticsResult {
#[derive(Serialize)]
pub(crate) struct GuardianReviewEventPayload {
pub(crate) session_id: String,
pub(crate) app_server_client: CodexAppServerClientMetadata,
pub(crate) runtime: CodexRuntimeMetadata,
#[serde(flatten)]
@@ -738,6 +740,7 @@ pub(crate) struct CodexHookRunEventRequest {
#[derive(Serialize)]
pub(crate) struct CodexCompactionEventParams {
pub(crate) thread_id: String,
pub(crate) session_id: String,
pub(crate) turn_id: String,
pub(crate) app_server_client: CodexAppServerClientMetadata,
pub(crate) runtime: CodexRuntimeMetadata,
@@ -767,6 +770,7 @@ pub(crate) struct CodexCompactionEventRequest {
#[derive(Serialize)]
pub(crate) struct CodexTurnEventParams {
pub(crate) thread_id: String,
pub(crate) session_id: String,
pub(crate) turn_id: String,
// TODO(rhan-oai): Populate once queued/default submission type is plumbed from
// the turn/start callsites instead of always being reported as None.
@@ -821,6 +825,7 @@ pub(crate) struct CodexTurnEventRequest {
#[derive(Serialize)]
pub(crate) struct CodexTurnSteerEventParams {
pub(crate) thread_id: String,
pub(crate) session_id: String,
pub(crate) expected_turn_id: Option<String>,
pub(crate) accepted_turn_id: Option<String>,
pub(crate) app_server_client: CodexAppServerClientMetadata,
@@ -926,6 +931,7 @@ pub(crate) fn codex_plugin_metadata(plugin: PluginTelemetryMetadata) -> CodexPlu
pub(crate) fn codex_compaction_event_params(
input: CodexCompactionEvent,
session_id: String,
app_server_client: CodexAppServerClientMetadata,
runtime: CodexRuntimeMetadata,
thread_source: Option<ThreadSource>,
@@ -934,6 +940,7 @@ pub(crate) fn codex_compaction_event_params(
) -> CodexCompactionEventParams {
CodexCompactionEventParams {
thread_id: input.thread_id,
session_id,
turn_id: input.turn_id,
app_server_client,
runtime,
@@ -991,6 +998,7 @@ fn analytics_hook_event_name(event_name: HookEventName) -> &'static str {
HookEventName::SessionStart => "SessionStart",
HookEventName::UserPromptSubmit => "UserPromptSubmit",
HookEventName::SubagentStart => "SubagentStart",
HookEventName::SubagentStop => "SubagentStop",
HookEventName::Stop => "Stop",
}
}
@@ -1025,6 +1033,7 @@ pub(crate) fn subagent_thread_started_event_request(
) -> ThreadInitializedEvent {
let event_params = ThreadInitializedEventParams {
thread_id: input.thread_id,
session_id: input.session_id,
app_server_client: CodexAppServerClientMetadata {
product_client_id: input.product_client_id,
client_name: Some(input.client_name),

View File

@@ -199,6 +199,7 @@ pub struct AppInvocation {
#[derive(Clone)]
pub struct SubAgentThreadStartedInput {
pub session_id: String,
pub thread_id: String,
pub parent_thread_id: Option<String>,
pub product_client_id: String,
@@ -229,6 +230,7 @@ pub enum CompactionReason {
#[serde(rename_all = "snake_case")]
pub enum CompactionImplementation {
Responses,
ResponsesCompactionV2,
ResponsesCompact,
}

View File

@@ -255,6 +255,7 @@ struct ItemReviewSummary {
#[derive(Clone)]
struct ThreadMetadataState {
session_id: String,
thread_source: Option<ThreadSource>,
initialization_mode: ThreadInitializationMode,
subagent_source: Option<String>,
@@ -263,6 +264,7 @@ struct ThreadMetadataState {
impl ThreadMetadataState {
fn from_thread_metadata(
session_id: String,
session_source: &SessionSource,
thread_source: Option<ThreadSource>,
initialization_mode: ThreadInitializationMode,
@@ -281,6 +283,7 @@ impl ThreadMetadataState {
| SessionSource::Unknown => (None, None),
};
Self {
session_id,
thread_source,
initialization_mode,
subagent_source,
@@ -525,6 +528,7 @@ impl AnalyticsReducer {
thread_state
.metadata
.get_or_insert_with(|| ThreadMetadataState {
session_id: input.session_id.clone(),
thread_source: Some(ThreadSource::Subagent),
initialization_mode: ThreadInitializationMode::New,
subagent_source: Some(subagent_source_name(&input.subagent_source)),
@@ -543,8 +547,8 @@ impl AnalyticsReducer {
input: GuardianReviewEventParams,
out: &mut Vec<TrackEventRequest>,
) {
let Some(connection_state) =
self.thread_connection_or_warn(AnalyticsDropSite::guardian(&input))
let Some((connection_state, thread_metadata)) =
self.thread_context_or_warn(AnalyticsDropSite::guardian(&input))
else {
return;
};
@@ -552,6 +556,7 @@ impl AnalyticsReducer {
GuardianReviewEventRequest {
event_type: "codex_guardian_review",
event_params: GuardianReviewEventPayload {
session_id: thread_metadata.session_id.clone(),
app_server_client: connection_state.app_server_client.clone(),
runtime: connection_state.runtime.clone(),
guardian_review: input,
@@ -1231,11 +1236,13 @@ impl AnalyticsReducer {
out: &mut Vec<TrackEventRequest>,
) {
let session_source: SessionSource = thread.source.into();
let session_id = thread.session_id;
let thread_id = thread.id;
let Some(connection_state) = self.connections.get(&connection_id) else {
return;
};
let thread_metadata = ThreadMetadataState::from_thread_metadata(
session_id.clone(),
&session_source,
thread.thread_source.map(Into::into),
initialization_mode,
@@ -1252,6 +1259,7 @@ impl AnalyticsReducer {
event_type: "codex_thread_initialized",
event_params: ThreadInitializedEventParams {
thread_id,
session_id,
app_server_client: connection_state.app_server_client.clone(),
runtime: connection_state.runtime.clone(),
model,
@@ -1277,6 +1285,7 @@ impl AnalyticsReducer {
event_type: "codex_compaction_event",
event_params: codex_compaction_event_params(
input,
thread_metadata.session_id.clone(),
connection_state.app_server_client.clone(),
connection_state.runtime.clone(),
thread_metadata.thread_source,
@@ -1379,6 +1388,7 @@ impl AnalyticsReducer {
event_type: "codex_turn_steer_event",
event_params: CodexTurnSteerEventParams {
thread_id: pending_request.thread_id,
session_id: thread_metadata.session_id.clone(),
expected_turn_id: Some(pending_request.expected_turn_id),
accepted_turn_id,
app_server_client: connection_state.app_server_client.clone(),
@@ -2447,6 +2457,7 @@ fn codex_turn_event_params(
let token_usage = turn_state.token_usage.clone();
CodexTurnEventParams {
thread_id,
session_id: thread_metadata.session_id.clone(),
turn_id,
app_server_client,
runtime,

View File

@@ -176,6 +176,7 @@ pub(crate) fn server_notification_requires_delivery(notification: &ServerNotific
matches!(
notification,
ServerNotification::TurnCompleted(_)
| ServerNotification::ThreadSettingsUpdated(_)
| ServerNotification::ItemCompleted(_)
| ServerNotification::AgentMessageDelta(_)
| ServerNotification::PlanDelta(_)
@@ -1121,7 +1122,9 @@ mod tests {
websocket,
JSONRPCMessage::Response(JSONRPCResponse {
id: request.id,
result: serde_json::json!({}),
result: serde_json::json!({
"userAgent": "codex_cli_rs/9.8.7-test (Test OS; x86_64) rust",
}),
}),
)
.await;
@@ -1456,6 +1459,7 @@ mod tests {
.await
.expect("remote client should connect");
assert_eq!(client.server_version(), Some("9.8.7-test"));
let response: GetAccountResponse = client
.request_typed(ClientRequest::GetAccount {
request_id: RequestId::Integer(1),

View File

@@ -150,6 +150,7 @@ pub struct RemoteAppServerClient {
command_tx: mpsc::Sender<RemoteClientCommand>,
event_rx: mpsc::UnboundedReceiver<AppServerEvent>,
pending_events: VecDeque<AppServerEvent>,
server_version: Option<String>,
worker_handle: tokio::task::JoinHandle<()>,
}
@@ -180,6 +181,10 @@ impl RemoteAppServerClient {
}
}
pub fn server_version(&self) -> Option<&str> {
self.server_version.as_deref()
}
async fn connect_with_stream<S>(
channel_capacity: usize,
endpoint: String,
@@ -190,7 +195,7 @@ impl RemoteAppServerClient {
S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
{
let mut stream = stream;
let pending_events = initialize_remote_connection(
let (pending_events, server_version) = initialize_remote_connection(
&mut stream,
&endpoint,
initialize_params,
@@ -466,6 +471,7 @@ impl RemoteAppServerClient {
command_tx,
event_rx,
pending_events: pending_events.into(),
server_version,
worker_handle,
})
}
@@ -606,6 +612,7 @@ impl RemoteAppServerClient {
command_tx,
event_rx,
pending_events: _pending_events,
server_version: _server_version,
worker_handle,
} = self;
let mut worker_handle = worker_handle;
@@ -793,12 +800,13 @@ async fn initialize_remote_connection<S>(
endpoint: &str,
params: InitializeParams,
initialize_timeout: Duration,
) -> IoResult<Vec<AppServerEvent>>
) -> IoResult<(Vec<AppServerEvent>, Option<String>)>
where
S: AsyncRead + AsyncWrite + Unpin,
{
let initialize_request_id = RequestId::String("initialize".to_string());
let mut pending_events = Vec::new();
let mut server_version = None;
write_jsonrpc_message(
stream,
JSONRPCMessage::Request(jsonrpc_request_from_client_request(
@@ -822,6 +830,14 @@ where
})?;
match message {
JSONRPCMessage::Response(response) if response.id == initialize_request_id => {
server_version = response
.result
.get("userAgent")
.and_then(serde_json::Value::as_str)
.and_then(|user_agent| {
let (_, rest) = user_agent.split_once('/')?;
rest.split_whitespace().next().map(str::to_string)
});
break Ok(());
}
JSONRPCMessage::Error(error) if error.id == initialize_request_id => {
@@ -913,7 +929,7 @@ where
)
.await?;
Ok(pending_events)
Ok((pending_events, server_version))
}
fn app_server_event_from_notification(notification: JSONRPCNotification) -> Option<AppServerEvent> {
@@ -1007,6 +1023,7 @@ mod tests {
command_tx,
event_rx,
pending_events: VecDeque::new(),
server_version: None,
worker_handle,
};

View File

@@ -74,6 +74,12 @@ pub struct BootstrapOptions {
pub remote_control_enabled: bool,
}
/// Passively probes an existing app-server socket and returns its reported
/// app-server version.
pub async fn probe_app_server_version(socket_path: &Path) -> Result<String> {
Ok(client::probe(socket_path).await?.app_server_version)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum BootstrapStatus {

View File

@@ -12,6 +12,28 @@
],
"type": "string"
},
"AdditionalContextEntry": {
"properties": {
"kind": {
"$ref": "#/definitions/AdditionalContextKind"
},
"value": {
"type": "string"
}
},
"required": [
"kind",
"value"
],
"type": "object"
},
"AdditionalContextKind": {
"enum": [
"untrusted",
"application"
],
"type": "string"
},
"ApprovalsReviewer": {
"description": "Configures who approval requests are routed to for review. Examples include sandbox escapes, blocked network access, MCP approval prompts, and ARC escalations. Defaults to `user`. `auto_review` uses a carefully prompted subagent to gather relevant context and apply a risk-based decision framework before approving or denying the request. The legacy value `guardian_subagent` is accepted for compatibility.",
"enum": [
@@ -423,7 +445,6 @@
]
},
"includeLayers": {
"default": false,
"type": "boolean"
}
},
@@ -721,8 +742,7 @@
}
},
"required": [
"classification",
"includeLogs"
"classification"
],
"type": "object"
},
@@ -984,6 +1004,26 @@
],
"title": "InputImageFunctionCallOutputContentItem",
"type": "object"
},
{
"properties": {
"encrypted_content": {
"type": "string"
},
"type": {
"enum": [
"encrypted_content"
],
"title": "EncryptedContentFunctionCallOutputContentItemType",
"type": "string"
}
},
"required": [
"encrypted_content",
"type"
],
"title": "EncryptedContentFunctionCallOutputContentItem",
"type": "object"
}
]
},
@@ -1014,7 +1054,6 @@
"GetAccountParams": {
"properties": {
"refreshToken": {
"default": false,
"description": "When `true`, requests a proactive token refresh before returning.\n\nIn managed auth mode this triggers the normal refresh-token flow. In external auth mode this flag is ignored. Clients should refresh tokens themselves and call `account/login/start` with `chatgptAuthTokens`.",
"type": "boolean"
}
@@ -1046,6 +1085,8 @@
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],
@@ -1126,6 +1167,12 @@
"integer",
"null"
]
},
"threadId": {
"type": [
"string",
"null"
]
}
},
"type": "object"
@@ -1541,6 +1588,34 @@
],
"type": "string"
},
"PermissionProfileListParams": {
"properties": {
"cursor": {
"description": "Opaque pagination cursor returned by a previous call.",
"type": [
"string",
"null"
]
},
"cwd": {
"description": "Optional working directory to resolve project config layers.",
"type": [
"string",
"null"
]
},
"limit": {
"description": "Optional page size; defaults to the full result set.",
"format": "uint32",
"minimum": 0.0,
"type": [
"integer",
"null"
]
}
},
"type": "object"
},
"Personality": {
"enum": [
"none",
@@ -1604,6 +1679,7 @@
"PluginListMarketplaceKind": {
"enum": [
"local",
"vertical",
"workspace-directory",
"shared-with-me"
],
@@ -3107,6 +3183,62 @@
],
"type": "object"
},
"ThreadGoalClearParams": {
"properties": {
"threadId": {
"type": "string"
}
},
"required": [
"threadId"
],
"type": "object"
},
"ThreadGoalGetParams": {
"properties": {
"threadId": {
"type": "string"
}
},
"required": [
"threadId"
],
"type": "object"
},
"ThreadGoalSetParams": {
"properties": {
"objective": {
"type": [
"string",
"null"
]
},
"status": {
"anyOf": [
{
"$ref": "#/definitions/ThreadGoalStatus"
},
{
"type": "null"
}
]
},
"threadId": {
"type": "string"
},
"tokenBudget": {
"format": "int64",
"type": [
"integer",
"null"
]
}
},
"required": [
"threadId"
],
"type": "object"
},
"ThreadGoalStatus": {
"enum": [
"active",
@@ -3319,7 +3451,6 @@
"ThreadReadParams": {
"properties": {
"includeTurns": {
"default": false,
"description": "When true, include turns and their items from rollout history.",
"type": "boolean"
},
@@ -4267,6 +4398,78 @@
"title": "Thread/name/setRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/RequestId"
},
"method": {
"enum": [
"thread/goal/set"
],
"title": "Thread/goal/setRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadGoalSetParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "Thread/goal/setRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/RequestId"
},
"method": {
"enum": [
"thread/goal/get"
],
"title": "Thread/goal/getRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadGoalGetParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "Thread/goal/getRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/RequestId"
},
"method": {
"enum": [
"thread/goal/clear"
],
"title": "Thread/goal/clearRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadGoalClearParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "Thread/goal/clearRequest",
"type": "object"
},
{
"properties": {
"id": {
@@ -5324,6 +5527,30 @@
"title": "ExperimentalFeature/listRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/RequestId"
},
"method": {
"enum": [
"permissionProfile/list"
],
"title": "PermissionProfile/listRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/PermissionProfileListParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "PermissionProfile/listRequest",
"type": "object"
},
{
"properties": {
"id": {

View File

@@ -64,6 +64,26 @@
},
"type": "object"
},
"ActivePermissionProfile": {
"properties": {
"extends": {
"default": null,
"description": "Parent profile identifier from the selected permissions profile's `extends` setting, when present.",
"type": [
"string",
"null"
]
},
"id": {
"description": "Identifier from `default_permissions` or the implicit built-in default, such as `:workspace` or a user-defined `[permissions.<id>]` profile.",
"type": "string"
}
},
"required": [
"id"
],
"type": "object"
},
"AdditionalFileSystemPermissions": {
"properties": {
"entries": {
@@ -415,6 +435,65 @@
],
"type": "object"
},
"ApprovalsReviewer": {
"description": "Configures who approval requests are routed to for review. Examples include sandbox escapes, blocked network access, MCP approval prompts, and ARC escalations. Defaults to `user`. `auto_review` uses a carefully prompted subagent to gather relevant context and apply a risk-based decision framework before approving or denying the request. The legacy value `guardian_subagent` is accepted for compatibility.",
"enum": [
"user",
"auto_review",
"guardian_subagent"
],
"type": "string"
},
"AskForApproval": {
"oneOf": [
{
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"granular": {
"properties": {
"mcp_elicitations": {
"type": "boolean"
},
"request_permissions": {
"default": false,
"type": "boolean"
},
"rules": {
"type": "boolean"
},
"sandbox_approval": {
"type": "boolean"
},
"skill_approval": {
"default": false,
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"granular"
],
"title": "GranularAskForApproval",
"type": "object"
}
]
},
"AuthMode": {
"description": "Authentication mode for OpenAI-backed providers.",
"oneOf": [
@@ -658,6 +737,22 @@
],
"type": "string"
},
"CollaborationMode": {
"description": "Collaboration mode for a Codex session.",
"properties": {
"mode": {
"$ref": "#/definitions/ModeKind"
},
"settings": {
"$ref": "#/definitions/Settings"
}
},
"required": [
"mode",
"settings"
],
"type": "object"
},
"CommandAction": {
"oneOf": [
{
@@ -1741,6 +1836,7 @@
"sessionStart",
"userPromptSubmit",
"subagentStart",
"subagentStop",
"stop"
],
"type": "string"
@@ -1935,6 +2031,8 @@
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],
@@ -2258,6 +2356,14 @@
}
]
},
"ModeKind": {
"description": "Initial collaboration mode to use when the TUI starts.",
"enum": [
"plan",
"default"
],
"type": "string"
},
"ModelRerouteReason": {
"enum": [
"highRiskCyberActivity"
@@ -2319,6 +2425,13 @@
],
"type": "object"
},
"NetworkAccess": {
"enum": [
"restricted",
"enabled"
],
"type": "string"
},
"NetworkApprovalProtocol": {
"enum": [
"http",
@@ -2402,6 +2515,14 @@
}
]
},
"Personality": {
"enum": [
"none",
"friendly",
"pragmatic"
],
"type": "string"
},
"PlanDeltaNotification": {
"description": "EXPERIMENTAL - proposed plan streaming deltas for plan items. Clients should not assume concatenated deltas match the completed plan item content.",
"properties": {
@@ -2655,6 +2776,26 @@
],
"type": "string"
},
"ReasoningSummary": {
"description": "A summary of the reasoning performed by the model. This can be useful for debugging and understanding the model's reasoning process. See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#reasoning-summaries",
"oneOf": [
{
"enum": [
"auto",
"concise",
"detailed"
],
"type": "string"
},
{
"description": "Option to disable reasoning summaries.",
"enum": [
"none"
],
"type": "string"
}
]
},
"ReasoningSummaryPartAddedNotification": {
"properties": {
"itemId": {
@@ -2807,6 +2948,105 @@
},
"type": "object"
},
"SandboxPolicy": {
"oneOf": [
{
"properties": {
"type": {
"enum": [
"dangerFullAccess"
],
"title": "DangerFullAccessSandboxPolicyType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DangerFullAccessSandboxPolicy",
"type": "object"
},
{
"properties": {
"networkAccess": {
"default": false,
"type": "boolean"
},
"type": {
"enum": [
"readOnly"
],
"title": "ReadOnlySandboxPolicyType",
"type": "string"
}
},
"required": [
"type"
],
"title": "ReadOnlySandboxPolicy",
"type": "object"
},
{
"properties": {
"networkAccess": {
"allOf": [
{
"$ref": "#/definitions/NetworkAccess"
}
],
"default": "restricted"
},
"type": {
"enum": [
"externalSandbox"
],
"title": "ExternalSandboxSandboxPolicyType",
"type": "string"
}
},
"required": [
"type"
],
"title": "ExternalSandboxSandboxPolicy",
"type": "object"
},
{
"properties": {
"excludeSlashTmp": {
"default": false,
"type": "boolean"
},
"excludeTmpdirEnvVar": {
"default": false,
"type": "boolean"
},
"networkAccess": {
"default": false,
"type": "boolean"
},
"type": {
"enum": [
"workspaceWrite"
],
"title": "WorkspaceWriteSandboxPolicyType",
"type": "string"
},
"writableRoots": {
"default": [],
"items": {
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": "array"
}
},
"required": [
"type"
],
"title": "WorkspaceWriteSandboxPolicy",
"type": "object"
}
]
},
"ServerRequestResolvedNotification": {
"properties": {
"requestId": {
@@ -2862,6 +3102,34 @@
}
]
},
"Settings": {
"description": "Settings for a collaboration mode.",
"properties": {
"developer_instructions": {
"type": [
"string",
"null"
]
},
"model": {
"type": "string"
},
"reasoning_effort": {
"anyOf": [
{
"$ref": "#/definitions/ReasoningEffort"
},
{
"type": "null"
}
]
}
},
"required": [
"model"
],
"type": "object"
},
"SkillsChangedNotification": {
"description": "Notification emitted when watched local skill files change.\n\nTreat this as an invalidation signal and re-run `skills/list` with the client's current parameters when refreshed skill metadata is needed.",
"type": "object"
@@ -3595,6 +3863,12 @@
"null"
]
},
"pluginId": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
@@ -4148,6 +4422,102 @@
],
"type": "object"
},
"ThreadSettings": {
"properties": {
"activePermissionProfile": {
"anyOf": [
{
"$ref": "#/definitions/ActivePermissionProfile"
},
{
"type": "null"
}
]
},
"approvalPolicy": {
"$ref": "#/definitions/AskForApproval"
},
"approvalsReviewer": {
"$ref": "#/definitions/ApprovalsReviewer"
},
"collaborationMode": {
"$ref": "#/definitions/CollaborationMode"
},
"cwd": {
"$ref": "#/definitions/AbsolutePathBuf"
},
"effort": {
"anyOf": [
{
"$ref": "#/definitions/ReasoningEffort"
},
{
"type": "null"
}
]
},
"model": {
"type": "string"
},
"modelProvider": {
"type": "string"
},
"personality": {
"anyOf": [
{
"$ref": "#/definitions/Personality"
},
{
"type": "null"
}
]
},
"sandboxPolicy": {
"$ref": "#/definitions/SandboxPolicy"
},
"serviceTier": {
"type": [
"string",
"null"
]
},
"summary": {
"anyOf": [
{
"$ref": "#/definitions/ReasoningSummary"
},
{
"type": "null"
}
]
}
},
"required": [
"approvalPolicy",
"approvalsReviewer",
"collaborationMode",
"cwd",
"model",
"modelProvider",
"sandboxPolicy"
],
"type": "object"
},
"ThreadSettingsUpdatedNotification": {
"properties": {
"threadId": {
"type": "string"
},
"threadSettings": {
"$ref": "#/definitions/ThreadSettings"
}
},
"required": [
"threadId",
"threadSettings"
],
"type": "object"
},
"ThreadSource": {
"enum": [
"user",
@@ -5089,6 +5459,26 @@
"title": "Thread/goal/clearedNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"thread/settings/updated"
],
"title": "Thread/settings/updatedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadSettingsUpdatedNotification"
}
},
"required": [
"method",
"params"
],
"title": "Thread/settings/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {

View File

@@ -372,6 +372,78 @@
"title": "Thread/name/setRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/v2/RequestId"
},
"method": {
"enum": [
"thread/goal/set"
],
"title": "Thread/goal/setRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/v2/ThreadGoalSetParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "Thread/goal/setRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/v2/RequestId"
},
"method": {
"enum": [
"thread/goal/get"
],
"title": "Thread/goal/getRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/v2/ThreadGoalGetParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "Thread/goal/getRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/v2/RequestId"
},
"method": {
"enum": [
"thread/goal/clear"
],
"title": "Thread/goal/clearRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/v2/ThreadGoalClearParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "Thread/goal/clearRequest",
"type": "object"
},
{
"properties": {
"id": {
@@ -1429,6 +1501,30 @@
"title": "ExperimentalFeature/listRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/v2/RequestId"
},
"method": {
"enum": [
"permissionProfile/list"
],
"title": "PermissionProfile/listRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/v2/PermissionProfileListParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "PermissionProfile/listRequest",
"type": "object"
},
{
"properties": {
"id": {
@@ -4031,6 +4127,26 @@
"title": "Thread/goal/clearedNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"thread/settings/updated"
],
"title": "Thread/settings/updatedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/v2/ThreadSettingsUpdatedNotification"
}
},
"required": [
"method",
"params"
],
"title": "Thread/settings/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
@@ -5622,7 +5738,7 @@
"properties": {
"extends": {
"default": null,
"description": "Parent profile identifier once permissions profiles support inheritance. This is currently always `null`.",
"description": "Parent profile identifier from the selected permissions profile's `extends` setting, when present.",
"type": [
"string",
"null"
@@ -5652,6 +5768,28 @@
],
"type": "string"
},
"AdditionalContextEntry": {
"properties": {
"kind": {
"$ref": "#/definitions/v2/AdditionalContextKind"
},
"value": {
"type": "string"
}
},
"required": [
"kind",
"value"
],
"type": "object"
},
"AdditionalContextKind": {
"enum": [
"untrusted",
"application"
],
"type": "string"
},
"AdditionalFileSystemPermissions": {
"properties": {
"entries": {
@@ -7245,19 +7383,6 @@
}
]
},
"profile": {
"type": [
"string",
"null"
]
},
"profiles": {
"additionalProperties": {
"$ref": "#/definitions/v2/ProfileV2"
},
"default": {},
"type": "object"
},
"review_model": {
"type": [
"string",
@@ -7575,7 +7700,6 @@
]
},
"includeLayers": {
"default": false,
"type": "boolean"
}
},
@@ -7613,6 +7737,12 @@
},
"ConfigRequirements": {
"properties": {
"allowAppshots": {
"type": [
"boolean",
"null"
]
},
"allowManagedHooksOnly": {
"type": [
"boolean",
@@ -7628,6 +7758,15 @@
"null"
]
},
"allowedPermissions": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
},
"allowedSandboxModes": {
"items": {
"$ref": "#/definitions/v2/SandboxMode"
@@ -8468,8 +8607,7 @@
}
},
"required": [
"classification",
"includeLogs"
"classification"
],
"title": "FeedbackUploadParams",
"type": "object"
@@ -9218,6 +9356,26 @@
],
"title": "InputImageFunctionCallOutputContentItem",
"type": "object"
},
{
"properties": {
"encrypted_content": {
"type": "string"
},
"type": {
"enum": [
"encrypted_content"
],
"title": "EncryptedContentFunctionCallOutputContentItemType",
"type": "string"
}
},
"required": [
"encrypted_content",
"type"
],
"title": "EncryptedContentFunctionCallOutputContentItem",
"type": "object"
}
]
},
@@ -9225,7 +9383,6 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"refreshToken": {
"default": false,
"description": "When `true`, requests a proactive token refresh before returning.\n\nIn managed auth mode this triggers the normal refresh-token flow. In external auth mode this flag is ignored. Clients should refresh tokens themselves and call `account/login/start` with `chatgptAuthTokens`.",
"type": "boolean"
}
@@ -9646,6 +9803,7 @@
"sessionStart",
"userPromptSubmit",
"subagentStart",
"subagentStop",
"stop"
],
"type": "string"
@@ -10002,6 +10160,8 @@
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],
@@ -10207,6 +10367,12 @@
"integer",
"null"
]
},
"threadId": {
"type": [
"string",
"null"
]
}
},
"title": "ListMcpServerStatusParams",
@@ -10542,6 +10708,12 @@
},
"type": "array"
},
"SubagentStop": {
"items": {
"$ref": "#/definitions/v2/ConfiguredHookMatcherGroup"
},
"type": "array"
},
"UserPromptSubmit": {
"items": {
"$ref": "#/definitions/v2/ConfiguredHookMatcherGroup"
@@ -10570,6 +10742,7 @@
"SessionStart",
"Stop",
"SubagentStart",
"SubagentStop",
"UserPromptSubmit"
],
"type": "object"
@@ -11202,6 +11375,14 @@
"defaultReasoningEffort": {
"$ref": "#/definitions/v2/ReasoningEffort"
},
"defaultServiceTier": {
"default": null,
"description": "Catalog default service tier id for this model, when one is configured.",
"type": [
"string",
"null"
]
},
"description": {
"type": "string"
},
@@ -11708,6 +11889,78 @@
}
]
},
"PermissionProfileListParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"cursor": {
"description": "Opaque pagination cursor returned by a previous call.",
"type": [
"string",
"null"
]
},
"cwd": {
"description": "Optional working directory to resolve project config layers.",
"type": [
"string",
"null"
]
},
"limit": {
"description": "Optional page size; defaults to the full result set.",
"format": "uint32",
"minimum": 0.0,
"type": [
"integer",
"null"
]
}
},
"title": "PermissionProfileListParams",
"type": "object"
},
"PermissionProfileListResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"data": {
"items": {
"$ref": "#/definitions/v2/PermissionProfileSummary"
},
"type": "array"
},
"nextCursor": {
"description": "Opaque cursor to pass to the next call to continue after the last item. If None, there are no more items to return.",
"type": [
"string",
"null"
]
}
},
"required": [
"data"
],
"title": "PermissionProfileListResponse",
"type": "object"
},
"PermissionProfileSummary": {
"properties": {
"description": {
"description": "Optional user-facing description for display in clients.",
"type": [
"string",
"null"
]
},
"id": {
"description": "Available permission profile identifier.",
"type": "string"
}
},
"required": [
"id"
],
"type": "object"
},
"Personality": {
"enum": [
"none",
@@ -12097,6 +12350,7 @@
"PluginListMarketplaceKind": {
"enum": [
"local",
"vertical",
"workspace-directory",
"shared-with-me"
],
@@ -12939,107 +13193,6 @@
],
"type": "object"
},
"ProfileV2": {
"additionalProperties": true,
"properties": {
"approval_policy": {
"anyOf": [
{
"$ref": "#/definitions/v2/AskForApproval"
},
{
"type": "null"
}
]
},
"approvals_reviewer": {
"anyOf": [
{
"$ref": "#/definitions/v2/ApprovalsReviewer"
},
{
"type": "null"
}
],
"description": "[UNSTABLE] Optional profile-level override for where approval requests are routed for review. If omitted, the enclosing config default is used."
},
"chatgpt_base_url": {
"type": [
"string",
"null"
]
},
"model": {
"type": [
"string",
"null"
]
},
"model_provider": {
"type": [
"string",
"null"
]
},
"model_reasoning_effort": {
"anyOf": [
{
"$ref": "#/definitions/v2/ReasoningEffort"
},
{
"type": "null"
}
]
},
"model_reasoning_summary": {
"anyOf": [
{
"$ref": "#/definitions/v2/ReasoningSummary"
},
{
"type": "null"
}
]
},
"model_verbosity": {
"anyOf": [
{
"$ref": "#/definitions/v2/Verbosity"
},
{
"type": "null"
}
]
},
"service_tier": {
"type": [
"string",
"null"
]
},
"tools": {
"anyOf": [
{
"$ref": "#/definitions/v2/ToolsV2"
},
{
"type": "null"
}
]
},
"web_search": {
"anyOf": [
{
"$ref": "#/definitions/v2/WebSearchMode"
},
{
"type": "null"
}
]
}
},
"type": "object"
},
"RateLimitReachedType": {
"enum": [
"rate_limit_reached",
@@ -15609,6 +15762,32 @@
],
"type": "object"
},
"ThreadGoalClearParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"threadId": {
"type": "string"
}
},
"required": [
"threadId"
],
"title": "ThreadGoalClearParams",
"type": "object"
},
"ThreadGoalClearResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"cleared": {
"type": "boolean"
}
},
"required": [
"cleared"
],
"title": "ThreadGoalClearResponse",
"type": "object"
},
"ThreadGoalClearedNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
@@ -15622,6 +15801,85 @@
"title": "ThreadGoalClearedNotification",
"type": "object"
},
"ThreadGoalGetParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"threadId": {
"type": "string"
}
},
"required": [
"threadId"
],
"title": "ThreadGoalGetParams",
"type": "object"
},
"ThreadGoalGetResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"goal": {
"anyOf": [
{
"$ref": "#/definitions/v2/ThreadGoal"
},
{
"type": "null"
}
]
}
},
"title": "ThreadGoalGetResponse",
"type": "object"
},
"ThreadGoalSetParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"objective": {
"type": [
"string",
"null"
]
},
"status": {
"anyOf": [
{
"$ref": "#/definitions/v2/ThreadGoalStatus"
},
{
"type": "null"
}
]
},
"threadId": {
"type": "string"
},
"tokenBudget": {
"format": "int64",
"type": [
"integer",
"null"
]
}
},
"required": [
"threadId"
],
"title": "ThreadGoalSetParams",
"type": "object"
},
"ThreadGoalSetResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"goal": {
"$ref": "#/definitions/v2/ThreadGoal"
}
},
"required": [
"goal"
],
"title": "ThreadGoalSetResponse",
"type": "object"
},
"ThreadGoalStatus": {
"enum": [
"active",
@@ -15989,6 +16247,12 @@
"null"
]
},
"pluginId": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
@@ -16620,7 +16884,6 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"includeTurns": {
"default": false,
"description": "When true, include turns and their items from rollout history.",
"type": "boolean"
},
@@ -17095,6 +17358,21 @@
"title": "ThreadRollbackResponse",
"type": "object"
},
"ThreadSearchResult": {
"properties": {
"snippet": {
"type": "string"
},
"thread": {
"$ref": "#/definitions/v2/Thread"
}
},
"required": [
"snippet",
"thread"
],
"type": "object"
},
"ThreadSetNameParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
@@ -17117,6 +17395,104 @@
"title": "ThreadSetNameResponse",
"type": "object"
},
"ThreadSettings": {
"properties": {
"activePermissionProfile": {
"anyOf": [
{
"$ref": "#/definitions/v2/ActivePermissionProfile"
},
{
"type": "null"
}
]
},
"approvalPolicy": {
"$ref": "#/definitions/v2/AskForApproval"
},
"approvalsReviewer": {
"$ref": "#/definitions/v2/ApprovalsReviewer"
},
"collaborationMode": {
"$ref": "#/definitions/v2/CollaborationMode"
},
"cwd": {
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
"effort": {
"anyOf": [
{
"$ref": "#/definitions/v2/ReasoningEffort"
},
{
"type": "null"
}
]
},
"model": {
"type": "string"
},
"modelProvider": {
"type": "string"
},
"personality": {
"anyOf": [
{
"$ref": "#/definitions/v2/Personality"
},
{
"type": "null"
}
]
},
"sandboxPolicy": {
"$ref": "#/definitions/v2/SandboxPolicy"
},
"serviceTier": {
"type": [
"string",
"null"
]
},
"summary": {
"anyOf": [
{
"$ref": "#/definitions/v2/ReasoningSummary"
},
{
"type": "null"
}
]
}
},
"required": [
"approvalPolicy",
"approvalsReviewer",
"collaborationMode",
"cwd",
"model",
"modelProvider",
"sandboxPolicy"
],
"type": "object"
},
"ThreadSettingsUpdatedNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"threadId": {
"type": "string"
},
"threadSettings": {
"$ref": "#/definitions/v2/ThreadSettings"
}
},
"required": [
"threadId",
"threadSettings"
],
"title": "ThreadSettingsUpdatedNotification",
"type": "object"
},
"ThreadShellCommandParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {

View File

@@ -134,7 +134,7 @@
"properties": {
"extends": {
"default": null,
"description": "Parent profile identifier once permissions profiles support inheritance. This is currently always `null`.",
"description": "Parent profile identifier from the selected permissions profile's `extends` setting, when present.",
"type": [
"string",
"null"
@@ -164,6 +164,28 @@
],
"type": "string"
},
"AdditionalContextEntry": {
"properties": {
"kind": {
"$ref": "#/definitions/AdditionalContextKind"
},
"value": {
"type": "string"
}
},
"required": [
"kind",
"value"
],
"type": "object"
},
"AdditionalContextKind": {
"enum": [
"untrusted",
"application"
],
"type": "string"
},
"AdditionalFileSystemPermissions": {
"properties": {
"entries": {
@@ -1098,6 +1120,78 @@
"title": "Thread/name/setRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/RequestId"
},
"method": {
"enum": [
"thread/goal/set"
],
"title": "Thread/goal/setRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadGoalSetParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "Thread/goal/setRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/RequestId"
},
"method": {
"enum": [
"thread/goal/get"
],
"title": "Thread/goal/getRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadGoalGetParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "Thread/goal/getRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/RequestId"
},
"method": {
"enum": [
"thread/goal/clear"
],
"title": "Thread/goal/clearRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadGoalClearParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "Thread/goal/clearRequest",
"type": "object"
},
{
"properties": {
"id": {
@@ -2155,6 +2249,30 @@
"title": "ExperimentalFeature/listRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/RequestId"
},
"method": {
"enum": [
"permissionProfile/list"
],
"title": "PermissionProfile/listRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/PermissionProfileListParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "PermissionProfile/listRequest",
"type": "object"
},
{
"properties": {
"id": {
@@ -3634,19 +3752,6 @@
}
]
},
"profile": {
"type": [
"string",
"null"
]
},
"profiles": {
"additionalProperties": {
"$ref": "#/definitions/ProfileV2"
},
"default": {},
"type": "object"
},
"review_model": {
"type": [
"string",
@@ -3964,7 +4069,6 @@
]
},
"includeLayers": {
"default": false,
"type": "boolean"
}
},
@@ -4002,6 +4106,12 @@
},
"ConfigRequirements": {
"properties": {
"allowAppshots": {
"type": [
"boolean",
"null"
]
},
"allowManagedHooksOnly": {
"type": [
"boolean",
@@ -4017,6 +4127,15 @@
"null"
]
},
"allowedPermissions": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
},
"allowedSandboxModes": {
"items": {
"$ref": "#/definitions/SandboxMode"
@@ -4857,8 +4976,7 @@
}
},
"required": [
"classification",
"includeLogs"
"classification"
],
"title": "FeedbackUploadParams",
"type": "object"
@@ -5607,6 +5725,26 @@
],
"title": "InputImageFunctionCallOutputContentItem",
"type": "object"
},
{
"properties": {
"encrypted_content": {
"type": "string"
},
"type": {
"enum": [
"encrypted_content"
],
"title": "EncryptedContentFunctionCallOutputContentItemType",
"type": "string"
}
},
"required": [
"encrypted_content",
"type"
],
"title": "EncryptedContentFunctionCallOutputContentItem",
"type": "object"
}
]
},
@@ -5725,7 +5863,6 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"refreshToken": {
"default": false,
"description": "When `true`, requests a proactive token refresh before returning.\n\nIn managed auth mode this triggers the normal refresh-token flow. In external auth mode this flag is ignored. Clients should refresh tokens themselves and call `account/login/start` with `chatgptAuthTokens`.",
"type": "boolean"
}
@@ -6146,6 +6283,7 @@
"sessionStart",
"userPromptSubmit",
"subagentStart",
"subagentStop",
"stop"
],
"type": "string"
@@ -6502,6 +6640,8 @@
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],
@@ -6756,6 +6896,12 @@
"integer",
"null"
]
},
"threadId": {
"type": [
"string",
"null"
]
}
},
"title": "ListMcpServerStatusParams",
@@ -7091,6 +7237,12 @@
},
"type": "array"
},
"SubagentStop": {
"items": {
"$ref": "#/definitions/ConfiguredHookMatcherGroup"
},
"type": "array"
},
"UserPromptSubmit": {
"items": {
"$ref": "#/definitions/ConfiguredHookMatcherGroup"
@@ -7119,6 +7271,7 @@
"SessionStart",
"Stop",
"SubagentStart",
"SubagentStop",
"UserPromptSubmit"
],
"type": "object"
@@ -7751,6 +7904,14 @@
"defaultReasoningEffort": {
"$ref": "#/definitions/ReasoningEffort"
},
"defaultServiceTier": {
"default": null,
"description": "Catalog default service tier id for this model, when one is configured.",
"type": [
"string",
"null"
]
},
"description": {
"type": "string"
},
@@ -8257,6 +8418,78 @@
}
]
},
"PermissionProfileListParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"cursor": {
"description": "Opaque pagination cursor returned by a previous call.",
"type": [
"string",
"null"
]
},
"cwd": {
"description": "Optional working directory to resolve project config layers.",
"type": [
"string",
"null"
]
},
"limit": {
"description": "Optional page size; defaults to the full result set.",
"format": "uint32",
"minimum": 0.0,
"type": [
"integer",
"null"
]
}
},
"title": "PermissionProfileListParams",
"type": "object"
},
"PermissionProfileListResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"data": {
"items": {
"$ref": "#/definitions/PermissionProfileSummary"
},
"type": "array"
},
"nextCursor": {
"description": "Opaque cursor to pass to the next call to continue after the last item. If None, there are no more items to return.",
"type": [
"string",
"null"
]
}
},
"required": [
"data"
],
"title": "PermissionProfileListResponse",
"type": "object"
},
"PermissionProfileSummary": {
"properties": {
"description": {
"description": "Optional user-facing description for display in clients.",
"type": [
"string",
"null"
]
},
"id": {
"description": "Available permission profile identifier.",
"type": "string"
}
},
"required": [
"id"
],
"type": "object"
},
"Personality": {
"enum": [
"none",
@@ -8646,6 +8879,7 @@
"PluginListMarketplaceKind": {
"enum": [
"local",
"vertical",
"workspace-directory",
"shared-with-me"
],
@@ -9488,107 +9722,6 @@
],
"type": "object"
},
"ProfileV2": {
"additionalProperties": true,
"properties": {
"approval_policy": {
"anyOf": [
{
"$ref": "#/definitions/AskForApproval"
},
{
"type": "null"
}
]
},
"approvals_reviewer": {
"anyOf": [
{
"$ref": "#/definitions/ApprovalsReviewer"
},
{
"type": "null"
}
],
"description": "[UNSTABLE] Optional profile-level override for where approval requests are routed for review. If omitted, the enclosing config default is used."
},
"chatgpt_base_url": {
"type": [
"string",
"null"
]
},
"model": {
"type": [
"string",
"null"
]
},
"model_provider": {
"type": [
"string",
"null"
]
},
"model_reasoning_effort": {
"anyOf": [
{
"$ref": "#/definitions/ReasoningEffort"
},
{
"type": "null"
}
]
},
"model_reasoning_summary": {
"anyOf": [
{
"$ref": "#/definitions/ReasoningSummary"
},
{
"type": "null"
}
]
},
"model_verbosity": {
"anyOf": [
{
"$ref": "#/definitions/Verbosity"
},
{
"type": "null"
}
]
},
"service_tier": {
"type": [
"string",
"null"
]
},
"tools": {
"anyOf": [
{
"$ref": "#/definitions/ToolsV2"
},
{
"type": "null"
}
]
},
"web_search": {
"anyOf": [
{
"$ref": "#/definitions/WebSearchMode"
},
{
"type": "null"
}
]
}
},
"type": "object"
},
"RateLimitReachedType": {
"enum": [
"rate_limit_reached",
@@ -11290,6 +11423,26 @@
"title": "Thread/goal/clearedNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"thread/settings/updated"
],
"title": "Thread/settings/updatedNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadSettingsUpdatedNotification"
}
},
"required": [
"method",
"params"
],
"title": "Thread/settings/updatedNotification",
"type": "object"
},
{
"properties": {
"method": {
@@ -13433,6 +13586,32 @@
],
"type": "object"
},
"ThreadGoalClearParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"threadId": {
"type": "string"
}
},
"required": [
"threadId"
],
"title": "ThreadGoalClearParams",
"type": "object"
},
"ThreadGoalClearResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"cleared": {
"type": "boolean"
}
},
"required": [
"cleared"
],
"title": "ThreadGoalClearResponse",
"type": "object"
},
"ThreadGoalClearedNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
@@ -13446,6 +13625,85 @@
"title": "ThreadGoalClearedNotification",
"type": "object"
},
"ThreadGoalGetParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"threadId": {
"type": "string"
}
},
"required": [
"threadId"
],
"title": "ThreadGoalGetParams",
"type": "object"
},
"ThreadGoalGetResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"goal": {
"anyOf": [
{
"$ref": "#/definitions/ThreadGoal"
},
{
"type": "null"
}
]
}
},
"title": "ThreadGoalGetResponse",
"type": "object"
},
"ThreadGoalSetParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"objective": {
"type": [
"string",
"null"
]
},
"status": {
"anyOf": [
{
"$ref": "#/definitions/ThreadGoalStatus"
},
{
"type": "null"
}
]
},
"threadId": {
"type": "string"
},
"tokenBudget": {
"format": "int64",
"type": [
"integer",
"null"
]
}
},
"required": [
"threadId"
],
"title": "ThreadGoalSetParams",
"type": "object"
},
"ThreadGoalSetResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"goal": {
"$ref": "#/definitions/ThreadGoal"
}
},
"required": [
"goal"
],
"title": "ThreadGoalSetResponse",
"type": "object"
},
"ThreadGoalStatus": {
"enum": [
"active",
@@ -13813,6 +14071,12 @@
"null"
]
},
"pluginId": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
@@ -14444,7 +14708,6 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"includeTurns": {
"default": false,
"description": "When true, include turns and their items from rollout history.",
"type": "boolean"
},
@@ -14919,6 +15182,21 @@
"title": "ThreadRollbackResponse",
"type": "object"
},
"ThreadSearchResult": {
"properties": {
"snippet": {
"type": "string"
},
"thread": {
"$ref": "#/definitions/Thread"
}
},
"required": [
"snippet",
"thread"
],
"type": "object"
},
"ThreadSetNameParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
@@ -14941,6 +15219,104 @@
"title": "ThreadSetNameResponse",
"type": "object"
},
"ThreadSettings": {
"properties": {
"activePermissionProfile": {
"anyOf": [
{
"$ref": "#/definitions/ActivePermissionProfile"
},
{
"type": "null"
}
]
},
"approvalPolicy": {
"$ref": "#/definitions/AskForApproval"
},
"approvalsReviewer": {
"$ref": "#/definitions/ApprovalsReviewer"
},
"collaborationMode": {
"$ref": "#/definitions/CollaborationMode"
},
"cwd": {
"$ref": "#/definitions/AbsolutePathBuf"
},
"effort": {
"anyOf": [
{
"$ref": "#/definitions/ReasoningEffort"
},
{
"type": "null"
}
]
},
"model": {
"type": "string"
},
"modelProvider": {
"type": "string"
},
"personality": {
"anyOf": [
{
"$ref": "#/definitions/Personality"
},
{
"type": "null"
}
]
},
"sandboxPolicy": {
"$ref": "#/definitions/SandboxPolicy"
},
"serviceTier": {
"type": [
"string",
"null"
]
},
"summary": {
"anyOf": [
{
"$ref": "#/definitions/ReasoningSummary"
},
{
"type": "null"
}
]
}
},
"required": [
"approvalPolicy",
"approvalsReviewer",
"collaborationMode",
"cwd",
"model",
"modelProvider",
"sandboxPolicy"
],
"type": "object"
},
"ThreadSettingsUpdatedNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"threadId": {
"type": "string"
},
"threadSettings": {
"$ref": "#/definitions/ThreadSettings"
}
},
"required": [
"threadId",
"threadSettings"
],
"title": "ThreadSettingsUpdatedNotification",
"type": "object"
},
"ThreadShellCommandParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {

View File

@@ -9,7 +9,6 @@
]
},
"includeLayers": {
"default": false,
"type": "boolean"
}
},

View File

@@ -352,19 +352,6 @@
}
]
},
"profile": {
"type": [
"string",
"null"
]
},
"profiles": {
"additionalProperties": {
"$ref": "#/definitions/ProfileV2"
},
"default": {},
"type": "object"
},
"review_model": {
"type": [
"string",
@@ -642,107 +629,6 @@
],
"type": "string"
},
"ProfileV2": {
"additionalProperties": true,
"properties": {
"approval_policy": {
"anyOf": [
{
"$ref": "#/definitions/AskForApproval"
},
{
"type": "null"
}
]
},
"approvals_reviewer": {
"anyOf": [
{
"$ref": "#/definitions/ApprovalsReviewer"
},
{
"type": "null"
}
],
"description": "[UNSTABLE] Optional profile-level override for where approval requests are routed for review. If omitted, the enclosing config default is used."
},
"chatgpt_base_url": {
"type": [
"string",
"null"
]
},
"model": {
"type": [
"string",
"null"
]
},
"model_provider": {
"type": [
"string",
"null"
]
},
"model_reasoning_effort": {
"anyOf": [
{
"$ref": "#/definitions/ReasoningEffort"
},
{
"type": "null"
}
]
},
"model_reasoning_summary": {
"anyOf": [
{
"$ref": "#/definitions/ReasoningSummary"
},
{
"type": "null"
}
]
},
"model_verbosity": {
"anyOf": [
{
"$ref": "#/definitions/Verbosity"
},
{
"type": "null"
}
]
},
"service_tier": {
"type": [
"string",
"null"
]
},
"tools": {
"anyOf": [
{
"$ref": "#/definitions/ToolsV2"
},
{
"type": "null"
}
]
},
"web_search": {
"anyOf": [
{
"$ref": "#/definitions/WebSearchMode"
},
{
"type": "null"
}
]
}
},
"type": "object"
},
"ReasoningEffort": {
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
"enum": [

View File

@@ -73,6 +73,12 @@
},
"ConfigRequirements": {
"properties": {
"allowAppshots": {
"type": [
"boolean",
"null"
]
},
"allowManagedHooksOnly": {
"type": [
"boolean",
@@ -88,6 +94,15 @@
"null"
]
},
"allowedPermissions": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
},
"allowedSandboxModes": {
"items": {
"$ref": "#/definitions/SandboxMode"
@@ -288,6 +303,12 @@
},
"type": "array"
},
"SubagentStop": {
"items": {
"$ref": "#/definitions/ConfiguredHookMatcherGroup"
},
"type": "array"
},
"UserPromptSubmit": {
"items": {
"$ref": "#/definitions/ConfiguredHookMatcherGroup"
@@ -316,6 +337,7 @@
"SessionStart",
"Stop",
"SubagentStart",
"SubagentStop",
"UserPromptSubmit"
],
"type": "object"

View File

@@ -39,8 +39,7 @@
}
},
"required": [
"classification",
"includeLogs"
"classification"
],
"title": "FeedbackUploadParams",
"type": "object"

View File

@@ -2,7 +2,6 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"refreshToken": {
"default": false,
"description": "When `true`, requests a proactive token refresh before returning.\n\nIn managed auth mode this triggers the normal refresh-token flow. In external auth mode this flag is ignored. Clients should refresh tokens themselves and call `account/login/start` with `chatgptAuthTokens`.",
"type": "boolean"
}

View File

@@ -15,6 +15,7 @@
"sessionStart",
"userPromptSubmit",
"subagentStart",
"subagentStop",
"stop"
],
"type": "string"

View File

@@ -15,6 +15,7 @@
"sessionStart",
"userPromptSubmit",
"subagentStart",
"subagentStop",
"stop"
],
"type": "string"

View File

@@ -30,6 +30,7 @@
"sessionStart",
"userPromptSubmit",
"subagentStart",
"subagentStop",
"stop"
],
"type": "string"

View File

@@ -287,6 +287,8 @@
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],
@@ -800,6 +802,12 @@
"null"
]
},
"pluginId": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{

View File

@@ -287,6 +287,8 @@
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],
@@ -800,6 +802,12 @@
"null"
]
},
"pluginId": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{

View File

@@ -36,6 +36,12 @@
"integer",
"null"
]
},
"threadId": {
"type": [
"string",
"null"
]
}
},
"title": "ListMcpServerStatusParams",

View File

@@ -43,6 +43,14 @@
"defaultReasoningEffort": {
"$ref": "#/definitions/ReasoningEffort"
},
"defaultServiceTier": {
"default": null,
"description": "Catalog default service tier id for this model, when one is configured.",
"type": [
"string",
"null"
]
},
"description": {
"type": "string"
},

View File

@@ -0,0 +1,30 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"cursor": {
"description": "Opaque pagination cursor returned by a previous call.",
"type": [
"string",
"null"
]
},
"cwd": {
"description": "Optional working directory to resolve project config layers.",
"type": [
"string",
"null"
]
},
"limit": {
"description": "Optional page size; defaults to the full result set.",
"format": "uint32",
"minimum": 0.0,
"type": [
"integer",
"null"
]
}
},
"title": "PermissionProfileListParams",
"type": "object"
}

View File

@@ -0,0 +1,44 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"PermissionProfileSummary": {
"properties": {
"description": {
"description": "Optional user-facing description for display in clients.",
"type": [
"string",
"null"
]
},
"id": {
"description": "Available permission profile identifier.",
"type": "string"
}
},
"required": [
"id"
],
"type": "object"
}
},
"properties": {
"data": {
"items": {
"$ref": "#/definitions/PermissionProfileSummary"
},
"type": "array"
},
"nextCursor": {
"description": "Opaque cursor to pass to the next call to continue after the last item. If None, there are no more items to return.",
"type": [
"string",
"null"
]
}
},
"required": [
"data"
],
"title": "PermissionProfileListResponse",
"type": "object"
}

View File

@@ -8,6 +8,7 @@
"PluginListMarketplaceKind": {
"enum": [
"local",
"vertical",
"workspace-directory",
"shared-with-me"
],

View File

@@ -47,6 +47,7 @@
"sessionStart",
"userPromptSubmit",
"subagentStart",
"subagentStop",
"stop"
],
"type": "string"

View File

@@ -140,11 +140,33 @@
],
"title": "InputImageFunctionCallOutputContentItem",
"type": "object"
},
{
"properties": {
"encrypted_content": {
"type": "string"
},
"type": {
"enum": [
"encrypted_content"
],
"title": "EncryptedContentFunctionCallOutputContentItemType",
"type": "string"
}
},
"required": [
"encrypted_content",
"type"
],
"title": "EncryptedContentFunctionCallOutputContentItem",
"type": "object"
}
]
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],

View File

@@ -424,6 +424,8 @@
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],
@@ -944,6 +946,12 @@
"null"
]
},
"pluginId": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{

View File

@@ -9,7 +9,7 @@
"properties": {
"extends": {
"default": null,
"description": "Parent profile identifier once permissions profiles support inheritance. This is currently always `null`.",
"description": "Parent profile identifier from the selected permissions profile's `extends` setting, when present.",
"type": [
"string",
"null"
@@ -529,6 +529,8 @@
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],
@@ -1421,6 +1423,12 @@
"null"
]
},
"pluginId": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{

View File

@@ -0,0 +1,13 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"threadId": {
"type": "string"
}
},
"required": [
"threadId"
],
"title": "ThreadGoalClearParams",
"type": "object"
}

View File

@@ -0,0 +1,13 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"cleared": {
"type": "boolean"
}
},
"required": [
"cleared"
],
"title": "ThreadGoalClearResponse",
"type": "object"
}

View File

@@ -0,0 +1,13 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"threadId": {
"type": "string"
}
},
"required": [
"threadId"
],
"title": "ThreadGoalGetParams",
"type": "object"
}

View File

@@ -0,0 +1,76 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"ThreadGoal": {
"properties": {
"createdAt": {
"format": "int64",
"type": "integer"
},
"objective": {
"type": "string"
},
"status": {
"$ref": "#/definitions/ThreadGoalStatus"
},
"threadId": {
"type": "string"
},
"timeUsedSeconds": {
"format": "int64",
"type": "integer"
},
"tokenBudget": {
"format": "int64",
"type": [
"integer",
"null"
]
},
"tokensUsed": {
"format": "int64",
"type": "integer"
},
"updatedAt": {
"format": "int64",
"type": "integer"
}
},
"required": [
"createdAt",
"objective",
"status",
"threadId",
"timeUsedSeconds",
"tokensUsed",
"updatedAt"
],
"type": "object"
},
"ThreadGoalStatus": {
"enum": [
"active",
"paused",
"blocked",
"usageLimited",
"budgetLimited",
"complete"
],
"type": "string"
}
},
"properties": {
"goal": {
"anyOf": [
{
"$ref": "#/definitions/ThreadGoal"
},
{
"type": "null"
}
]
}
},
"title": "ThreadGoalGetResponse",
"type": "object"
}

View File

@@ -0,0 +1,49 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"ThreadGoalStatus": {
"enum": [
"active",
"paused",
"blocked",
"usageLimited",
"budgetLimited",
"complete"
],
"type": "string"
}
},
"properties": {
"objective": {
"type": [
"string",
"null"
]
},
"status": {
"anyOf": [
{
"$ref": "#/definitions/ThreadGoalStatus"
},
{
"type": "null"
}
]
},
"threadId": {
"type": "string"
},
"tokenBudget": {
"format": "int64",
"type": [
"integer",
"null"
]
}
},
"required": [
"threadId"
],
"title": "ThreadGoalSetParams",
"type": "object"
}

View File

@@ -0,0 +1,72 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"ThreadGoal": {
"properties": {
"createdAt": {
"format": "int64",
"type": "integer"
},
"objective": {
"type": "string"
},
"status": {
"$ref": "#/definitions/ThreadGoalStatus"
},
"threadId": {
"type": "string"
},
"timeUsedSeconds": {
"format": "int64",
"type": "integer"
},
"tokenBudget": {
"format": "int64",
"type": [
"integer",
"null"
]
},
"tokensUsed": {
"format": "int64",
"type": "integer"
},
"updatedAt": {
"format": "int64",
"type": "integer"
}
},
"required": [
"createdAt",
"objective",
"status",
"threadId",
"timeUsedSeconds",
"tokensUsed",
"updatedAt"
],
"type": "object"
},
"ThreadGoalStatus": {
"enum": [
"active",
"paused",
"blocked",
"usageLimited",
"budgetLimited",
"complete"
],
"type": "string"
}
},
"properties": {
"goal": {
"$ref": "#/definitions/ThreadGoal"
}
},
"required": [
"goal"
],
"title": "ThreadGoalSetResponse",
"type": "object"
}

View File

@@ -450,6 +450,8 @@
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],
@@ -1236,6 +1238,12 @@
"null"
]
},
"pluginId": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{

View File

@@ -450,6 +450,8 @@
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],
@@ -1236,6 +1238,12 @@
"null"
]
},
"pluginId": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{

View File

@@ -2,7 +2,6 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"includeTurns": {
"default": false,
"description": "When true, include turns and their items from rollout history.",
"type": "boolean"
},

View File

@@ -450,6 +450,8 @@
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],
@@ -1236,6 +1238,12 @@
"null"
]
},
"pluginId": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{

View File

@@ -199,11 +199,33 @@
],
"title": "InputImageFunctionCallOutputContentItem",
"type": "object"
},
{
"properties": {
"encrypted_content": {
"type": "string"
},
"type": {
"enum": [
"encrypted_content"
],
"title": "EncryptedContentFunctionCallOutputContentItemType",
"type": "string"
}
},
"required": [
"encrypted_content",
"type"
],
"title": "EncryptedContentFunctionCallOutputContentItem",
"type": "object"
}
]
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],

View File

@@ -9,7 +9,7 @@
"properties": {
"extends": {
"default": null,
"description": "Parent profile identifier once permissions profiles support inheritance. This is currently always `null`.",
"description": "Parent profile identifier from the selected permissions profile's `extends` setting, when present.",
"type": [
"string",
"null"
@@ -529,6 +529,8 @@
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],
@@ -1421,6 +1423,12 @@
"null"
]
},
"pluginId": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{

View File

@@ -450,6 +450,8 @@
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],
@@ -1236,6 +1238,12 @@
"null"
]
},
"pluginId": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{

View File

@@ -0,0 +1,381 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"AbsolutePathBuf": {
"description": "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.",
"type": "string"
},
"ActivePermissionProfile": {
"properties": {
"extends": {
"default": null,
"description": "Parent profile identifier from the selected permissions profile's `extends` setting, when present.",
"type": [
"string",
"null"
]
},
"id": {
"description": "Identifier from `default_permissions` or the implicit built-in default, such as `:workspace` or a user-defined `[permissions.<id>]` profile.",
"type": "string"
}
},
"required": [
"id"
],
"type": "object"
},
"ApprovalsReviewer": {
"description": "Configures who approval requests are routed to for review. Examples include sandbox escapes, blocked network access, MCP approval prompts, and ARC escalations. Defaults to `user`. `auto_review` uses a carefully prompted subagent to gather relevant context and apply a risk-based decision framework before approving or denying the request. The legacy value `guardian_subagent` is accepted for compatibility.",
"enum": [
"user",
"auto_review",
"guardian_subagent"
],
"type": "string"
},
"AskForApproval": {
"oneOf": [
{
"enum": [
"untrusted",
"on-failure",
"on-request",
"never"
],
"type": "string"
},
{
"additionalProperties": false,
"properties": {
"granular": {
"properties": {
"mcp_elicitations": {
"type": "boolean"
},
"request_permissions": {
"default": false,
"type": "boolean"
},
"rules": {
"type": "boolean"
},
"sandbox_approval": {
"type": "boolean"
},
"skill_approval": {
"default": false,
"type": "boolean"
}
},
"required": [
"mcp_elicitations",
"rules",
"sandbox_approval"
],
"type": "object"
}
},
"required": [
"granular"
],
"title": "GranularAskForApproval",
"type": "object"
}
]
},
"CollaborationMode": {
"description": "Collaboration mode for a Codex session.",
"properties": {
"mode": {
"$ref": "#/definitions/ModeKind"
},
"settings": {
"$ref": "#/definitions/Settings"
}
},
"required": [
"mode",
"settings"
],
"type": "object"
},
"ModeKind": {
"description": "Initial collaboration mode to use when the TUI starts.",
"enum": [
"plan",
"default"
],
"type": "string"
},
"NetworkAccess": {
"enum": [
"restricted",
"enabled"
],
"type": "string"
},
"Personality": {
"enum": [
"none",
"friendly",
"pragmatic"
],
"type": "string"
},
"ReasoningEffort": {
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
"enum": [
"none",
"minimal",
"low",
"medium",
"high",
"xhigh"
],
"type": "string"
},
"ReasoningSummary": {
"description": "A summary of the reasoning performed by the model. This can be useful for debugging and understanding the model's reasoning process. See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#reasoning-summaries",
"oneOf": [
{
"enum": [
"auto",
"concise",
"detailed"
],
"type": "string"
},
{
"description": "Option to disable reasoning summaries.",
"enum": [
"none"
],
"type": "string"
}
]
},
"SandboxPolicy": {
"oneOf": [
{
"properties": {
"type": {
"enum": [
"dangerFullAccess"
],
"title": "DangerFullAccessSandboxPolicyType",
"type": "string"
}
},
"required": [
"type"
],
"title": "DangerFullAccessSandboxPolicy",
"type": "object"
},
{
"properties": {
"networkAccess": {
"default": false,
"type": "boolean"
},
"type": {
"enum": [
"readOnly"
],
"title": "ReadOnlySandboxPolicyType",
"type": "string"
}
},
"required": [
"type"
],
"title": "ReadOnlySandboxPolicy",
"type": "object"
},
{
"properties": {
"networkAccess": {
"allOf": [
{
"$ref": "#/definitions/NetworkAccess"
}
],
"default": "restricted"
},
"type": {
"enum": [
"externalSandbox"
],
"title": "ExternalSandboxSandboxPolicyType",
"type": "string"
}
},
"required": [
"type"
],
"title": "ExternalSandboxSandboxPolicy",
"type": "object"
},
{
"properties": {
"excludeSlashTmp": {
"default": false,
"type": "boolean"
},
"excludeTmpdirEnvVar": {
"default": false,
"type": "boolean"
},
"networkAccess": {
"default": false,
"type": "boolean"
},
"type": {
"enum": [
"workspaceWrite"
],
"title": "WorkspaceWriteSandboxPolicyType",
"type": "string"
},
"writableRoots": {
"default": [],
"items": {
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": "array"
}
},
"required": [
"type"
],
"title": "WorkspaceWriteSandboxPolicy",
"type": "object"
}
]
},
"Settings": {
"description": "Settings for a collaboration mode.",
"properties": {
"developer_instructions": {
"type": [
"string",
"null"
]
},
"model": {
"type": "string"
},
"reasoning_effort": {
"anyOf": [
{
"$ref": "#/definitions/ReasoningEffort"
},
{
"type": "null"
}
]
}
},
"required": [
"model"
],
"type": "object"
},
"ThreadSettings": {
"properties": {
"activePermissionProfile": {
"anyOf": [
{
"$ref": "#/definitions/ActivePermissionProfile"
},
{
"type": "null"
}
]
},
"approvalPolicy": {
"$ref": "#/definitions/AskForApproval"
},
"approvalsReviewer": {
"$ref": "#/definitions/ApprovalsReviewer"
},
"collaborationMode": {
"$ref": "#/definitions/CollaborationMode"
},
"cwd": {
"$ref": "#/definitions/AbsolutePathBuf"
},
"effort": {
"anyOf": [
{
"$ref": "#/definitions/ReasoningEffort"
},
{
"type": "null"
}
]
},
"model": {
"type": "string"
},
"modelProvider": {
"type": "string"
},
"personality": {
"anyOf": [
{
"$ref": "#/definitions/Personality"
},
{
"type": "null"
}
]
},
"sandboxPolicy": {
"$ref": "#/definitions/SandboxPolicy"
},
"serviceTier": {
"type": [
"string",
"null"
]
},
"summary": {
"anyOf": [
{
"$ref": "#/definitions/ReasoningSummary"
},
{
"type": "null"
}
]
}
},
"required": [
"approvalPolicy",
"approvalsReviewer",
"collaborationMode",
"cwd",
"model",
"modelProvider",
"sandboxPolicy"
],
"type": "object"
}
},
"properties": {
"threadId": {
"type": "string"
},
"threadSettings": {
"$ref": "#/definitions/ThreadSettings"
}
},
"required": [
"threadId",
"threadSettings"
],
"title": "ThreadSettingsUpdatedNotification",
"type": "object"
}

View File

@@ -9,7 +9,7 @@
"properties": {
"extends": {
"default": null,
"description": "Parent profile identifier once permissions profiles support inheritance. This is currently always `null`.",
"description": "Parent profile identifier from the selected permissions profile's `extends` setting, when present.",
"type": [
"string",
"null"
@@ -529,6 +529,8 @@
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],
@@ -1421,6 +1423,12 @@
"null"
]
},
"pluginId": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{

View File

@@ -450,6 +450,8 @@
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],
@@ -1236,6 +1238,12 @@
"null"
]
},
"pluginId": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{

View File

@@ -450,6 +450,8 @@
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],
@@ -1236,6 +1238,12 @@
"null"
]
},
"pluginId": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{

View File

@@ -424,6 +424,8 @@
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],
@@ -944,6 +946,12 @@
"null"
]
},
"pluginId": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{

View File

@@ -5,6 +5,28 @@
"description": "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute.",
"type": "string"
},
"AdditionalContextEntry": {
"properties": {
"kind": {
"$ref": "#/definitions/AdditionalContextKind"
},
"value": {
"type": "string"
}
},
"required": [
"kind",
"value"
],
"type": "object"
},
"AdditionalContextKind": {
"enum": [
"untrusted",
"application"
],
"type": "string"
},
"ApprovalsReviewer": {
"description": "Configures who approval requests are routed to for review. Examples include sandbox escapes, blocked network access, MCP approval prompts, and ARC escalations. Defaults to `user`. `auto_review` uses a carefully prompted subagent to gather relevant context and apply a risk-based decision framework before approving or denying the request. The legacy value `guardian_subagent` is accepted for compatibility.",
"enum": [
@@ -101,6 +123,8 @@
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],

View File

@@ -424,6 +424,8 @@
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],
@@ -944,6 +946,12 @@
"null"
]
},
"pluginId": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{

View File

@@ -424,6 +424,8 @@
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],
@@ -944,6 +946,12 @@
"null"
]
},
"pluginId": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{

View File

@@ -1,6 +1,28 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"AdditionalContextEntry": {
"properties": {
"kind": {
"$ref": "#/definitions/AdditionalContextKind"
},
"value": {
"type": "string"
}
},
"required": [
"kind",
"value"
],
"type": "object"
},
"AdditionalContextKind": {
"enum": [
"untrusted",
"application"
],
"type": "string"
},
"ByteRange": {
"properties": {
"end": {
@@ -22,6 +44,8 @@
},
"ImageDetail": {
"enum": [
"auto",
"low",
"high",
"original"
],

File diff suppressed because one or more lines are too long

View File

@@ -7,4 +7,4 @@ import type { ImageDetail } from "./ImageDetail";
* Responses API compatible content items that can be returned by a tool call.
* This is a subset of ContentItem with the types we support as function call outputs.
*/
export type FunctionCallOutputContentItem = { "type": "input_text", text: string, } | { "type": "input_image", image_url: string, detail?: ImageDetail, };
export type FunctionCallOutputContentItem = { "type": "input_text", text: string, } | { "type": "input_image", image_url: string, detail?: ImageDetail, } | { "type": "encrypted_content", encrypted_content: string, };

View File

@@ -2,4 +2,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type ImageDetail = "high" | "original";
export type ImageDetail = "auto" | "low" | "high" | "original";

File diff suppressed because one or more lines are too long

View File

@@ -9,7 +9,7 @@ export type ActivePermissionProfile = {
*/
id: string,
/**
* Parent profile identifier once permissions profiles support
* inheritance. This is currently always `null`.
* Parent profile identifier from the selected permissions profile's
* `extends` setting, when present.
*/
extends: string | null, };

View File

@@ -0,0 +1,6 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { AdditionalContextKind } from "./AdditionalContextKind";
export type AdditionalContextEntry = { value: string, kind: AdditionalContextKind, };

View File

@@ -0,0 +1,5 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type AdditionalContextKind = "untrusted" | "application";

View File

@@ -12,7 +12,6 @@ import type { AnalyticsConfig } from "./AnalyticsConfig";
import type { ApprovalsReviewer } from "./ApprovalsReviewer";
import type { AskForApproval } from "./AskForApproval";
import type { ForcedChatgptWorkspaceIds } from "./ForcedChatgptWorkspaceIds";
import type { ProfileV2 } from "./ProfileV2";
import type { SandboxMode } from "./SandboxMode";
import type { SandboxWorkspaceWrite } from "./SandboxWorkspaceWrite";
import type { ToolsV2 } from "./ToolsV2";
@@ -21,4 +20,4 @@ export type Config = {model: string | null, review_model: string | null, model_c
* [UNSTABLE] Optional default for where approval requests are routed for
* review.
*/
approvals_reviewer: ApprovalsReviewer | null, sandbox_mode: SandboxMode | null, sandbox_workspace_write: SandboxWorkspaceWrite | null, forced_chatgpt_workspace_id: ForcedChatgptWorkspaceIds | null, forced_login_method: ForcedLoginMethod | null, web_search: WebSearchMode | null, tools: ToolsV2 | null, profile: string | null, profiles: { [key in string]?: ProfileV2 }, instructions: string | null, developer_instructions: string | null, compact_prompt: string | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, service_tier: string | null, analytics: AnalyticsConfig | null, desktop: { [key in string]?: JsonValue } | null} & ({ [key in string]?: number | string | boolean | Array<JsonValue> | { [key in string]?: JsonValue } | null });
approvals_reviewer: ApprovalsReviewer | null, sandbox_mode: SandboxMode | null, sandbox_workspace_write: SandboxWorkspaceWrite | null, forced_chatgpt_workspace_id: ForcedChatgptWorkspaceIds | null, forced_login_method: ForcedLoginMethod | null, web_search: WebSearchMode | null, tools: ToolsV2 | null, instructions: string | null, developer_instructions: string | null, compact_prompt: string | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, service_tier: string | null, analytics: AnalyticsConfig | null, desktop: { [key in string]?: JsonValue } | null} & ({ [key in string]?: number | string | boolean | Array<JsonValue> | { [key in string]?: JsonValue } | null });

View File

@@ -2,7 +2,7 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type ConfigReadParams = { includeLayers: boolean,
export type ConfigReadParams = { includeLayers?: boolean,
/**
* Optional working directory to resolve project config layers. If specified,
* return the effective config as seen from that directory (i.e., including any

View File

@@ -7,4 +7,4 @@ import type { ComputerUseRequirements } from "./ComputerUseRequirements";
import type { ResidencyRequirement } from "./ResidencyRequirement";
import type { SandboxMode } from "./SandboxMode";
export type ConfigRequirements = {allowedApprovalPolicies: Array<AskForApproval> | null, allowedSandboxModes: Array<SandboxMode> | null, allowedWebSearchModes: Array<WebSearchMode> | null, allowManagedHooksOnly: boolean | null, computerUse: ComputerUseRequirements | null, featureRequirements: { [key in string]?: boolean } | null, enforceResidency: ResidencyRequirement | null};
export type ConfigRequirements = {allowedApprovalPolicies: Array<AskForApproval> | null, allowedSandboxModes: Array<SandboxMode> | null, allowedPermissions: Array<string> | null, allowedWebSearchModes: Array<WebSearchMode> | null, allowManagedHooksOnly: boolean | null, allowAppshots: boolean | null, computerUse: ComputerUseRequirements | null, featureRequirements: { [key in string]?: boolean } | null, enforceResidency: ResidencyRequirement | null};

View File

@@ -2,4 +2,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type FeedbackUploadParams = { classification: string, reason?: string | null, threadId?: string | null, includeLogs: boolean, extraLogFiles?: Array<string> | null, tags?: { [key in string]?: string } | null, };
export type FeedbackUploadParams = { classification: string, reason?: string | null, threadId?: string | null, includeLogs?: boolean, extraLogFiles?: Array<string> | null, tags?: { [key in string]?: string } | null, };

Some files were not shown because too many files have changed in this diff Show More