Commit Graph
6661 Commits
Author SHA1 Message Date
Adam Perry @ OpenAIandGitHub dce673905a core: load AGENTS.md from foreign environments (#28958)
## Why

Make it possible to load AGENTS.md from remote exec-servers whose OS is
different than app-server.

## What

- keep `AGENTS.md` discovery and provenance as `PathUri`, with
root-aware parent and ancestor traversal
- expose lifecycle instruction sources as legacy app-server path strings
in events while retaining `PathUri` internally
- preserve and test mixed POSIX and Windows paths in model context and
TUI status output
- cover remote Windows loading end to end by seeding the Wine prefix
through host filesystem APIs
- fix bug in `PathUri`'s parent() implementation that would erase
Windows drive letters
2026-06-18 15:06:23 -07:00
xl-openaiandGitHub 406062c3af [codex] Preserve remote plugin download status errors (#28863)
## Summary

- preserve the original HTTP status when a remote plugin bundle download
returns a non-success response
- retain at most 8 KiB of the error response body and annotate
truncation or body-read failures
- add regression coverage for an oversized error response

## Root cause

The non-success response path reused the normal size-limited body
reader. When an error response exceeded 8 KiB, that reader returned
`DownloadTooLarge` before the code constructed `DownloadStatus`, masking
the upstream HTTP status and response context.

## Impact

Remote plugin installation failures now retain the actionable upstream
HTTP status without allowing unbounded error bodies into logs.

## Validation

- `just test -p codex-app-server
plugin_install_preserves_status_when_remote_bundle_error_body_is_too_large`
- `just fmt`
- `git diff --check`
2026-06-18 14:29:01 -07:00
Alex DaleyandGitHub 4af7762f01 [connectors] Ignore synthetic links for app accessibility (#28770)
Summary
- Stop treating Codex Apps MCP tools with
`_meta._codex_apps.synthetic_link: true` as evidence that a connector is
accessible in `app/list`.
- Preserve synthetic tools in the agent-facing MCP connector set so they
remain available for install/auth flows.
- Keep the app-list accessibility cache limited to connectors backed by
at least one non-synthetic tool.
- Add focused regression coverage for both sides of the boundary.

Validation
- `just fmt`
- `just test -p codex-core
synthetic_links_are_exposed_to_the_agent_but_not_accessible_in_app_list`
- `git diff --check`
- A crate-wide `just test -p codex-core` run completed with 2,699
passing and 51 unrelated local sandbox/state failures, primarily state
DB migration races (`UNIQUE constraint failed:
_sqlx_migrations.version`).
2026-06-18 17:19:24 -04:00
AdrianandGitHub ec848dde0e feat: opt ChatGPT auth into agent identity (#19049)
## Stack

This is PR 2 of the simplified HAI single-run-task stack:

- [#19047](https://github.com/openai/codex/pull/19047) Agent Identity
assertion and task-registration primitives, including the shared
run-task helper used by existing Agent Identity JWT auth.
- [#19049](https://github.com/openai/codex/pull/19049)
Disabled-by-default ChatGPT auth opt-in that provisions/reuses persisted
Agent Identity runtime auth and its single run task.
- [#19051](https://github.com/openai/codex/pull/19051) Run-scoped
provider auth that uses one backend-owned task id for first-party
inference and compaction requests.

[#19054](https://github.com/openai/codex/pull/19054) collapsed out of
the active stack because the simplified design no longer needs a
separate background/control-plane task helper.

## Summary

This PR adds the disabled-by-default path for normal ChatGPT-login Codex
sessions to obtain Agent Identity runtime auth through the Codex
backend. Existing Agent Identity JWT startup mode remains a separate
path and does not require the feature flag.

What changed:

- adds the experimental `use_agent_identity` feature flag and config
schema entry
- adds an explicit `AgentIdentityAuthPolicy` so call sites choose
`JwtOnly` or `ChatGptAuth` instead of passing a bare boolean
- stores standalone Agent Identity JWT credentials separately from
backend-registered Agent Identity records
- persists the registered Agent Identity record, private key, and single
run task id in `auth.json` so process restarts reuse the same identity
- derives the agent/task registration base URL from ChatGPT/Codex auth
config while keeping JWT JWKS lookup separate
- provisions and caches ChatGPT-derived Agent Identity runtime auth when
`use_agent_identity` is enabled
- reuses the shared run-task registration helper from PR1 rather than
adding a second task-registration path

This PR intentionally does not switch model inference over to
`AgentAssertion` auth. The provider-auth integration lands in the next
PR.

## Testing

- `just test -p codex-login`
2026-06-18 14:05:27 -07:00
765309d5a6 Emit Trusted MCP App Identity on Tool-Call Items (#27132)
## Summary

- Add optional `appContext` to app-server MCP tool-call items with
trusted `connectorId`, `linkId`, and `mcpAppResourceUri` metadata.
- Preserve that context across tool-call events, persisted history,
reconnects, and thread resume.
- Keep the deprecated top-level `mcpAppResourceUri` temporarily for
client migration.

The consumer contract is `{ appContext: { connectorId, linkId,
mcpAppResourceUri }, tool }`.

## Validation

- Full GitHub Actions suite passes, including CLA, Bazel tests, clippy,
release builds, and argument-comment lint.

---------

Co-authored-by: martinauyeung-oai <280153141+martinauyeung-oai@users.noreply.github.com>
2026-06-18 14:02:54 -07:00
canvrno-oaiandGitHub 9bcc09f9f7 TUI: improve unified mention selection visibility (#28959)
## Summary

[@milanglacier reported in
#28653](https://github.com/openai/codex/issues/28653) that the active
mention candidate is hard to distinguish. I suspect [@binbjz’s #28500
report](https://github.com/openai/codex/issues/28500) _(where arrow-key
navigation appeared not to work)_ may describe the same presentation
problem: the selection may have been changing, but the UI was not
showing the active row clearly in their terminal. This PR makes two
small changes to the selection indication behavior:

- Reserve a two-character gutter and mark the active candidate with `> `
for color-agnostic indicator coverage.
- Apply the shared theme-aware accent to the entire selected row for
extra emphasis.
- Update the existing popup snapshot.

Reverse-video styling was considered, but avoided it because it is
overly dependent on the user’s terminal palette.

<img width="2046" height="482" alt="image"
src="https://github.com/user-attachments/assets/b5eb62c3-fd24-4c09-906e-7bd66913b5c6"
/>

## Testing

- `just test -p codex-tui default_unified_mention_popup_snapshot`
- `just clippy -p codex-tui`
- `just fmt`
- Compiled `codex-cli` and tested the unified mentions picker in the
terminal.
2026-06-18 13:37:04 -07:00
Eric NingandGitHub 29eb434bc5 [codex] Remove hardcoded app ID filters (#28947)
## Summary

- remove the duplicated originator-specific connector ID denylists
- stop filtering connector directory/accessibility results and
live/cached Codex Apps MCP tools by hardcoded connector ID
- remove the now-unused `codex-login` dependency from
`codex-utils-plugins`
- update regression coverage so formerly blocked connector IDs are
preserved

## Why

The client-side policy was duplicated across crates, used opaque IDs
without ownership or expiry information, and could drift between app
listing and MCP tool behavior. Server-provided visibility,
authorization, plugin discoverability, accessibility, enabled-state
handling, and consequential-tool approval templates remain unchanged.

## Validation

- `just fmt`
- `just bazel-lock-update`
- `just bazel-lock-check`
- `git diff --check`
- confirmed the final diff contains no hardcoded denylist symbols

A targeted `codex-mcp` test build spent an unusually long time in local
compilation/linking. Its first attempt exposed a test-only `PartialEq`
assertion issue, which was corrected. A follow-up non-linking `cargo
check -p codex-mcp --tests` was still running when this draft was
opened; CI should provide the complete Rust validation.
2026-06-18 20:29:01 +00:00
maja-openaiandGitHub d9dace8a59 Make auto-review on-request prompt more proactive (#26496)
## Why

`on-request` approval policy text is currently tuned for user-reviewed
approvals. For auto-reviewed productivity runs, likely sandbox blocks
should be escalated earlier so commands that need remote services,
authentication, or other out-of-sandbox access do not first fail or hang
inside the sandbox.

## What changed

- Adds a separate `on_request_auto_review.md` permissions prompt
selected for `AskForApproval::OnRequest` with
`ApprovalsReviewer::AutoReview`.
- Keeps the normal user-reviewed `on-request` wording unchanged.
- Makes the `When to request escalation` bullets more explicit about
likely sandbox blocks, network access, remote
auth/cluster/cloud/database access, out-of-sandbox environment access,
git operations that may write lock files, and short-timeout reruns after
likely sandbox-blocked attempts.
- Omits approved command prefix and `prefix_rule` guidance for the
auto-review on-request prompt.
- Adds prompt tests covering the auto-review path, normal on-request
wording, and inline permission request behavior.
2026-06-18 13:16:14 -07:00
rka-oaiandGitHub f4602b7516 Add app-server current-time impl (varlatency 3/n) (#28835)
## What

Server should request:

```
{
  "id": 42,
  "method": "currentTime/read",
  "params": {
    "threadId": "11111111-1111-1111-1111-aaaaafdc2c11"
  }
}
```

Client should respond with something like:

```rust
{
  "id": 42,
  "result": {
    "currentTimeAt": 1781717655
  }
}
```

## Why

Sessions configured with `clock_source = "external"` need a
thread-specific external time source before inference. The system clock
remains the default production provider.

## Validation

- `cargo test -p codex-app-server-protocol`
- `cargo test -p codex-app-server --test all
current_time_read_round_trip_adds_reminder_to_model_input`
- `cargo test -p codex-app-server
first_attestation_capable_connection_for_thread_only_uses_thread_subscribers`
- `cargo test -p codex-analytics`
- `just fix -p codex-app-server-protocol`
- `just fix -p codex-app-server`

Stacked on #28824.
2026-06-18 13:12:11 -07:00
Adam Perry @ OpenAIandGitHub 0f89dd768c apply-patch: carry paths as PathUri (#28854)
## Why

Allows the model to edit files that are hosted on a different OS than
where app-server is running.

## What

* Use `PathUri` for apply_patch-internal data structures
* Limit `PathUri` -> `AbsolutePathBuf` conversion to cases where the
inferred path convention matches the host OS, allows requiring valid
paths to pass to perms check
* Adds `PathConvention::path_segments()` for iterating over path
segments regardless of OS
* Handle cross-platform relative paths in path filename parsing for
sniffing a shell
* Ensure we can apply patches in the wine e2e test
2026-06-18 19:31:19 +00:00
Matthew ZengandGitHub a52a3b5197 [codex] Cache plugin metadata for tool suggestions (#27812)
## Why

`built_tools` runs for every sampling request, and local plugin
discovery was repeatedly rereading plugin manifests, skills, MCP
configuration, and app declarations to build the same tool-suggest
metadata.

That source-derived metadata is stable until the existing plugin manager
reloads its cache. Runtime eligibility still needs to reflect the
current install, disable, policy, app-overlap, and authentication state.

## What changed

- Add a bounded, in-memory tool-suggest metadata cache owned by
`PluginsManager`.
- Key cached metadata by plugin identity and source, while applying
authentication routing each time the metadata is projected.
- Invalidate the metadata alongside the existing loaded-plugin cache,
including its normal configuration, marketplace refresh, and
remote-installed-plugin invalidation paths.
- Guard against an in-flight load repopulating stale metadata after
invalidation.
- Keep marketplace membership and all runtime eligibility filtering live
rather than introducing a separate catalog or revision model.

## Impact

Repeated sampling requests reuse already-loaded plugin capability
metadata while retaining the existing plugin-manager lifecycle as the
single freshness boundary.

## Validation

- `just test -p codex-core-plugins` — 252 passed
- Added focused coverage for cache invalidation and authentication
reprojection.
2026-06-18 12:25:07 -07:00
rka-oaiandGitHub 752ed90d78 current time reminders impl for system clock (varlatency 2/n) (#28824)
Stacked on #28822.

## Summary

- add a host-injectable current-time provider with a built-in system
implementation
- record UTC developer reminders in history immediately before due model
requests
- keep cadence state per session and force a refresh after compaction

This does NOT include the app server client <-> server clock logic. This
PR is only for the reminder message & system clock that will be used in
prod.

## Testing

- `just test -p codex-core varlatency_`
- `just clippy -p codex-core -p codex-app-server -p codex-mcp-server -p
codex-thread-manager-sample`
- `just fmt`
2026-06-18 19:18:42 +00:00
TomandGitHub 01a2df2947 [codex] Make thread store turn filter optional (#28949)
Make `ListItemsParams::turn_id` optional so callers can list persisted
items across an entire thread or narrow the result to one turn. This
aligns the thread-store API and documentation with thread-wide item
listing while preserving the optional turn-filter behavior for
implementations.
2026-06-18 12:13:31 -07:00
Gabriel PealandGitHub 21a599fa56 Support openai/form extended form elicitations (#27500)
# Summary
Allow App Server clients to opt into `openai/form` MCP elicitations.
2026-06-18 11:54:49 -07:00
rka-oaiandGitHub 32a696dbac [codex] rollout budget implementation (varlength 2/N) (#28494)
## Stack

Depends on #28746. This PR implements shared rollout-budget accounting
and model-visible reminders using the configuration defined in #28746.

# Description / Main changes to Core:

`AgentControl` will now be the area where "rollout level" features &
accounting will have to live. It is incorrectly named for this
responsibility, but I think it can hold all the necessary shared state &
features (rollout token budget, mutliple thread interruption
responsibilitym etc)

In this PR, we have one "token ledger" that each thread will subtract
from when sampling. The "charge" will occur when response.completed() is
done and the calculation will be done on the responses api usage
carrier. The calculation will weigh sampling and pre-fill tokens as
specified.

Every time the budget crosses the configured reminder threshold, a
developer message is appended before the thread's next request

This remaining budget will _always_ be restated/reminded after a
compaction event.

Expiration and fan-out interruption will be in the stacked follow-up
(and also live in Agent Control).

## Reminders

"You have weighted {session_tokens_left} tokens left in the shared
session token budget."

The first request in each thread context receives the current remainder.
Later reminders are emitted after aggregate weighted usage crosses a
configured interval. If several intervals are crossed before a thread
sends another request, Core inserts one reminder with the latest
remainder.

Compaction response usage is charged before the next context starts. The
next reminder is appended after the compaction summary, leaving the
initial context content stable.

## Tests

Integration coverage verifies:

- weighted output and non-cached input accounting
- initial and periodic reminders
- shared accounting between a root and sub-agent
- post-compaction remainder and message placement

Local checks:

- `just fmt`
- `just test -p codex-core rollout_budget`
- `git diff --check`

The full workspace test suite was not run locally.
2026-06-18 18:52:19 +00:00
rka-oaiandGitHub df5f122854 Add Config for Time Reminders (varlatency 1/n) (#28822)
## Summary

Example:

> [features.current_time_reminder]
enabled = true
reminder_interval_model_requests = 1
clock_source = "system"

## Testing

- `just test -p codex-core varlatency`
- `just test -p codex-core
lock_contains_prompts_and_materializes_features`
- `just fix -p codex-core -p codex-config -p codex-features`
2026-06-18 11:39:02 -07:00
rka-oaiandGitHub 636a2594c6 Synchronize realtime notification test requests (#28946)
## What

Deliver the scripted realtime notification batch after the assistant
text append request instead of after the preceding developer text append
request.

## Why

The batch ends with an upstream error that closes the realtime
conversation. When it is emitted after the developer append, it races
the subsequent assistant append: the app-server RPC can acknowledge the
append before its downstream WebSocket send completes, and the test
intermittently observes three requests instead of four.

Making the fake server wait for the assistant append before emitting the
terminal batch establishes the ordering the test asserts without sleeps
or production-code changes.

## Validation

- `git diff --check`
- CI (the failure is timing-dependent and most reproducible in the
Windows Bazel shard)
2026-06-18 18:05:53 +00:00
iceweasel-oaiandGitHub afbb69a2fb [codex] Fix Windows sandbox runtime ACL refresh (#28943)
## Why

Codex Desktop repairs sandbox-user read/execute access for binaries
copied to `%LOCALAPPDATA%\OpenAI\Codex\bin`, but Computer Use launches
its bundled Node runtime from `%LOCALAPPDATA%\OpenAI\Codex\runtimes`.

On fresh Windows installations, `CodexSandboxUsers` may therefore be
unable to execute the bundled Node binary. The command runner starts,
but `CreateProcessAsUserW` fails with error 5 (`ACCESS_DENIED`), causing
the Node REPL to exit before Computer Use can discover applications.

This is a follow-up to #21564, which added the original runtime `bin`
ACL repair.

## What changed

- Expand the Codex Desktop runtime ACL roots from only `bin` to both
`bin` and `runtimes`.
- Apply the existing inherited read/execute ACL repair to each runtime
directory when it exists.
- Rename the setup helper to reflect that it now handles multiple
runtime paths.

## Validation

- `cargo fmt -- --check`
- `just test -p codex-windows-sandbox` was run: 113 tests passed and
five environment-dependent legacy execution tests failed because
`CreateRestrictedToken` returned error 87.
2026-06-18 11:04:30 -07:00
4c7228e423 [codex] Initialize exec-server OpenTelemetry at startup (#25019)
## Summary

- Initialize stderr tracing and the configured OpenTelemetry provider
for local and remote `codex exec-server` startup.
- Instrument the local and remote server entrypoints with a root runtime
span.
- Keep raw Noise environment, registration, and stream identifiers out
of exported spans while preserving them in local debug events.
- Keep telemetry setup in a focused CLI module instead of growing the
top-level command entrypoint.

## Stack

- Previous: none (`#27058` has merged)
- Next: #27466

## Validation

- `just test -p codex-exec-server --lib` (139 passed)
- `just test -p codex-cli --test exec_server` (3 passed)
- `just bazel-lock-check`
- `just fix -p codex-exec-server -p codex-cli`
- `just fmt`

---------

Co-authored-by: Richard Lee <richardlee@openai.com>
2026-06-18 11:03:42 -07:00
Eric TrautandGitHub e8dd1b45cb Fix goal-first live threads missing from thread/list (#28808)
Fixes #28263.

## Why

When a thread starts with `/goal`, the goal extension can update SQLite
goal state before the thread has any user-turn rollout items.
`thread/list` and `thread/search` rely on persisted listing metadata, so
a goal-first live thread could be absent from app-server listings after
restart even though the goal itself existed.

This regressed when goal handling moved out of core: the core path wrote
the goal update through the live thread rollout path, while the
extension-backed app-server path only updated goal state and emitted the
live notification.

## What

- Add `GoalSetOutcome::thread_goal_updated_item()` so the goal extension
owns the canonical `ThreadGoalUpdated` rollout item shape.
- Expose a narrow `CodexThread::append_rollout_items()` helper that
appends through the live thread and keeps derived SQLite metadata in
sync.
- When app-server sets a goal on an active live thread, persist the goal
update through that live-thread path.
- Add an app-server regression test that starts a live thread with
`thread/goal/set` and verifies it appears in state-DB-only
`thread/list`.

## Verification

- `env -u CODEX_SQLITE_HOME just test -p codex-app-server
goal_first_live_thread_appears_in_state_db_thread_list`
2026-06-18 10:50:15 -07:00
jifandGitHub 9684ec25be Add turn-scoped context contributions (#28911)
## Summary
- keep context injection on a single ContextContributor trait
- split context injection into thread-scoped and turn-scoped
contribution methods
- wire turn-scoped fragments into initial context assembly so extensions
can contribute context from turn-local state
2026-06-18 19:40:28 +02:00
jifandGitHub 790213ded0 Scope MCP sandbox metadata to server environment (#28914)
Scope MCP sandbox metadata to the MCP server's owning environment.

Previously, `codex/sandbox-state-meta` always used the turn's primary
cwd and rebuilt a legacy sandbox policy from that cwd. That can be wrong
for MCP servers owned by a different execution environment.

This now sends the owning environment cwd as a `file:` URI in
`sandboxCwd`, keeps `permissionProfile` as the permission source of
truth, and omits sandbox-state metadata when a non-default server
environment is not selected for the turn. Local/default MCP servers keep
the existing fallback cwd behavior.

Tests:
- `just fmt`
- `just bazel-lock-update`
- `just bazel-lock-check`
- `just test -p codex-mcp`
- `just test -p codex-core mcp_sandbox_cwd`
- `cargo build -p codex-rmcp-client --bin test_stdio_server`
- `just test -p codex-core
stdio_mcp_tool_call_includes_sandbox_state_meta`
2026-06-18 19:31:07 +02:00
jifandGitHub 83e6a786a2 Recover exec process stdin writes (#28895)
## Summary

Remote stdio MCP servers send tool calls by writing JSON-RPC bytes
through `process/write`.

When the exec-server websocket drops at the wrong time, the remote
process can survive session recovery, but the stdin write can still fail
back to RMCP as a transport send error. RMCP then closes the stdio MCP
transport, so tools like `node_repl` are lost even though the
process/session recovery path is working.

This changes `process/write` to be safe to retry across exec-server
recovery:

- adds a required `writeId` to `process/write`
- retries remote `Session::write` with the same `writeId` after
reconnect
- remembers accepted write ids per process so duplicate retries return
`Accepted` without writing the same bytes to child stdin again
- covers both the client retry path and server-side write id dedupe with
tests

In simple terms:

```text
before:
write to MCP stdin -> websocket closes -> write errors -> RMCP closes node_repl

after:
write to MCP stdin -> websocket closes -> reconnect -> retry same writeId
server either writes once or recognizes it already did
```
2026-06-18 19:04:26 +02:00
Eric TrautandGitHub 07298a948c Pause active goals before TUI interrupts (#28813)
Fixes #28104.

## Summary
Active `/goal` turns should leave the persisted goal paused whenever the
TUI interrupts the running turn. The bug in #28104 showed this most
visibly through `Esc`: some interrupt paths aborted the turn without
updating the goal status, so the goal could remain active and continue
automatically.

This change makes `ChatWidget` pause an active goal before the TUI sends
an interrupt from the status-row path, the pending-steer path, `Ctrl+C`,
or a request-user-input overlay. The modal overlay now reports whether a
key will interrupt the turn, which keeps modal `Esc` and `Ctrl+C`
behavior aligned with the normal interrupt paths.

## Manual Testing
Built the local CLI with `just codex --help`, then launched the local
TUI with goals enabled. Started an active `/goal` turn and interrupted
it with `Esc`, then resumed and repeated with `Ctrl+C`; both paths
showed `Goal paused`, the interrupted-conversation message, and the
`Goal paused (/goal resume)` footer. I also stopped the background
terminal and exited the TUI cleanly after the run.

I did not find a reliable standalone manual path to force the
request-user-input overlay case, so that path is covered by the focused
automated test.
2026-06-18 08:48:03 -07:00
jifandGitHub 5670360009 Avoid sandbox helper in apply_patch approval tests (#28915)
## Summary
This keeps the apply_patch approval tests focused on approval behavior
instead of macOS sandboxed filesystem helper startup.

The changed cases still force patch approval with `UnlessTrusted`, but
use `DangerFullAccess` after approval so the patch write is direct and
cheap. Workspace-write and sandbox-helper behavior remain covered by the
filesystem and apply_patch sandbox tests.
2026-06-18 15:13:55 +02:00
jifandGitHub 0369b24d54 Add network environment ID plumbing (#28766)
## Why

Prepare network approval scoping to distinguish execution environments
without changing behavior yet.

## What changed

- Add optional environment IDs to network policy requests.
- Add optional network environment IDs to exec and sandbox request
structs.
- Thread default None values through existing construction points.
- Fix stale constructor call sites that caused the CI compile failures.

## Not included

- Per-environment proxy listeners.
- Network approval cache or prompt behavior changes.
- Ambiguous request attribution handling.

Those behavior changes moved to stacked follow-up #28899.

## Validation

- just fmt
- CI will run tests and clippy
2026-06-18 14:09:38 +02:00
rka-oaiandGitHub ecc4c30e28 [codex] add rollout token budget configuration (varlength 1/N) (#28746)
## What

This PR defines the structured configuration contract for shared rollout
token budgets (across ALL agent threads under 1 rollout).

```toml
[features.rollout_budget]
enabled = true
limit_tokens = 100000
reminder_interval_tokens = 10000
sampling_token_weight = 1.0
prefill_token_weight = 0.1
```

The reminder interval defaults to 10% of the rollout limit. Sampling and
prefill weights default to `1.0`.

## Scope

This PR only defines and validates configuration. It does not track
usage, inject reminders, or stop a rollout. Accounting and reminders are
implemented in the stacked follow-up #28494.

The existing `token_budget` feature remains unchanged. `rollout_budget`
has its own feature key and configuration type.

## Tests

The config test verifies that the structured fields resolve into
`RolloutBudgetConfig` and do not enable the existing `token_budget`
feature.

Local checks:

- `just write-config-schema`
- `just test -p codex-core load_config_resolves_rollout_budget`
- `cargo check -p codex-thread-manager-sample`
- `git diff --check`

The full workspace test suite was not run locally.
2026-06-18 04:29:47 -07:00
Matthew ZengandGitHub c73296a0f0 [codex] Pass plugin namespace into skill loading (#28608)
## What changed

- retain the parsed plugin manifest namespace on loaded plugins
- carry that namespace through `PluginSkillRoot` and `SkillRoot`
- use the provided namespace when qualifying plugin skill names
- include the namespace in the skills cache key

## Why

Plugin loading has already parsed `plugin.json`, but skill parsing
currently walks every `SKILL.md` ancestor and probes/reads the manifest
again to reconstruct the same namespace. Passing the parsed namespace
removes those repeated filesystem calls, which are particularly costly
on remote filesystems.

Context:
https://openai.slack.com/archives/C0ARA9GF5D4/p1781639496496439?thread_ts=1781202444.891669&cid=C0ARA9GF5D4

## Impact

Plugin skill names remain unchanged. A regression test uses a
deliberately different on-disk manifest name to verify that plugin roots
use the provided parsed namespace.

## Validation

- `just test -p codex-core-skills -p codex-core-plugins -p codex-plugin
-p codex-utils-plugins` (352 passed)
- `just fix -p codex-core-skills -p codex-core-plugins -p codex-plugin
-p codex-utils-plugins`
- `just fmt`
2026-06-18 00:16:46 -07:00
Matthew ZengandGitHub 2c7802e7cf [codex] Split plugin and skill warmup tracing (#28605)
## What changed

- promote plugin config loading to an info-level `plugins_for_config`
span
- promote skill config loading to an info-level `skills_for_config` span
- attach stable OpenTelemetry names to both spans

## Why

`session_init.plugin_skill_warmup` currently combines plugin loading and
skill loading, which makes cold-start traces unable to identify which
phase dominates. These child spans preserve the existing aggregate while
making the two costs independently visible.

Context:
https://openai.slack.com/archives/C0ARA9GF5D4/p1781639496496439?thread_ts=1781202444.891669&cid=C0ARA9GF5D4

## Impact

This is observability-only. It does not change plugin or skill loading
behavior.

## Validation

- `just test -p codex-core-skills -p codex-core-plugins` (347 passed)
- `just fmt`
2026-06-17 22:45:10 -07:00
Adam Perry @ OpenAIandGitHub 3931bc2bde unified-exec: retain PathUri in command events (#28780)
## Why

App-server must report command events containing foreign-platform paths
without changing existing client or rollout path-string formats.

## What changed

- retain `PathUri` through exec command begin/end events
- convert cwd values to `LegacyAppPathString` at the app-server
compatibility boundary
- drop command actions with foreign paths and log them
- serialize rollout-trace cwd values using their inferred native path
representation
- restore Wine coverage for retained Windows cwd values and successful
completion
2026-06-18 05:00:04 +00:00
charlesgong-openaiandGitHub e12dd73b7d [codex] Support plugin manifest path lists (#28790)
## Summary

Allow plugin manifests to declare `skills` as either a single path
string or an array of path strings in the core plugin loader.

## Why

Some plugin packages need to expose skills from more than one directory.
Before this change, `plugin.json` only accepted a single string for
`skills`, so manifests like this were ignored as an invalid `skills`
shape:

```json
{
  "skills": ["./skills/abc", "./skills/edk"]
}
```

This keeps the existing single-string form working while adding support
for the list form. The final scope is intentionally limited to the core
plugin manifest/load path for `skills`; `apps`, file-backed
`mcpServers`, and the bundled plugin-creator assets are unchanged in
this PR.

## What changed

- Parse `skills` as either a string or an array of strings in
`plugin.json`.
- Store resolved skill paths as a list in `PluginManifestPaths`.
- Load manifest-declared skill roots in addition to the default
`./skills` root.
- Deduplicate exact duplicate skill roots before loading.
- Rely on existing skill-loader dedupe by canonical `SKILL.md` path for
overlapping roots such as `./skills` plus `./skills/abc`.
- Update plugin manifest tests to cover:
  - single string `skills`
  - list of string `skills`
  - duplicate skill roots
  - `./skills` as a manifest path
  - explicit child roots like `./skills/abc` and `./skills/edk`
  - overlapping-root dedupe

## Validation

- `just test -p codex-plugin`
- `just test -p codex-core-plugins`
- `just test -p codex-mcp-extension`
- `git diff --check`
2026-06-17 21:33:53 -07:00
Won ParkandGitHub 78a9e169bb Expose selecte namespaces as direct model tools (#28825)
## Why

Som tools, such as history and notes, must remain top-level when MCP
deferral is enabled while staying unavailable through code-mode `exec`.

## What changed

- Added `features.code_mode.direct_only_tool_namespaces`.
- Classified matching MCP tools as `DirectModelOnly`.
- Kept those tools top-level in `code_mode_only`.
- Excluded them from `tool_search` deferral and the nested `exec`
surface.
- Updated the generated config schema.

## Validation

- `code_mode_only_exposes_direct_model_only_mcp_namespaces`
- `load_config_resolves_code_mode_config`
2026-06-18 04:07:54 +00:00
Anton PanasenkoandGitHub ac3fe64100 Refresh signed exec-server URLs on reconnect (#28374)
## Summary

- add a provider API that supplies a fresh signed WebSocket URL for each
remote exec-server connection
- refresh the signed URL after disconnects and retry once when a
handshake returns `401 Unauthorized`
- allow `EnvironmentManager` consumers to register remote environments
backed by the URL provider

## Tests

- `just test -p codex-exec-server -E
'test(remote_websocket_client_refreshes_url_after_unauthorized_handshake)
| test(remote_websocket_client_refreshes_url_after_disconnect)'` — 2
passed
- `cargo check -p codex-core-api` — passed
- `just fix -p codex-exec-server` — passed
- `just fix -p codex-core-api` — no test targets; no-op
- `just fmt` — passed
- `just test -p codex-exec-server` — 187 passed; 32 unrelated macOS
sandbox tests could not invoke nested `sandbox-exec` (`Operation not
permitted`)
2026-06-17 20:58:48 -07:00
guinness-oaiandGitHub e922f46a0f [codex] Support assistant realtime append text (#28836)
## Why

Frontend realtime voice continuity needs to replay a tiny
previous-session overlap as actual conversation items, including
assistant text. The app-server `thread/realtime/appendText` API already
carries a role through to the Rust realtime websocket layer, but the
shared role enum only accepted `user` and `developer`.

## What Changed

- Added `assistant` to `ConversationTextRole` and regenerated the
app-server schema/type fixtures.
- Added `output_text` as a realtime conversation content type.
- Updated realtime websocket item creation so assistant appendText emits
`content: [{ type: "output_text", text }]`, while user and developer
continue to emit `input_text`.
- Updated app-server docs and tests to cover assistant appendText
alongside the existing developer role behavior.

## Validation

- `just write-app-server-schema`
- `just fmt` (first sandboxed attempt failed because `uv` could not
access `~/.cache/uv`; reran with filesystem access and passed)
- `just test -p codex-api` passed: 126/126
- `just test -p codex-app-server-protocol` passed: 239/239, including
generated JSON/TypeScript fixture checks
- `just test -p codex-app-server` was started locally but stopped per
request after unrelated local sandbox/Seatbelt failures (`sandbox-exec:
sandbox_apply: Operation not permitted`) and one missing local `codex`
binary failure; CI should be faster and more authoritative for the full
suite.
2026-06-17 20:57:13 -07:00
jiayuhuang-openaiandGitHub 683bd170dc [codex] control automatic realtime handoff delivery (#27986)
## What

Built on the realtime speech-control plumbing merged in #27917.

- Add optional `codexResponseHandoffPrefix` to `thread/realtime/start`.
- Apply that prefix only to automatic V1 commentary sent through
`conversation.handoff.append`; final answers remain unprefixed.
- Add opt-in `clientManagedHandoffs`. When true, core suppresses
automatic response handoffs and completion output so delivery is
controlled by explicit client append APIs.
- Preserve existing automatic behavior by default.
`codexResponsesAsItems: true` continues to select item routing when
client-managed mode is disabled.

## Why

Voice clients need two delivery policies: automatic background context
with silent commentary instructions and fully client-owned handoffs.
Phase-aware prefixing keeps routine commentary silent without
suppressing the final answer, while client-managed mode lets an app
decide exactly which updates to append.

## Validation

- `just fmt`
- `cargo test -p codex-app-server-protocol
serialize_thread_realtime_start`
- `RUST_MIN_STACK=16777216 cargo test -p codex-core --test all
conversation_handoff_persists_across_item_done_until_turn_complete`
- `RUST_MIN_STACK=16777216 cargo test -p codex-app-server --test all
webrtc_v1_client_managed_handoffs_disable_automatic_output`
- `RUST_MIN_STACK=16777216 cargo test -p codex-app-server --test all
webrtc_v1_final_automatic_handoff_omits_silent_prefix`
- `cargo build -p codex-cli --bin codex`
- Local Codex Apps compatibility check: 43 focused webview tests passed,
and a live voice session routed through the source-built app-server.

The explicit `RUST_MIN_STACK` avoids a macOS Tokio test-worker stack
overflow seen with the default test environment.
2026-06-18 02:22:29 +00:00
guinness-oaiandGitHub a306ac4ee3 [codex] Use unique IDs for realtime-routed turns (#28826)
## Why

A durable realtime voice orchestrator can reconnect and resume through
multiple fresh `Session` instances. Realtime handoffs were using the
Session-local `auto-compact-N` counter as their turn identity, but that
counter restarts at zero for every resumed Session. The durable thread
could therefore accumulate duplicate turn IDs, violating the uniqueness
assumptions made by app-server and web clients. In Codex Apps, a new
delegated response stream could be attached to an older turn with the
same ID, placing live output higher in history and putting turn-scoped
actions at risk.

Persisted rollout and reconstructed model-context order were already
correct because raw response items remain append-only and chronological.
This change restores unique identity for reconstructed and live turn
surfaces.

## What changed

- Generate a UUIDv7 specifically for each realtime-routed delegation.
- Leave the existing `auto-compact-N` identity path unchanged for actual
internal auto-compaction turns.
- Extend the inbound realtime handoff integration test to require a UUID
turn ID from `turn/started`.

## Verification

- `just test -p codex-core inbound_handoff_request_starts_turn`
- `just fix -p codex-core`
- `just fmt`
2026-06-17 19:13:38 -07:00
pakrym-oaiandGitHub dbd2857f4b [codex] Add optional IDs to response items (#28812)
## Why

`ResponseItem` variants do not have a consistent internal ID shape: some
variants carry required IDs, some carry optional IDs, and some cannot
represent an ID at all. The existing fields also use inconsistent serde,
TypeScript, and JSON-schema annotations. A single enum-level access path
is needed before history recording can assign and retain IDs.

This PR establishes that internal model only. It intentionally does not
generate or serialize IDs; allocation and wire persistence are isolated
in the stacked follow-up.

## What changed

- Give every concrete `ResponseItem` variant an `Option<String>` ID
field.
- Apply the same internal-only annotations to every ID field:
`#[serde(default, skip_serializing)]`, `#[ts(skip)]`, and
`#[schemars(skip)]`.
- Add `ResponseItem::id()` and `ResponseItem::set_id()` as the shared
accessors.
- Preserve IDs when history items are rewritten for truncation.
- Adapt consumers that previously assumed reasoning and image-generation
IDs were required.
- Regenerate app-server schemas so the hidden fields are represented
consistently.

The serde catch-all `ResponseItem::Other` remains ID-less because it
must remain a unit variant.

## Test plan

- `cargo check --tests -p codex-core -p codex-api -p codex-rollout-trace
-p codex-image-generation-extension`
- `just test -p codex-protocol`
- `just test -p codex-app-server-protocol`
- `just test -p codex-api -p codex-rollout-trace -p
codex-image-generation-extension`
- `just test -p codex-core event_mapping`
2026-06-17 18:27:43 -07:00
Anton PanasenkoandGitHub c274a83f8b feat(exec-server): add Noise rendezvous environment (#28774)
## Why

Codex can run a remote exec server through the Noise relay, but the
normal
environment-manager path could not establish an
environment-registry-backed
harness connection. Signed rendezvous URLs and harness authorizations
are
short-lived, so reconnects must fetch a fresh bundle instead of
retaining
stale connection credentials. A stalled registry request must also fail
within
the regular remote connection deadline, without exposing these
credentials in
debug logs.

Issue: N/A (internal environment-service integration).

## What Changed

- Add environment-manager configuration for a registry-backed Noise
rendezvous
  environment.
- Request a fresh bundle from
`/cloud/environment/{environment_id}/connect` for every physical harness
  connection, using the existing 10-second remote connection timeout.
- Share the Environment Registry register, connect, and validate wire
payloads
  through `codex-exec-server` and `codex-core-api`.
- Redact the signed rendezvous URL and harness authorization from the
public
  connect response's `Debug` output.
- Add focused coverage for registry bundle retrieval, stalled requests,
and
  credential redaction.
2026-06-17 17:20:53 -07:00
Adam Perry @ OpenAIandGitHub e7b6e0d859 path-uri: decouple native path parsing (#28778)
## Why

`PathUri::join` should not depend on the app-server compatibility
wrapper `LegacyAppPathString` to parse native paths. Native path parsing
belongs to the URI abstraction that it constructs.

## What

Move platform-independent native path parsing into the root `PathUri`
module. `PathUri::join` and `LegacyAppPathString` now share the
crate-private `PathUri::from_absolute_native_path` constructor.
2026-06-17 22:17:07 +00:00
Owen LinandGitHub 38211a3ff8 [codex] trace tools build latency (#28782)
Add more tracing spans around tool building.
2026-06-17 14:53:54 -07:00
Casey ChowandGitHub a760b63f83 fix(plugins): support root local marketplace plugins (#28771)
## Summary
- allow local marketplace `source.path: "."` and `source.path: "./"` to
resolve to the marketplace root
- keep `""` invalid and preserve rejection of non-root paths without
`./` plus non-normal/traversal paths
- add focused regression coverage for repo-root plugin layouts and
rejected local paths

## Tests
- `RUSTUP_TOOLCHAIN=stable just fmt`
- `RUSTUP_TOOLCHAIN=stable just test -p codex-core-plugins`
- `RUSTUP_TOOLCHAIN=stable just fix -p codex-core-plugins`

Note: plain pinned-toolchain `just fmt` was blocked locally by a rustup
`clippy` component conflict, so validation used the working stable 1.95
toolchain fallback.
2026-06-17 14:06:42 -07:00
a0586ad12d exec-server: expose environment registry payloads (#28651)
## Why

Services that proxy the exec-server environment registry endpoints need
to deserialize and forward the same Noise registration and harness-key
validation payloads. Those wire models currently live as private,
serialize-only structs in `exec-server`, which forces consumers to
duplicate the contract.

## What changed

- Add owned serde models for registration and harness-key validation
requests and responses.
- Use those models in the existing exec-server registry client.
- Re-export the models from `codex-exec-server` and `codex-core-api`.
- Keep the harness authorization request free of a derived `Debug`
implementation so it is not accidentally logged.

## Testing

- Focused exec-server registration and harness-key validation tests: 2
passed.
- `cargo check -p codex-core-api`

The full `codex-exec-server` suite compiled and ran 254 tests: 222
passed, while 32 existing filesystem sandbox tests could not run under
the nested macOS sandbox (`sandbox_apply: Operation not permitted`).

Co-authored-by: Codex <noreply@openai.com>
2026-06-17 13:27:25 -07:00
charlesgong-openaiandGitHub 3959ab0ffc [codex] Track plugin install and import telemetry failures (#28731)
## Summary
- Track plugin install failures through the unified
`codex_plugin_install_failed` event for local installs, remote install
preflight failures, bundle failures, and remote catalog/backend
failures.
- Send classified `error_type` values in plugin install failure
analytics instead of raw error strings.
- Stop sending raw external-agent import errors in analytics while
preserving raw failure details in app-facing import
notifications/history.
- Keep raw plugin/migration diagnostics in `tracing::warn!` logs.
- Keep remote failure plugin names as the existing local placeholder
(`unknown`) and remove the extra telemetry plugin-name override.
- Change `ExternalAgentConfigImportParams.source` from a generated enum
to `string | null`, with legacy `claudeCode` / `claudeCowork` inputs
normalized to existing analytics values.

## Testing
2026-06-17 13:16:34 -07:00
Adam Perry @ OpenAIandGitHub 5867b529ae unified-exec: preserve PathUri through exec-server (#28681)
## Why

It should be possible for app-server to handle "foreign" OS paths in
unified_exec working directories, allowing e.g. a Linux app-server to
run processes on e.g. a Windows exec-server.

## What

Convert the core unified_exec cwd values to use `PathUri`.

Adds fallible path conversion in several places to try to minimize the
scope of this change. The only time this change suppresses errors from
converting `PathUri` to an `AbsolutePathBuf` is when the turn is
configured with no sandboxing at all to allow us to make progress
testing without sandboxing.

Future changes to apply_patch and sandboxing will clean up these error
paths.

A tool's cwd is resolved from joining a model-provided workdir to the
environment's cwd. When using `AbsolutePathBuf::join()`, an
absolute-path workdir would overwrite the environment's cwd and we would
resolve permissions/sandboxing against the model-provided path. This
change extends `PathUri::join()` to also treat an absolute rhs as an
override of the base/lhs.

This also removes some coverage from the remove_env_windows tests until
a follow-up converts foreign paths in command exec events correctly.

## Breaking Changes

When using `AbsolutePathBuf::join()` for workdir resolution, we ended up
resolving tilde-prefixed paths against the app-server's `$HOME`, e.g.
`~/foo/bar` becomes `/home/anp/foo/bar`. It's difficult to do this with
`PathUri` joining, so after offline discussion this PR no longer
implements it.

A quick check of some power users' rollouts suggests that models don't
actually generate home-prefixed absolute working directories for their
spawns, so this shouldn't have any real blast radius.
2026-06-17 19:36:16 +00:00
Konstantine KahadzeandGitHub cca39d51ba [codex] Use compact OpenAI docs search queries (#28389)
## Summary

Updates the bundled OpenAI Docs skill to use compact, title-like search
queries. This performs better in Codex.

## Validation

- OpenAI Docs skill validation passed
- `git diff --check`
2026-06-17 19:16:18 +00:00
canvrno-oaiandGitHub b668abb488 Extract TUI plugin catalog rendering (#28768)
This mechanically extracts the existing TUI plugin catalog and detail
popup rendering from `chatwidget/plugins.rs` into a new
`chatwidget/plugin_catalog.rs` module. `plugins.rs` now keeps the
stateful plugin workflow and orchestration, while `plugin_catalog.rs`
owns the presentation-heavy catalog/detail popup construction and its
pure helpers. The goal is to keep `plugins.rs` focused before later
plugin sharing work adds more catalog behavior.

- Moves existing catalog/detail popup builders and related pure helpers
into `plugin_catalog.rs`
- Leaves plugin fetch/state/key handling in `plugins.rs`
- Adds only minimal sibling-module visibility/import wiring
- Intentionally makes no product behavior or UI changes beyond the code
move
2026-06-17 11:57:19 -07:00
Jeremy RoseandGitHub 7dc7096ae1 [codex] Restore thread recency with compatible migration history (#28671)
## Summary

- Revert #28655, restoring the thread `recencyAt` behavior introduced by
#27910.
- Move `threads_recency_at` to migration 0039 so it no longer collides
with `external_agent_config_imports` at version 0038.
- Repair databases that already applied the recency migration as version
38 by moving the matching migration-history row to version 39 before
SQLx validation. The current version-38 migration can then apply
normally.

## Validation

- `just test -p codex-state
migrations::tests::repairs_recency_migration_that_was_applied_as_version_38`
- `just test -p codex-state -p codex-rollout -p codex-thread-store -p
codex-app-server-protocol -p codex-tui`: 3,439 passed; six TUI tests
could not open the machine's existing read-only incident database at
`~/.codex/sqlite/state_5.sqlite`.
- `just fix -p codex-state`
- `just fmt`
- Verified that state migration versions are unique.
2026-06-17 18:52:18 +00:00
AdrianandGitHub d9e0551564 feat: add run task identity primitives (#19047)
## Stack

This is PR 1 of the simplified HAI single-run-task stack:

- [#19047](https://github.com/openai/codex/pull/19047) Agent Identity
assertion and task-registration primitives, including the shared
run-task helper used by existing Agent Identity JWT auth.
- [#19049](https://github.com/openai/codex/pull/19049)
Disabled-by-default ChatGPT auth opt-in that provisions/reuses persisted
Agent Identity runtime auth and its single run task.
- [#19051](https://github.com/openai/codex/pull/19051) Run-scoped
provider auth that uses one backend-owned task id for first-party
inference and compaction requests.

[#19054](https://github.com/openai/codex/pull/19054) collapsed out of
the active stack because the simplified design no longer needs a
separate background/control-plane task helper.

## Summary

The simplified POC shape is one backend-owned task per Agent Identity
run. This PR makes the first layer match that final shape directly
instead of introducing task targets, caller-owned external task refs, or
intermediate wrappers that later PRs would need to undo.

What changed:

- keeps the `AgentAssertion` wire payload as `agent_runtime_id`,
`task_id`, `timestamp`, and `signature`
- exposes `register_agent_task` as the single task-registration helper
for both existing Agent Identity JWT auth and the ChatGPT-registration
path added later in the stack
- makes task registration send only the signed registration timestamp;
the backend owns the returned opaque task id
- removes the unused target/task-kind/external-task-ref surfaces from
`codex-agent-identity`
- keeps Agent Identity JWT JWKS lookup separate from agent/task
registration URL derivation
- updates Agent Identity JWT auth to register one run task during auth
construction and share that task across cloned auth handles

This PR intentionally does not enable ChatGPT-derived Agent Identity.
That opt-in and config gate are added in the next PR.

## Testing

- `just test -p codex-agent-identity`
2026-06-17 11:23:39 -07:00
jifandGitHub 1391d786bc Scope command approvals by execution environment (#28738)
## Why

Command approval cache keys included the command and working directory,
but not the execution environment. An approval for `/workspace` locally
could therefore be reused for the same command and path on an executor.

## What changed

- Include the selected environment ID in shell and unified-exec approval
cache keys.
- Carry that ID through the normal command approval request so clients
can show which environment is being approved.
- Expose the environment through app-server as a required nullable
`environmentId` and show it in the inline TUI approval prompt.
- Keep older recorded approval events compatible when the environment is
absent.

For example, `echo ok` in local `/workspace` and `echo ok` in executor
`/workspace` now produce different approval keys and separate prompts.

## Scope

This PR does not change network approvals, Guardian review actions, MCP
elicitation, full-screen TUI rendering, or environment-ID validation.
Remote `shell_command` execution itself remains in #28722; this PR only
makes its approval key environment-aware.
2026-06-17 19:52:43 +02:00
charlesgong-openaiandGitHub 0065c3a17d [codex] Repair invalid skill frontmatter scalars (#28628)
## Why

The community marketplace audit found many skill frontmatter parse
failures where values were intended as prose, but were not valid YAML.
Common examples include unquoted scalar values with `: `, such as
`description: Build for AWS: ECS` or `argument-hint: <duration: e.g.
7d>`, and flow-looking values such as `tags: [next,@supabase/ssr]`.

`serde_yaml` does not expose a permissive mode for this. The parser
fails before unknown frontmatter fields can be ignored, so a
compatibility repair has to happen before retrying YAML parsing.

## What changed

Skill frontmatter loading still uses `serde_yaml` as the primary parser.
If that parse fails, the loader performs a line-oriented repair of
scalar frontmatter field values, then retries parsing.

The fallback now:

- applies to any frontmatter mapping field, not just `description` /
`short-description`
- quotes unquoted scalar values that contain a YAML colon separator such
as `: `
- quotes invalid flow-looking scalar values that start with `[`, `{`,
`@`, or backtick
- preserves already quoted values
- skips `|` / `>` block scalar bodies so multiline descriptions are not
rewritten
- returns the original YAML error if the repaired frontmatter still
cannot parse

## Examples

This previously failed because the second `: ` was parsed as YAML
structure:

```yaml
description: AWS deployment patterns: ECS Fargate, Lambda, and S3
```

The fallback now parses it as if it had been written explicitly as:

```yaml
description: 'AWS deployment patterns: ECS Fargate, Lambda, and S3'
```

The same repair now applies to ignored frontmatter fields that still
need to be valid YAML for the parser to get through the document:

```yaml
argument-hint: <duration: e.g. 7d, 2w>
tags: [next,@supabase/ssr]
```

Valid YAML multiline descriptions continue to work through normal
parsing without repair:

```yaml
description: |-
  Build for AWS: ECS
  and Lambda
```

## Validation

- Added loader coverage for unquoted `description` values containing `:
`.
- Added loader coverage for unquoted `metadata.short-description` values
containing `: ` and an apostrophe.
- Added loader coverage for unrecognized frontmatter fields that need
quoting, including `argument-hint` and `tags`.
- Added block-scalar coverage to ensure multiline description bodies are
preserved while other fields are repaired.
- `just test -p codex-core-skills` (106 passed)
- `just fix -p codex-core-skills`
2026-06-17 10:37:14 -07:00