Compare commits

..

147 Commits

Author SHA1 Message Date
Ahmed Ibrahim
fdfe2ca044 Wire remote MCP stdio through executor
Use the MCP server experimental_environment string to choose local stdio or executor-backed stdio at client startup time.

Co-authored-by: Codex <noreply@openai.com>
2026-04-16 14:50:30 -04:00
Ahmed Ibrahim
97fd5d45f3 Add executor-backed rmcp stdio transport
Add an rmcp transport and runtime that bridge stdio bytes through the executor process API.

Co-authored-by: Codex <noreply@openai.com>
2026-04-16 14:44:23 -04:00
Ahmed Ibrahim
0666bc7110 rmcp-client: reuse rmcp transport for stdio launcher
Return an RMCP Transport from stdio launchers and let the transport own local process cleanup state. This keeps RmcpClient on the same serve_client path for stdio as for other transports.

Co-authored-by: Codex <noreply@openai.com>
2026-04-16 14:31:01 -04:00
Ahmed Ibrahim
d94930b3fa Reorder MCP stdio launcher code
Keep the shared launcher API before the local implementation and move local launch helpers onto LocalStdioServerLauncher.\n\nCo-authored-by: Codex <noreply@openai.com>
2026-04-16 12:44:14 -04:00
Ahmed Ibrahim
6e1452d0db Clarify MCP stdio launcher abstraction
Rename the stdio runtime abstraction around launching a server so the PR boundary is about process placement, not a parallel stdio/executor runtime shape.\n\nCo-authored-by: Codex <noreply@openai.com>
2026-04-16 12:44:14 -04:00
Ahmed Ibrahim
e77367575f Abstract rmcp stdio transport runtime
Move local stdio process startup behind a runtime trait while preserving the existing local MCP stdio behavior.

Co-authored-by: Codex <noreply@openai.com>
2026-04-16 12:44:14 -04:00
Ahmed Ibrahim
53ea2a8dda Add pushed exec process events
Add a retained event stream for exec processes so subscribers can consume output and lifecycle events either live or after the process has already closed.

Co-authored-by: Codex <noreply@openai.com>
2026-04-16 12:44:09 -04:00
Ahmed Ibrahim
086ae0abe5 Support piped stdin in exec process API
Add pipe_stdin to process/start so non-tty processes can opt into a writable stdin pipe for process/write.

Co-authored-by: Codex <noreply@openai.com>
2026-04-16 12:44:06 -04:00
Won Park
3a4fa77ad7 Make yolo skip managed-network tool enforcement (#18042)
## Summary

This makes `DangerFullAccess` / yolo tool execution fully opt out of
managed-network enforcement.

Previously, yolo turns could have `turn.network` stripped while tool
orchestration still derived `enforce_managed_network=true` from
`requirements.toml.network`. That created an inconsistent state where
the turn had no managed proxy attached, but tool execution still behaved
like managed networking was active.

This updates the tool orchestration and JS REPL paths to treat managed
networking as active only when the current turn actually has
`turn.network`.

## Behavior

- Yolo / `DangerFullAccess`: no managed proxy, no managed-network
enforcement.
- Guardian / workspace-write with managed proxy: managed-network
enforcement still applies.
- Avoids the half-state where yolo has no proxy but still gets
managed-network sandbox behavior.

## Tests

- `just fmt`
- `cargo test -p codex-core
danger_full_access_tool_attempts_do_not_enforce_managed_network --
--nocapture`
- `cargo test -p codex-core danger_full_access -- --nocapture`
- `just fix -p codex-core`

Co-authored-by: jgershen-oai <jgershen@openai.com>
2026-04-16 09:06:10 -07:00
Won Park
85203d8872 Launch image generation by default (#17153)
## Summary
- Promote `image_generation` from under-development to stable
- Enable image generation by default in the feature registry
- Update feature coverage for the new launch-state expectation
- Add the missing image-generation auth fixture field in a tool registry
test

## Testing
- `just fmt`
- `cargo test -p codex-features`
- `cargo test -p codex-tools` currently fails:
`test_full_toolset_specs_for_gpt5_codex_unified_exec_web_search` needs
its expected default tool list updated for `image_generation`
2026-04-16 09:05:38 -07:00
Eric Traut
ab82568536 Fix invalid TUI resume hints (#18059)
Addresses #18011

Problem: #16987 allowed zero-token TUI exits to print resume hints,
which exposed precomputed thread ids before their rollout files were
persisted; #17222 made the same invalid hint visible when switching
sessions via `/resume`.

Solution: Only include resume commands for TUI sessions backed by a
materialized non-empty rollout, and cover both missing-rollout and
persisted-rollout summary behavior.

Testing: Manually verified by pressing Ctrl+D before the first prompt
and confirming that no "to continue this session" message was generated.
2026-04-16 09:03:55 -07:00
Eric Traut
9c56e89e4f Prefill rename prompt with current thread name (#18057)
Addresses #12178

Problem: The TUI /rename prompt opened blank even when the current
thread already had a custom name, making small edits awkward.

Solution: Let custom prompts receive initial text and prefill /rename
with the existing thread name while preserving the empty prompt for
unnamed threads.

Testing: Manually verified that the feature works by using `/rename`
with unnamed and already-named threads.
2026-04-16 09:01:45 -07:00
sayan-oai
9c6d038622 [code mode] defer mcp tools from exec description (#17287)
## Summary
- hide deferred MCP/app nested tool descriptions from the `exec` prompt
in code mode
- add short guidance that omitted nested tools are still available
through `ALL_TOOLS`
- cover the code_mode_only path with an integration test that discovers
and calls a deferred app tool

## Motivation
`code_mode_only` exposes only top-level `exec`/`wait`, but the `exec`
description could still include a large nested-tool reference. This
keeps deferred nested tools callable while avoiding that prompt bloat.

## Tests
- `just fmt`
- `just fix -p codex-code-mode`
- `just fix -p codex-tools`
- `cargo test -p codex-code-mode
exec_description_mentions_deferred_nested_tools_when_available`
- `cargo test -p codex-tools
create_code_mode_tool_matches_expected_spec`
- `cargo test -p codex-core
code_mode_only_guides_all_tools_search_and_calls_deferred_app_tools`
2026-04-17 00:01:14 +08:00
Eric Traut
8475d51655 fix(tui): remove duplicate context statusline item (#18054)
Addresses #18045

Problem: `/statusline` exposed both `context-remaining` and
`context-remaining-percent` after conflicting PRs attempted to address
the same context-status issue, including #17637, allowing duplicate
footer segments.

Solution: Remove the duplicate `context-remaining-percent` status-line
item and update status-line tests and snapshots to use only canonical
`context-remaining`.
2026-04-16 09:00:16 -07:00
Ahmed Ibrahim
b4be3617f9 [1/8] Add MCP server environment config (#18085)
## Summary
- Add an MCP server environment setting with local as the default.
- Thread the default through config serialization, schema generation,
and existing config fixtures.

## Stack
```text
o  #18027 [8/8] Fail exec client operations after disconnect
│
o  #18025 [7/8] Cover MCP stdio tests with executor placement
│
o  #18089 [6/8] Wire remote MCP stdio through executor
│
o  #18088 [5/8] Add executor process transport for MCP stdio
│
o  #18087 [4/8] Abstract MCP stdio server launching
│
o  #18020 [3/8] Add pushed exec process events
│
o  #18086 [2/8] Support piped stdin in exec process API
│
@  #18085 [1/8] Add MCP server environment config
│
o  main
```

Co-authored-by: Codex <noreply@openai.com>
2026-04-16 08:50:03 -07:00
jif-oai
b178d1cf17 chore: use justfile_directory in just file (#18146)
This was driving me crazy
2026-04-16 16:20:37 +01:00
jif-oai
76ea694db5 fix: auth preflight (#18117)
Fix app-server startup when `remote_control = true` is enabled without
ChatGPT auth.

Remote control now starts in a degraded/retrying state instead of
failing app-server initialization, so Desktop is not stranded before the
initial initialize handshake.
2026-04-16 16:17:11 +01:00
David de Regt
6adba99f4d Stabilize Bazel tests (timeout tweaks and flake fixes) (#17791) 2026-04-16 07:57:51 -07:00
jif-oai
895e2d056f nit: get rid of an expect (#18144)
Get rid of an `expect()` that caused a `panic` in the TUI

<img width="1320" height="415" alt="Screenshot 2026-04-16 at 15 30 20"
src="https://github.com/user-attachments/assets/588aaf6f-b009-4b58-8daf-56c3a9d6fe3b"
/>


Basically in `from_absolute_path` there is a `absolutize::absolutize`
that calls a `current_dir()` . But the dir in which Codex was running
got re-generated (because of Codex I guess but I can't exactly see the
source). So `current_dir()` returns an `ENOENT` and 💥
2026-04-16 15:51:52 +01:00
jif-oai
b33478c236 chore: unify memory drop endpoints (#18134)
Unify all the memories drop behind a single implementation that drops
both the main memories and the extensions
2026-04-16 15:44:23 +01:00
jif-oai
18e9ac8c75 chore: more pollution filtering (#18138) 2026-04-16 15:32:32 +01:00
jif-oai
de98b1d3e8 debug: windows flake (#18135)
Make sure Bazel logs shows every errors so that we can debug flakes +
fix a small flake on Windows by updating the sleep command to a
`Start-Sleep` instead of a PowerShell nested command (otherwise we had
double nesting which is absurdely slow)
2026-04-16 14:51:47 +01:00
jif-oai
9c326c4cb4 nit: add min values for memories (#18137)
Just add min values to some memories config fields
2026-04-16 14:37:43 +01:00
jif-oai
d4223091d0 fix: windows flake (#18127)
Fix `sqlite_feedback_logs_match_feedback_formatter_shape` by explicitly
flushing the async log DB layer before querying SQLite.
2026-04-16 13:52:21 +01:00
jif-oai
b0324f9f05 fix: more flake (#18006)
Stabilizes the Responses API proxy header test by splitting the coverage
at the right boundary:
- Core integration test now verifies parent/subagent identity headers
directly from captured `/responses` requests.
- Proxy dump unit test now verifies those identity headers are preserved
in dumped request JSON.
- Removes the flaky real proxy process + temp-file dump polling path
from the core test.
2026-04-16 10:01:45 +01:00
jackz-oai
f97be7dfff [codex] Route Fed ChatGPT auth through Fed edge (#17151)
## Summary
- parse chatgpt_account_is_fedramp from signed ChatGPT auth metadata
- add _account_is_fedramp=true to ChatGPT backend-api requests only for
FedRAMP ChatGPT-auth accounts
2026-04-16 07:13:15 +00:00
Eric Traut
4cd85b28d2 Fix MCP startup cancellation through app server (#18078)
Addresses https://github.com/openai/codex/issues/17143

Problem: TUI interrupts without an active turn stopped cancelling slow
MCP startup after routing through the app-server APIs.

Solution: Route no-active-turn interrupts through app-server as startup
cancels, acknowledge them immediately, and emit cancelled MCP startup
updates.

Testing: I manually confirmed that MCP cancellation didn't work prior to
this PR and works after the fix was in place.
2026-04-16 00:03:50 -07:00
xl-openai
48cf3ed7b0 Extract plugin loading and marketplace logic into codex-core-plugins (#18070)
Split plugin loading, marketplace, and related infrastructure out of
core into codex-core-plugins, while keeping the core-facing
configuration and orchestration flow in codex-core.

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-15 23:13:17 -07:00
Matthew Zeng
224dad41ac [codex][mcp] Add resource uri meta to tool call item. (#17831)
- [x] Add resource uri meta to tool call item so that the app-server
client can start prefetching resources immediately without loading mcp
server status.
2026-04-16 05:09:17 +00:00
Matthew Zeng
77fe33bf72 Update ToolSearch to be enabled by default (#17854)
## Summary
- Promote `Feature::ToolSearch` to `Stable` and enable it in the default
feature set
- Update feature tests and tool registry coverage to match the new
default
- Adjust the search-tool integration test to assert the default-on path
and explicit disable fallback

## Testing
- `just fmt`
- `cargo test -p codex-features`
- `cargo test -p codex-core --test all search_tool`
- `cargo test -p codex-tools`
2026-04-15 22:01:05 -07:00
pakrym-oai
bd61737e8a Async config loading (#18022)
Parts of config will come from executor. Prepare for that by making
config loading methods async.
2026-04-15 19:18:38 -07:00
canvrno-oai
d97bad1272 Display YOLO mode permissions if set when launching TUI (#17877)
- When launching the TUI client, if YOLO mode is enabled, display this
in the header.
- Eligibility is determined by `approval_policy = "never"` and
`sandbox_mode = "danger-full-access"`

<img width="886" height="230" alt="image"
src="https://github.com/user-attachments/assets/d7064778-e32c-4123-8e44-ca0c9016ab09"
/>
2026-04-15 18:28:11 -07:00
Michael Bolin
d63ba2d5ec feat: introduce codex-pr-body skill (#18033)
## Motivation

Codex needs a repeatable workflow for updating PR metadata after a pull
request already exists. This is more specific than generic GitHub
handling: the assistant needs to preserve author-provided body content,
explain why the PR exists before listing implementation details, and
describe only the net change under review, including when Sapling stacks
put a PR on top of another PR instead of `main`.

## Changes

- Adds `.codex/skills/codex-pr-body/SKILL.md`.
- Documents how to infer the target PR from the current branch or
commit, including Sapling-specific PR metadata and `sl sl` output.
- Defines the expected PR body update behavior: inspect the existing
body, preserve key content such as images, avoid local absolute paths,
use Markdown formatting, include relevant issue/PR references, and call
out developer docs follow-up only when applicable.
- Captures stacked-PR handling so generated PR text describes the change
between the PR's base and head, rather than unrelated ancestor changes.

## Verification

Not run; this is a Codex skill documentation addition.
2026-04-15 18:07:46 -07:00
Ruslan Nigmatullin
f948690fc8 [codex] Make command exec delta tests chunk tolerant (#17999)
## Summary
- Make command/exec output-delta tests accumulate streamed chunks
instead of assuming complete logical output in a single notification.
- Collect stdout and stderr independently so stream interleaving does
not fail the pipe streaming test.

## Why
The command/exec protocol exposes output as deltas, so tests should not
rely on chunk boundaries being stable. A line like `out-start\n` may
arrive split across multiple notifications, and stdout/stderr
notifications may interleave.

## Validation
- `just fmt`
- `git diff --check`
- `cargo test -p codex-app-server suite::v2::command_exec`
2026-04-15 17:57:02 -07:00
Won Park
e2dbe7dfc3 removing network proxy for yolo (#17742)
**Summary**
- prevent managed requirements.toml network settings from leaking into
DangerFullAccess / yolo turns by gating managed proxy attachment on
sandbox mode
- keep guardian/sandboxed modes on the managed proxy path, while making
true yolo bypass the proxy entirely, including /shell full-access
commands
2026-04-16 00:02:42 +00:00
bxie-openai
c2bdb7812c Clarify realtime v2 context and handoff messages (#17896)
## Summary
- wrap realtime startup context in
`<startup_context>...</startup_context>` tags
- prefix V2 mirrored user text and relayed backend text with `[USER]` /
`[BACKEND]`
- remove the V2 progress suffix and replace the final V2 handoff output
with a short completion acknowledgement while preserving the existing V1
wrapper

## Testing
- cargo test -p codex-api
realtime_v2_session_update_includes_background_agent_tool_and_handoff_output_item
-- --exact
- cargo test -p codex-app-server webrtc_v2_background_agent_
- cargo test -p codex-app-server webrtc_v2_text_input_is_
- cargo test -p codex-core conversation_user_text_turn_is_
2026-04-15 16:26:20 -07:00
evawong-oai
18d61f6923 [docs] Restore SECURITY.md update from PR 17848 (#18004)
Restore the `SECURITY.md` section from
https://github.com/openai/codex/pull/17848. Note this was lost in the
revert PR https://github.com/openai/codex/pull/18003.
2026-04-15 15:09:39 -07:00
Matthew Zeng
28b76d13fe [mcp] Add dummy tools for previously called but currently missing tools. (#17853)
- [x] Add dummy tools for previously called but currently missing tools.
Currently supporting MCP tools only.
2026-04-15 21:48:05 +00:00
efrazer-oai
9d1bf002c6 Significantly improve standalone installer (#17022)
## Summary

This PR significantly improves the standalone installer experience.

The main changes are:

1. We now install the codex binary and other dependencies in a
subdirectory under CODEX_HOME.
(`CODEX_HOME/packages/standalone/releases/...`)

2. We replace the `codex.js` launcher that npm/bun rely on with logic in
the Rust binary that automatically resolves its dependencies (like
ripgrep)

## Motivation

A few design constraints pushed this work.

1. Currently, the entrypoint to codex is through `codex.js`, which
forces a node dependency to kick off our rust app. We want to move away
from this so that the entrypoint to codex does not rely on node or
external package managers.
2. Right now, the native script adds codex and its dependencies directly
to user PATH. Given that codex is likely to add more binary dependencies
than ripgrep, we want a solution which does not add arbitrary binaries
to user PATH -- the only one we want to add is the `codex` command
itself.
3. We want upgrades to be atomic. We do not want scenarios where
interrupting an upgrade command can move codex into undefined state (for
example, having a new codex binary but an old ripgrep binary). This was
~possible with the old script.
4. Currently, the Rust binary uses heuristics to determine which
installer created it. These heuristics are flaky and are tied to the
`codex.js` launcher. We need a more stable/deterministic way to
determine how the binary was installed for standalone.
5. We do not want conflicting codex installations on PATH. For example,
the user installing via npm, then installing via brew, then installing
via standalone would make it unclear which version of codex is being
launched and make it tough for us to determine the right upgrade
command.

## Design

### Standalone package layout

Standalone installs now live under `CODEX_HOME/packages/standalone`:

```text
$CODEX_HOME/
  packages/
    standalone/
      current -> releases/0.111.0-x86_64-unknown-linux-musl
      releases/
        0.111.0-x86_64-unknown-linux-musl/
          codex
          codex-resources/
            rg
```

where `standalone/current` is a symlink to a release directory.

On Windows, the release directory has the same shape, with `.exe` names
and Windows helpers in `codex-resources`:

```text
%CODEX_HOME%\
  packages\
    standalone\
      current -> releases\0.111.0-x86_64-pc-windows-msvc
      releases\
        0.111.0-x86_64-pc-windows-msvc\
          codex.exe
          codex-resources\
            rg.exe
            codex-command-runner.exe
            codex-windows-sandbox-setup.exe
```

This gives us:
- atomic upgrades because we can fully stage a release before switching
`standalone/current`
- a stable way for the binary to recognize a standalone install from its
canonical `current_exe()` path under CODEX_HOME
- a clean place for binary dependencies like `rg`, Windows sandbox
helpers, and, in the future, our custom `zsh` etc

### Command location

On Unix, we add a symlink at `~/.local/bin/codex` which points directly
to the `$CODEX_HOME/packages/standalone/current/codex` binary. This
becomes the main entrypoint for the CLI.

On Windows, we store the link at
`%LOCALAPPDATA%\Programs\OpenAI\Codex\bin`.

### PATH persistence

This is a tricky part of the PR, as there's no ~super reliable way to
ensure that we end up on PATH without significant tradeoffs.

Most Unix variants will have `~/.local/bin` on PATH already, which means
we *should* be fine simply registering the command there in most cases.
However, there are cases where this is not the case. In these cases, we
directly edit the profile depending on the shell we're in.

- macOS zsh: `~/.zprofile`
- macOS bash: `~/.bash_profile`
- Linux zsh: `~/.zshrc`
- Linux bash: `~/.bashrc`
- fallback: `~/.profile`

On Windows, we update the User `Path` environment variable directly and
we don't need to worry about shell profiles.

### Standalone runtime detection

This PR adds a new shared crate, `codex-install-context`, which computes
install ownership once per process and caches it in a `OnceLock`.

That context includes:
- install manager (`Standalone`, `Npm`, `Bun`, `Brew`, `Other`)
- the managed standalone release directory, when applicable
- the managed standalone `codex-resources` directory, when present
- the resolved `rg_command`

The standalone path is detected by canonicalizing `current_exe()`,
canonicalizing CODEX_HOME via `find_codex_home()`, and checking whether
the binary is running from under
`$CODEX_HOME/packages/standalone/releases`.

We intentionally do not use a release metadata file. The binary path is
the source of truth.

### Dependency resolution

For standalone installs, `grep_files` now resolves bundled `rg` from
`codex-resources` next to the Codex binary.

For npm/bun/brew/other installs, `grep_files` falls back to resolving
`rg` from PATH.

For Windows standalone installs, Windows sandbox helpers are still found
as direct siblings when present. If they are not direct siblings, the
lookup also checks the sibling `codex-resources` directory.

### TUI update path

The TUI now has `UpdateAction::StandaloneUnix` and
`UpdateAction::StandaloneWindows`, which rerun the standalone install
commands.

Unix update command:

```sh
sh -c "curl -fsSL https://chatgpt.com/codex/install.sh | sh"
```

Windows update command:

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

The Windows updater runs PowerShell directly. We do this because `cmd
/C` would parse the `|iex` as a cmd pipeline instead of passing it to
PowerShell.

## Additional installer behavior

- standalone installs now warn about conflicting npm/bun/brew-managed
`codex` installs and offer to uninstall them
- same-version reruns do not redownload the release if it is already
staged locally

## Testing

Installer smoke tests run:
- macOS: fresh install into isolated `HOME` and `CODEX_HOME` with
`scripts/install/install.sh --release latest`
- macOS: reran the installer against the same isolated install to verify
the same-version/update path and PATH block idempotence
- macOS: verified the installed `codex --version` and bundled
`codex-resources/rg --version`
- Windows: parsed `scripts/install/install.ps1` with PowerShell via
`[scriptblock]::Create(...)`
- Windows: verified the standalone update action builds a direct
PowerShell command and does not route the `irm ...|iex` command through
`cmd /C`

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-15 14:44:01 -07:00
Curtis 'Fjord' Hawthorne
9e2fc31854 Support original-detail metadata on MCP image outputs (#17714)
## Summary
- honor `_meta["codex/imageDetail"] == "original"` on MCP image content
and map it to `detail: "original"` where supported
- strip that detail back out when the active model does not support
original-detail image inputs
- update code-mode `image(...)` to accept individual MCP image blocks
- teach `js_repl` / `codex.emitImage(...)` to preserve the same hint
from raw MCP image outputs
- document the new `_meta` contract and add generic RMCP-backed coverage
across protocol, core, code-mode, and js_repl paths
2026-04-15 14:43:33 -07:00
evawong-oai
17d94bd1e3 [docs] Revert extra changes from PR 17848 (#18003)
## Summary

1. Revert https://github.com/openai/codex/pull/17848 so the Bazel and
`BUILD` file changes leave `main`.
2. Prepare for a narrower follow up that restores only `SECURITY.md`.

## Validation

1. Reviewed the revert diff against `main`.
2. Ran a clean diff check before push.
2026-04-15 14:43:30 -07:00
xl-openai
e70ccdeaf7 feat: Support alternate marketplace manifests and local string (#17885)
- Discover marketplace manifests from different supported layout paths
instead of only .agents/plugins/marketplace.json.
- Accept local plugin sources written either as { source: "local", path:
... } or as a direct string path.
- Skip unsupported or invalid plugin source entries without failing the
entire marketplace, and keep valid local plugins loadable.
2026-04-15 14:16:41 -07:00
jif-oai
83dc8da9cc Re-enable it (#18002)
Reverts openai/codex#17981
2026-04-15 22:09:41 +01:00
Eugene Brevdo
bc969b6516 Dismiss stale app-server requests after remote resolution (#15134)
Dismiss stale TUI app-server approvals after remote resolution

When an approval, user-input prompt, or elicitation request is resolved
by another client, the TUI now dismisses the matching local UI instead
of leaving stale prompts behind and emitting a misleading local
cancellation.

This change teaches pending app-server request tracking to map
`serverRequest/resolved` notifications back to the concrete request type
and stable request key, then propagates that resolved request into TUI
prompt state. Approval, request-user-input, and MCP elicitation overlays
now drop the resolved current or queued request quietly, advance to the
next queued request when present, and avoid emitting abort/cancel events
for stale UI.

The latest update also retires matching prompts while they are still
deferred behind active streaming and suppresses buffered active-thread
requests whose app-server request id has already been resolved before
drain. `ChatWidget` removes a resolved request from both the deferred
interrupt queue and the materialized bottom-pane stack, while
active-thread request handling verifies the app-server request is still
pending before showing a prompt. Lifecycle events such as exec begin/end
remain queued so approved work can still render normally.

Tests cover resolved-request mapping, overlay dismissal behavior,
deferred prompt pruning for same-turn user input, exec approval IDs,
lifecycle-event retention, and the buffered active-thread ordering
regression.

Validation:
- `just fmt`
- `git diff --check`
- `cargo test -p codex-tui
resolved_buffered_approval_does_not_become_actionable_after_drain`
- `cargo test -p codex-tui
enqueue_primary_thread_session_replays_buffered_approval_after_attach`
- `cargo test -p codex-tui chatwidget::interrupts`
- `just fix -p codex-tui`

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-15 13:57:41 -07:00
starr-openai
ba36415a30 [codex] Restore remote exec-server filesystem tests (#17989)
## Summary

- Re-enable remote variants for the exec-server filesystem
sandbox/symlink tests that were made local-only in PR #17671.
- Restore `use_remote` parameterization for the readable-root,
normalized symlink escape, symlink removal, and symlink
copy-preservation cases.
- Preserve `mode={use_remote}` context on key async filesystem failures
so CI failures point at the local or remote lane.

## Validation

- `cd codex-rs && just fmt`
- Not run: `bazel test
//codex-rs/exec-server:exec-server-file_system-test` per local Codex
development guidance to avoid test runs unless explicitly requested.

Co-authored-by: Codex <noreply@openai.com>
2026-04-15 20:48:13 +00:00
Tom
50d3128269 Migrate archive/unarchive to local ThreadStore (#17892)
# Summary
- implement local ThreadStore archive/unarchive operations
- implement local ThreadStore read_thread operation
- break up the various ThreadStore local method implementations into
separate files
- migrate app-server archive/unarchive and core archive fixture to use
ThreadStore (but not all read operations yet!)
- use the ThreadStore's read operation as a proxy check for thread
persistence/existence in the app server code
- move all other filesystem operations related to archive (path
validation etc) into the local thread store.

# Tests
- add dedicated local store archive/unarchive tests
2026-04-15 20:48:09 +00:00
pakrym-oai
ab715021e6 Auto install start-codex-exec.sh dependencies (#17990) 2026-04-15 13:27:17 -07:00
evawong-oai
0bb438bca6 [docs] Add security boundaries reference in SECURITY.md (#17848)
## Summary
1. Add a Security Boundaries section to `SECURITY.md`.
2. Point readers to the Codex Agent approvals and security documentation
for sandboxing, approvals, and network controls.

## Validation
1. Reviewed the `SECURITY.md` diff in a clean worktree.
2. No tests run. Docs only change.
2026-04-15 20:12:46 +00:00
Ivan Murashko
f2a4925f63 Support remote compaction for Azure responses providers (#17958)
Azure Responses providers were still falling back to local compaction
because the compaction gate only checked
`ModelProviderInfo::is_openai()`.

Move the capability check onto `ModelProviderInfo` with
`supports_remote_compaction()`, backed by the existing Azure Responses
endpoint detection used in `codex-api`, and have `core::compact`
delegate to that helper.

Add regression coverage for:
- OpenAI providers using remote compaction
- Azure providers using remote compaction
- non-OpenAI/non-Azure providers staying on the local path

resolves #17773

---------

Co-authored-by: Michael Bolin <mbolin@openai.com>
2026-04-15 13:05:11 -07:00
jif-oai
6696e0bbc3 chore: tmp disable (#17981) 2026-04-15 20:40:41 +01:00
Dylan Hurd
81d9cde9cb chore(tui) cleanup (#17920)
## Summary
Cleanup extraneous plugins.
2026-04-15 12:36:55 -07:00
jif-oai
7e7b35b4d2 fix: propagate log db (#17953)
It restores the TRACE logs in the DB and `/feedback`
Fix https://github.com/openai/codex/pull/16184 

Result:
https://openai.sentry.io/issues/6972946529/?project=4510195390611458&query=019d91e9-f931-7451-8852-c5240514a419&referrer=issue-stream
2026-04-15 20:25:53 +01:00
Michael Bolin
66533ddc61 mcp: remove codex/sandbox-state custom request support (#17957)
## Why

#17763 moved sandbox-state delivery for MCP tool calls to request
`_meta` via the `codex/sandbox-state-meta` experimental capability.
Keeping the older `codex/sandbox-state` capability meant Codex still
maintained a second transport that pushed updates with the custom
`codex/sandbox-state/update` request at server startup and when the
session sandbox policy changed.

That duplicate MCP path is redundant with the per-tool-call metadata
path and makes the sandbox-state contract larger than needed. The
existing managed network proxy refresh on sandbox-policy changes is
still needed, so this keeps that behavior separate from the removed MCP
notification.

## What Changed

- Removed the exported `MCP_SANDBOX_STATE_CAPABILITY` and
`MCP_SANDBOX_STATE_METHOD` constants.
- Removed detection of `codex/sandbox-state` during MCP initialization
and stopped sending `codex/sandbox-state/update` at server startup.
- Removed the `McpConnectionManager::notify_sandbox_state_change`
plumbing while preserving the managed network proxy refresh when a user
turn changes sandbox policy.
- Slimmed `McpConnectionManager::new` so startup paths pass only the
initial `SandboxPolicy` needed for MCP elicitation state.
- Kept `codex/sandbox-state-meta` support intact; servers that opt in
still receive the current `SandboxState` on tool-call request `_meta`
([remaining call
path](ff2d3c1e72/codex-rs/core/src/mcp_tool_call.rs (L487-L526))).
- Added regression coverage for refreshing the live managed network
proxy on a per-turn sandbox-policy change.

## Verification

- `cargo test -p codex-core
new_turn_refreshes_managed_network_proxy_for_sandbox_change`
- `cargo test -p codex-mcp`
2026-04-15 12:02:40 -07:00
Ruslan Nigmatullin
83abf67d20 app-server: track remote-control seq IDs per stream (#17902)
## Summary

- Track outbound remote-control sequence IDs independently for each
client stream.
- Retain unacked outbound messages per stream using FIFO buffers.
- Require stream-scoped acks and update tests for contiguous per-stream
sequencing.

## Why

The remote-control peer uses outbound sequence gaps to detect lost
messages and re-initialize. A single global outbound sequence counter
can create apparent gaps on an individual stream when another stream
receives an interleaved message.

## Validation

- `just fmt`
- `cargo test -p codex-app-server remote_control`
- `just fix -p codex-app-server`
- `git diff --check`
2026-04-15 11:52:53 -07:00
pakrym-oai
f5e8eac2ae Refactor auth providers to mutate request headers (#17866)
## Summary
- Move auth header construction into the
`AuthProvider::add_auth_headers` contract.
- Inline `CoreAuthProvider` header mutation in its provider impl and
remove the shared header-map helper.
- Update HTTP, websocket, file upload, sideband websocket, and test auth
callsites to use the provider method.
- Add direct coverage for `CoreAuthProvider` auth header mutation.

## Testing
- `just fmt`
- `cargo test -p codex-api`
- `cargo test -p codex-core
client::tests::auth_request_telemetry_context_tracks_attached_auth_and_retry_phase`
- `cargo test -p codex-core` failed on unrelated/reproducible
`tools::handlers::multi_agents::tests::multi_agent_v2_followup_task_interrupts_busy_child_without_losing_message`

---------

Co-authored-by: Celia Chen <celia@openai.com>
2026-04-15 11:52:51 -07:00
Shijie Rao
f53210d332 Add CLI update announcement (#17942)
## Summary
- Keep the existing local-build test announcement as the first
announcement entry
- Add the CLI update reminder for versions below `0.120.0`
- Remove expired onboarding and gpt-5.3-codex announcement entries
<img width="1576" height="276" alt="Screenshot 2026-04-15 at 1 32 53 PM"
src="https://github.com/user-attachments/assets/10b55d0b-09cd-4de0-ab51-4293d811b80c"
/>
2026-04-15 11:39:06 -07:00
Tom
cdfcd2ca92 [codex] Add local thread store listing (#17824)
Builds on top of #17659 

Move the filesystem + sqlite thread listing-related operations inside of
a local ThreadStore implementation and call ThreadStore from the places
that used to perform these filesystem/sqlite operations.

This is the first of a series of PRs that will implement the rest of the
local ThreadStore.

Testing:
- added unit tests for the thread store implementation
- adjusted some unit tests in the realtime + personality packages whose
callsites changed. Specifically I'm trying to hide ThreadMetadata inside
of the local implementation and make ThreadMetadata a sqlite
implementation detail concern rather than a public interface, preferring
the more generate StoredThread interface instead
- added a corner case test for the personality migration package that
wasn't covered by the existing test suite
- adjust the behavior of searched thread listing to run the existing
local rollout repair/backfill pass _before_ querying SQLite results, so
callers using ThreadStore::list_threads do not miss matches after a
partial metadata warm-up
2026-04-15 11:34:27 -07:00
Shijie Rao
78ce61c78e Fix empty tool descriptions (#17946)
## Summary
- Ensure direct namespaced MCP tool groups are emitted with a non-empty
namespace description even when namespace metadata is missing or blank.
- Add regression coverage for missing MCP namespace descriptions.

## Cause
Latest `main` can serialize a direct namespaced MCP tool group with an
empty top-level `description`. The namespace description path used
`unwrap_or_default()` when `tool_namespaces` did not include metadata
for that namespace, so the outbound Responses API payload could contain
a tool like `{"type":"namespace","description":""}`. The Responses API
rejects that because namespace tool descriptions must be a non-empty
string.

## Fix
- Add a fallback namespace description: `Tools in the <namespace>
namespace.`
- Preserve provided namespace descriptions after trimming, but treat
blank descriptions as missing.

### Issue I am seeing
This is what I am seeing on the local build.
<img width="1593" height="488" alt="Screenshot 2026-04-15 at 10 55 55
AM"
src="https://github.com/user-attachments/assets/bab668ba-bf17-4c71-be4e-b102202fce57"
/>

---------

Co-authored-by: Sayan Sisodiya <sayan@openai.com>
2026-04-15 18:14:43 +00:00
Michael Bolin
aca781b3a7 fix: rename is_azure_responses_wire_base_url to is_azure_responses_provider (#17965)
## Why

While reviewing https://github.com/openai/codex/pull/17958, the helper
name `is_azure_responses_wire_base_url` looked misleading because the
helper returns true for either the `azure` provider name or an Azure
Responses `base_url`. The new name makes both inputs part of the
contract.

## What

- Rename `is_azure_responses_wire_base_url` to
`is_azure_responses_provider`.
- Move the `openai.azure.` marker into
`matches_azure_responses_base_url` so all base URL marker matching is
centralized.
- Keep `Provider::is_azure_responses_endpoint()` behavior unchanged.

## Verification

- Compared the parent and current implementations.
`name.eq_ignore_ascii_case("azure")` still returns true before
consulting `base_url`, `None` still returns false, base URLs are still
lowercased before marker matching, and the same Azure marker set is
checked.
- Ran `cargo test -p codex-api`.
2026-04-15 11:07:57 -07:00
Dylan Hurd
652380d362 chore(features) codex dependencies feat (#17960)
## Summary
Setting this up

## Testing
- [x] Unit tests pass
2026-04-15 10:59:59 -07:00
willwang-openai
a3d475d33f Fix fs/readDirectory to skip broken symlinks (#17907)
## Summary
- Skip directory entries whose metadata lookup fails during
`fs/readDirectory`
- Add an exec-server regression test covering a broken symlink beside
valid entries

## Testing
- `just fmt`
- `cargo test -p codex-exec-server` (started, but dependency/network
updates stalled before completion in this environment)
2026-04-15 10:50:22 -07:00
Adrian
8e784bba2f Register agent identities behind use_agent_identity (#17386)
## Summary

Stack PR 2 of 4 for feature-gated agent identity support.

This PR adds agent identity registration behind
`features.use_agent_identity`. It keeps the app-server protocol
unchanged and starts registration after ChatGPT auth exists rather than
requiring a client restart.

## Stack

- PR1: https://github.com/openai/codex/pull/17385 - add
`features.use_agent_identity`
- PR2: https://github.com/openai/codex/pull/17386 - this PR
- PR3: https://github.com/openai/codex/pull/17387 - register agent tasks
when enabled
- PR4: https://github.com/openai/codex/pull/17388 - use `AgentAssertion`
downstream when enabled

## Validation

Covered as part of the local stack validation pass:

- `just fmt`
- `cargo test -p codex-core --lib agent_identity`
- `cargo test -p codex-core --lib agent_assertion`
- `cargo test -p codex-core --lib websocket_agent_task`
- `cargo test -p codex-api api_bridge`
- `cargo build -p codex-cli --bin codex`

## Notes

The full local app-server E2E path is still being debugged after PR
creation. The current branch stack is directionally ready for review
while that follow-up continues.
2026-04-15 10:08:27 -07:00
pakrym-oai
1dead46c90 Remove exec-server fs sandbox request preflight (#17883)
## Summary
- Remove the exec-server-side manual filesystem request path preflight
before invoking the sandbox helper.
- Keep sandbox helper policy construction and platform sandbox
enforcement as the access boundary.
- Add a portable local+remote regression for writing through an
explicitly configured alias root.
- Remove the metadata symlink-escape assertion that depended on the
deleted manual preflight; no replacement metadata-specific access probe
is added.

## Tests
- `cargo test -p codex-exec-server --lib`
- `cargo test -p codex-exec-server --test file_system`
- `git diff --check`
2026-04-15 09:28:30 -07:00
jif-oai
da86cedbd4 feat: reset memories button (#17937)
<img width="720" height="175" alt="Screenshot 2026-04-15 at 14 35 02"
src="https://github.com/user-attachments/assets/041d73ff-8c16-42a9-8e92-c245805084f0"
/>
2026-04-15 15:34:25 +01:00
jif-oai
ec13aaac89 feat: sanitize rollouts before phase 1 (#17938) 2026-04-15 15:00:27 +01:00
jif-oai
ea13527961 nit: doc (#17941) 2026-04-15 14:51:20 +01:00
sayan-oai
0df7e9a820 register all mcp tools with namespace (#17404)
stacked on #17402.

MCP tools returned by `tool_search` (deferred tools) get registered in
our `ToolRegistry` with a different format than directly available
tools. this leads to two different ways of accessing MCP tools from our
tool catalog, only one of which works for each. fix this by registering
all MCP tools with the namespace format, since this info is already
available.

also, direct MCP tools are registered to responsesapi without a
namespace, while deferred MCP tools have a namespace. this means we can
receive MCP `FunctionCall`s in both formats from namespaces. fix this by
always registering MCP tools with namespace, regardless of deferral
status.

make code mode track `ToolName` provenance of tools so it can map the
literal JS function name string to the correct `ToolName` for
invocation, rather than supporting both in core.

this lets us unify to a single canonical `ToolName` representation for
each MCP tool and force everywhere to use that one, without supporting
fallbacks.
2026-04-15 21:02:59 +08:00
jif-oai
9402347f34 feat: memories menu (#17632)
Add menu that:
1. If memories feature is not enabled, propose to enable it
2. Let you choose if you want to generate memories and to use memories
2026-04-15 14:02:35 +01:00
jif-oai
544b4e39e3 nit: stable test (#17924) 2026-04-15 12:05:50 +01:00
jif-oai
5e544be3c9 chore: do not disable memories for past rollouts on reset (#17919) 2026-04-15 12:05:39 +01:00
sayan-oai
b99a62c526 [codex] Fix current main CI blockers (#17917)
## Summary
- Fix marketplace-add local path detection on Windows by using
`Path::is_absolute()`.
- Make marketplace-add local-source tests parse/write TOML through the
same helpers instead of raw string matching.
- Update `rand` 0.9.x to 0.9.3 and document the remaining audited `rand`
0.8.5 advisory exception.
- Refresh `MODULE.bazel.lock` after the Cargo.lock update.

## Why
Latest `main` had two independent CI blockers: marketplace-add tests
were not portable to Windows path/TOML escaping, and cargo-deny still
reported `RUSTSEC-2026-0097` after the recent rustls-webpki fix.

## Validation
- `cargo test -p codex-core marketplace_add -- --nocapture`
- `cargo deny --all-features check`
- `just bazel-lock-check`
- `just fix -p codex-core`
- `just fmt`
- `git diff --check`
2026-04-15 11:47:26 +01:00
jif-oai
af9230d74d chore: exp flag (#17921) 2026-04-15 11:47:01 +01:00
jif-oai
b6244f776d feat: cleaning of memories extension (#17844) 2026-04-15 10:38:11 +01:00
jif-oai
7579d5ad75 feat: add endpoint to delete memories (#17913) 2026-04-15 10:35:06 +01:00
jif-oai
13248008f9 fix: cargo deny (#17915) 2026-04-15 10:14:54 +01:00
aaronl-openai
2e1003728c Support Unix socket allowlists in macOS sandbox (#17654)
## Changes

Allows sandboxes to restrict overall network access while granting
access to specific unix sockets on mac.

## Details

- `codex sandbox macos`: adds a repeatable `--allow-unix-socket` option.
- `codex-sandboxing`: threads explicit Unix socket roots into the macOS
Seatbelt profile generation.
- Preserves restricted network behavior when only Unix socket IPC is
requested, and preserves full network behavior when full network is
already enabled.

## Verification

- `cargo test -p codex-cli -p codex-sandboxing`
- `cargo build -p codex-cli --bin codex`
- verified that `codex sandbox macos --allow-unix-socket /tmp/test.sock
-- test-client` grants access as expected
2026-04-15 00:53:24 -07:00
aaronl-openai
42528a905d Send sandbox state through MCP tool metadata (#17763)
## Changes

Allows MCPs to opt in to receiving sandbox config info through `_meta`
on model-initiated tool calls. This lets MCPs adhere to the thread's
sandbox if they choose to.

## Details

- Adds the `codex/sandbox-state-meta` experimental MCP capability.
- Tracks whether each MCP server advertises that capability.
- When a server opts in, `codex-core` injects the current `SandboxState`
into model-initiated MCP tool-call request `_meta`.

## Verification

- added an integration test for the capability
2026-04-15 00:49:15 -07:00
viyatb-oai
e4a3612f11 fix: add websocket capability token hash support (#17871)
## Summary
- Allow app-server websocket capability auth to accept a precomputed
SHA-256 digest via `--ws-token-sha256`.
- Keep token-file support and enforce exactly one capability token
source.
- Document the new auth flag.

## Testing
- `just fmt`
- `cargo test -p codex-app-server transport::auth::tests`
- `cargo test -p codex-app-server
websocket_capability_token_sha256_args_parse`
- `cargo test -p codex-cli app_server_capability_token_flags_parse`
- `cargo clippy -p codex-app-server --all-targets -- -D warnings`
- `just fix -p codex-cli`

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-14 22:06:39 -07:00
Michael Bolin
c6defb1f0f fix: cleanup the contract of the general-purpose exec() function (#17870)
`exec()` had a number of arguments that were unused, making the function
signature misleading. This PR aims to clean things up to clarify the
role of this function and to clarify which fields of `ExecParams` are
unused and why.
2026-04-15 04:40:12 +00:00
Michael Bolin
d34bc66466 sandbox: remove dead seatbelt helper and update tests (#17859)
## Why

`spawn_command_under_seatbelt()` in `codex-rs/core/src/seatbelt.rs` had
fallen out of production use and was only referenced by test-only
wrappers. That left us with sandbox tests that could stay green even if
the actual seatbelt exec path regressed, because production shell
execution now flows through `SandboxManager::transform()` and
`ExecRequest::from_sandbox_exec_request()` instead of that helper.

Removing the dead helper also exposed one downstream `codex-exec`
integration test that still imported it, which broke `just clippy`.

## What Changed

- Removed `codex-rs/core/src/seatbelt.rs` and stopped exporting
`codex_core::seatbelt`.
- Removed the redundant `codex-rs/core/tests/suite/seatbelt.rs` coverage
that only exercised the dead helper.
- Kept the `openpty` regression check, but moved it into
`codex-rs/core/tests/suite/exec.rs` so it now runs through
`process_exec_tool_call()`.
- Fixed the seatbelt denial test in `codex-rs/core/tests/suite/exec.rs`
to use `/usr/bin/touch`, so it actually exercises the sandbox instead of
a nonexistent path.
- Updated `codex-rs/exec/tests/suite/sandbox.rs` on macOS to build the
sandboxed command through `build_exec_request()` and spawn the
transformed command, instead of importing the removed helper.
- Left the lower-level seatbelt policy coverage in
`codex-rs/sandboxing/src/seatbelt_tests.rs`, where the policy generator
is still covered directly.

## Verification

- `cargo test -p codex-core suite::exec::`
- `cargo test -p codex-exec`
- `cargo clippy -p codex-exec --tests -- -D warnings`
2026-04-14 20:48:01 -07:00
starr-openai
e063596c67 Reuse remote exec-server in core tests (#17837)
## Summary
- reuse a shared remote exec-server for remote-aware codex-core
integration tests within a test binary process
- keep per-test remote cwd creation and cleanup so tests retain
workspace isolation
- leave codex_self_exe, codex_linux_sandbox_exe, cwd_path(), and
workspace_path() behavior unchanged

## Validation
- rustfmt codex-rs/core/tests/common/test_codex.rs
- git diff --check
- CI is running on the updated branch
2026-04-14 20:42:03 -07:00
canvrno-oai
679f63ba06 Fix clippy warnings in external agent config migration (#17884)
Fix clippy warnings in external agent config migration

```
error: this expression creates a reference which is immediately dereferenced by the compiler
   --> core/src/external_agent_config.rs:188:55
    |
188 |             let migrated = build_config_from_external(&settings)?;
    |                                                       ^^^^^^^^^ help: change this to: `settings`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.93.0/index.html#needless_borrow
    = note: requested on the command line with `-D clippy::needless-borrow`

error: useless conversion to the same type: `codex_utils_absolute_path::AbsolutePathBuf`
   --> core/src/external_agent_config.rs:355:27
    |
355 |                       match AbsolutePathBuf::try_from(
    |  ___________________________^
356 | |                         add_marketplace_outcome
357 | |                             .installed_root
358 | |                             .join(INSTALLED_MARKETPLACE_MANIFEST_RELATIVE_PATH),
359 | |                     ) {
    | |_____________________^
    |
    = help: consider removing `AbsolutePathBuf::try_from()`
    = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.93.0/index.html#useless_conversion
    = note: `-D clippy::useless-conversion` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::useless_conversion)]`

error: aborting due to 2 previous errors
```
2026-04-14 20:36:48 -07:00
guinness-oai
6f5ddd408b Wrap delegated input text (#17868)
## Summary
- wrap routed delegation text in a small XML envelope before submitting
it as a user turn
- escape XML text content so the envelope stays well formed
- update focused coverage for the wrapper and the affected routed-turn
expectations
2026-04-14 19:58:58 -07:00
Abhinav
130b047beb Disable hooks in guardian review sessions (#17872)
## What
Disable `Feature::CodexHooks` when building guardian review session
config

## Why
Guardian review sessions were respecting the Stop hook and could ingest
synthetic `<hook_prompt>` user turns Guardian should ignore hooks, while
the main session and regular subagents continue to respect them

In other words Guardian was getting ralph-looped

Co-authored-by: Codex <noreply@openai.com>
2026-04-14 19:47:50 -07:00
alexsong-oai
ca650561d6 support plugins in external agent config migration (#17855) 2026-04-14 19:39:10 -07:00
Won Park
2bfa627613 Fix for CI Tests failing from stack overflow (#17846)
### **Issue**
guardian_parallel_reviews_fork_from_last_committed_trunk_history was
failing on Windows/Bazel with a stack overflow:

`thread
'guardian::tests::guardian_parallel_reviews_fork_from_last_committed_trunk_history'
has overflowed its stack`

- This problem was a stack-headroom problem

### **Solution**

Reduced stack pressure in the guardian async path by boxing thin wrapper
futures, and run the affected test on a dedicated 2 MiB thread stack.

Concretely:
- added Box::pin(...) around thin async wrapper hops in the guardian
review/delegate path
- changed
guardian_parallel_reviews_fork_from_last_committed_trunk_history to run
inside an explicitly sized thread stack so it has enough headroom in
low-stack environments
2026-04-14 18:04:35 -07:00
xli-oai
3cc689fb23 [codex] Support local marketplace sources (#17756)
## Summary

- Port marketplace source support into the shared core marketplace-add
flow
- Support local marketplace directory sources
- Support direct `marketplace.json` URL sources
- Persist the new source types in config/schema and cover them in CLI
and app-server tests

## Validation

- `cargo test -p codex-core marketplace_add`
- `cargo test -p codex-cli marketplace_add`
- `cargo test -p codex-app-server marketplace_add`
- `just write-config-schema`
- `just fmt`
- `just fix -p codex-core`
- `just fix -p codex-cli`

## Context

Current `main` moved marketplace-add behavior into shared core code and
still assumed only git-backed sources. This change keeps that structure
but restores support for local directories and direct manifest URLs in
the shared path.
2026-04-14 15:58:14 -07:00
pakrym-oai
96254a763a Make skill loading filesystem-aware (#17720)
Migrates skill loading to support reading repo skills from the remote
environment.
2026-04-14 15:40:40 -07:00
Michael Bolin
5ecaf09ab0 Add Bazel verify-release-build job (#17705)
## Why

`main` recently needed
[#17691](https://github.com/openai/codex/pull/17691) because code behind
`cfg(not(debug_assertions))` was not being compiled by the Bazel PR
workflow. Our existing CI only built the fast/debug configuration, so
PRs could stay green while release-only Rust code still failed to
compile. This PR adds a release-style compile check that is cheap enough
to run on every PR.

## What Changed

- Added a `verify-release-build` job to `.github/workflows/bazel.yml`.
- Represented each supported OS once in that job's matrix: x64 Linux,
arm64 macOS, and x64 Windows.
- Kept the build close to fastbuild cost by using
`--compilation_mode=fastbuild` while forcing Rust to compile with
`-Cdebug-assertions=no`, which makes `cfg(not(debug_assertions))` true
without also turning on release optimizations or debug-info generation.
- Added comments in `.github/workflows/bazel.yml` and
`scripts/list-bazel-release-targets.sh` to make the job's intent and
target scope explicit.
- Restored the Bazel repository cache save behavior to run after every
non-cancelled job, matching
[#16926](https://github.com/openai/codex/pull/16926), and removed the
now-unused `repository-cache-hit` output from `prepare-bazel-ci`.
- Reused the shared `prepare-bazel-ci` action from the parent PR so the
new job does not duplicate Bazel setup boilerplate.

## Verification

- Used `bazel aquery` on `//codex-rs/tui:codex-tui` to confirm the Rust
compile still uses `opt-level=0` and `debuginfo=0` while passing
`-Cdebug-assertions=no`.
- Parsed `.github/workflows/bazel.yml` as YAML locally.
- Ran `bash -n scripts/list-bazel-release-targets.sh`.
2026-04-14 15:36:51 -07:00
malone hedges
78835d7e63 Adjust default tool search result caps (#17684)
## Summary

- Allows selected MCP results to return a larger default result set.
- Keeps the existing default cap for other MCP results.
- Applies the cap consistently when higher explicit limits are
requested.

## Testing

- `cargo test -p codex-core tool_search`
- Ran a local CLI smoke test with two stdio MCP servers exposing 100
tools each; the selected-server query returned 20 tools and the
regular-server query returned 8.
2026-04-14 14:57:19 -07:00
Ahmed Ibrahim
8b7d0e9201 Add realtime wire trace logs (#17838)
- Add trace-only wire logging for realtime websocket request/event text
payloads and the WebRTC call SDP request.
- Gate raw realtime logs behind
`RUST_LOG=codex_api::realtime_websocket::wire=trace` so normal logs stay
quiet.

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-14 14:39:28 -07:00
jif-oai
42166ba260 fix: apply patch bin refresh (#17808)
Make sure the link to apply patch binary (i.e. codex) does not die in
case of an update

Fix this:
https://openai.slack.com/archives/C08MGJXUCUQ/p1776183247771849

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-14 22:27:47 +01:00
pakrym-oai
dd1321d11b Spread AbsolutePathBuf (#17792)
Mechanical change to promote absolute paths through code.
2026-04-14 14:26:10 -07:00
Tom
dae56994da ThreadStore interface (#17659)
Introduce a ThreadStore interface for mediating access to the filesystem
(rollout jsonl files + sqlite db) based thread storage.

In later PRs we'll move the existing fs code behind a "local"
implementation of this ThreadStore interface.

This PR should be a no-op behaviorally, it only introduces the
interface.
2026-04-14 13:51:00 -07:00
rhan-oai
d6b13276c7 [codex-analytics] enable general analytics by default (#17389)
## Summary
- Make GeneralAnalytics stable and enabled by default.
- Update feature tests and app-server lifecycle fixtures for explicit
general_analytics=false.
- Keep app-server integration tests isolated from host managed config so
explicit feature fixtures are deterministic.

## Validation
- cargo test -p codex-features
- cargo test -p codex-app-server general_analytics (matched 0 tests)
- cargo test -p codex-app-server thread_start_
- cargo test -p codex-app-server thread_fork_
- cargo test -p codex-app-server thread_resume_
- cargo test -p codex-app-server
config_read_includes_system_layer_and_overrides
2026-04-14 13:20:46 -07:00
Eric Traut
1fd9c33207 [codex] Fix app-server initialized request analytics build (#17830)
Problem: PR #17372 moved initialized request handling into
`dispatch_initialized_client_request`, leaving analytics code that uses
`connection_id` without a local binding and breaking `codex-app-server`
builds.

Solution: Restore the `connection_id` binding from
`connection_request_id` before initialized request validation and
analytics tracking.
2026-04-14 13:11:04 -07:00
starr-openai
706f830dc6 Fix remote skill popup loading (#17702)
## Summary

Fix the TUI `$` skill popup so personal skills appear reliably when
Codex is connected to a remote app-server.

## What changed

- load skills on TUI startup with an explicit forced refresh
- refresh skills using the actual current cwd instead of an empty `cwds`
list
- resync an already-open `$` popup when skill mentions are updated
- add a regression test for refreshing an open mention popup

## Root cause

The TUI was sometimes sending `list_skills` with `cwds: []` after
`SessionConfigured`.

For the launchd app-server flow, the server resolved that empty cwd list
to its own process cwd, which was `/`. The response therefore came back
tagged with `cwd: "/"`, and the TUI later filtered skills by exact cwd
match against the actual project cwd such as `/Users/starr/code/dream`.
That dropped all personal skills from the mention list, so `$` only
showed plugins/apps.

## Verification

Built successfully with remote cache disabled:

```bash
cd /Users/starr/code/codex-worktrees/starr-skill-popup-20260413130509
bazel --output_base=/tmp/codex-bazel-verify-starr-skill-popup build //codex-rs/cli:codex --noremote_accept_cached --noremote_upload_local_results --disk_cache=
```

Also verified interactively in a PTY against the live app-server at
`ws://127.0.0.1:4511`:
- launched the built TUI
- typed `$`
- confirmed personal skills appeared in the popup, including entries
such as `Applied Devbox`, `CI Debug`, `Channel Summarization`, `Codex PR
Review`, and `Daily Digest`

## Files changed

- `codex-rs/tui/src/app.rs`
- `codex-rs/tui/src/chatwidget.rs`
- `codex-rs/tui/src/bottom_pane/chat_composer.rs`

Co-authored-by: Codex <noreply@openai.com>
2026-04-14 12:49:49 -07:00
starr-openai
c24124b37d Route apply_patch through the environment filesystem (#17674)
## Summary
- route apply_patch runtime execution through the selected Environment
filesystem instead of the local self-exec path
- keep the standalone apply_patch command surface intact while restoring
its launcher/test/docs contract
- add focused apply_patch filesystem sandbox regression coverage

## Validation
- remote devbox Bazel run in progress
- passed: //codex-rs/apply-patch:apply-patch-unit-tests
--test_filter=test_read_file_utf8_with_context_reports_invalid_utf8
- in progress / follow-up: focused core and exec Bazel test slices on
dev

## Follow-up under review
- remote pre-verification and approval/retry behavior still need
explicit scrutiny for delete/update flows
- runtime sandbox-denial classification may need a tighter assertion
path than rendered stderr matching

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-14 12:49:02 -07:00
Michael Bolin
440597c7e7 Refactor Bazel CI job setup (#17704)
## Why

This stack adds a new Bazel CI lane that verifies Rust code behind
`cfg(not(debug_assertions))`, but adding that job directly to
`.github/workflows/bazel.yml` would duplicate the same setup in multiple
places. Extracting the shared setup first keeps the follow-up change
easier to review and reduces the chance that future Bazel workflow edits
drift apart.

## What Changed

- Added `.github/actions/prepare-bazel-ci/action.yml` as a composite
action for the Bazel job bootstrap shared by multiple workflow jobs.
- Moved the existing Bazel setup, repository-cache restore, and
execution-log setup behind that action.
- Updated the `test` and `clippy` jobs in `.github/workflows/bazel.yml`
to call `prepare-bazel-ci`.
- Exposed `repository-cache-hit` and `repository-cache-path` outputs so
callers can keep the existing cache-save behavior without duplicating
the restore step.

## Verification

- Parsed `.github/workflows/bazel.yml` as YAML locally after rebasing
the stack.
- CI will exercise the refactored jobs end to end.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/17704).
* #17705
* __->__ #17704
2026-04-14 12:37:36 -07:00
Ruslan Nigmatullin
23d4098c0f app-server: prepare to run initialized rpcs concurrently (#17372)
## Summary

- Refactors `MessageProcessor` and per-connection session state so
initialized service RPC handling can be moved into spawned tasks in a
follow-up PR.
- Shares the processor and initialized session data with
`Arc`/`OnceLock` instead of mutable borrowed connection state.
- Keeps initialized request handling synchronous in this PR; it does
**not** call `tokio::spawn` for service RPCs yet.

## Testing

- `just fmt`
- `cargo test -p codex-app-server` *(fails on existing hardening gaps
covered by #17375, #17376, and #17377; the pipelined config regression
passed before the unrelated failures)*
- `just fix -p codex-app-server`
2026-04-14 11:24:34 -07:00
Curtis 'Fjord' Hawthorne
769b1c3d7e Keep image_detail_original as a removed feature flag (#17803) 2026-04-14 18:06:50 +00:00
Rasmus Rygaard
d013576f8b Redirect debug client output to a file (#17234)
In the app-server debug client, allow redirecting output to a file in
addition to just stdout. Shell redirecting works OK but is a bit weird
with the interactive mode of the debug client since a bunch of newlines
get dumped into the shell. With async messages from MCPs starting it's
also tricky to actually type in a prompt.
2026-04-14 09:53:17 -07:00
viyatb-oai
81c0bcc921 fix: Revert danger-full-access denylist-only mode (#17732)
## Summary

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

## Verification

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

Not run: full workspace `cargo test` (repo instructions ask for
confirmation before that broader run).
2026-04-14 09:50:14 -07:00
jif-oai
b3ae531b3a feat: codex sampler (#17784)
Add a pure sampler using the Codex auth and model config. To be used by
other binary such as tape recorder
2026-04-14 17:00:18 +01:00
David de Regt
4f2fc3e3fa Moving updated-at timestamps to unique millisecond times (#17489)
To allow the ability to have guaranteed-unique cursors, we make two
important updates:
* Add new updated_at_ms and created_at_ms columns that are in
millisecond precision
* Guarantee uniqueness -- if multiple items are inserted at the same
millisecond, bump the new one by one millisecond until it becomes unique

This lets us use single-number cursors for forwards and backwards paging
through resultsets and guarantee that the cursor is a fixed point to do
(timestamp > cursor) and get new items only.

This updated implementation is backwards-compatible since multiple
appservers can be running and won't handle the previous method well.
2026-04-14 11:55:34 -04:00
marksteinbrick-oai
61fe23159e [codex-analytics] add session source to client metadata (#17374)
## Summary

Adds `thread_source` field to the existing Codex turn metadata sent to
Responses API
- Sends `thread_source: "user"` for user-initiated sessions: CLI, VS
Code, and Exec
- Sends `thread_source: "subagent"` for subagent sessions
- Omits `thread_source` for MCP, custom, and unknown session sources
- Uses the existing turn metadata transport:
  - HTTP requests send through the `x-codex-turn-metadata` header
- WebSocket `response.create` requests send through
`client_metadata["x-codex-turn-metadata"]`

## Testing
- `cargo test -p codex-protocol
session_source_thread_source_name_classifies_user_and_subagent_sources`
- `cargo test -p codex-core turn_metadata_state`
- `cargo test -p codex-core --test responses_headers
responses_stream_includes_turn_metadata_header_for_git_workspace_e2e --
--nocapture`
2026-04-14 08:55:12 -07:00
Curtis 'Fjord' Hawthorne
f030ab62eb Always enable original image detail on supported models (#17665)
## Summary

This PR removes `image_detail_original` as a runtime experiment and
makes original image detail available whenever the selected model
supports it.

Concretely, this change:
- drops the `image_detail_original` feature flag from the feature
registry and generated config schema
- makes tool-emitted image detail depend only on
`ModelInfo.supports_image_detail_original`
- updates `view_image` and `code_mode`/`js_repl` image emission to use
that capability check directly
- removes now-redundant experiment-specific tests and instruction
coverage
- keeps backward compatibility for existing configs by silently ignoring
a stale `features.image_detail_original` entry

The net effect is that `detail: "original"` is always available on
supported models, without requiring an experiment toggle.
2026-04-14 08:15:56 -07:00
jif-oai
e6947f85f6 feat: add context percent to status line (#17637)
Co-authored-by: Codex <noreply@openai.com>
2026-04-14 14:27:24 +01:00
jif-oai
34a9ca083e nit: feature flag (#17777) 2026-04-14 13:44:01 +01:00
Ahmed Ibrahim
2f6fc7c137 Add realtime output modality and transcript events (#17701)
- Add outputModality to thread/realtime/start and wire text/audio output
selection through app-server, core, API, and TUI.\n- Rename the realtime
transcript delta notification and add a separate transcript done
notification that forwards final text from item done without correlating
it with deltas.
2026-04-14 00:13:13 -07:00
Ahmed Ibrahim
a6b03a22cc Log realtime call location (#17761)
Add a trace-level log for the realtime call Location header when
decoding the call id.
2026-04-13 23:33:51 -07:00
rhan-oai
b704df85b8 [codex-analytics] feature plumbing and emittance (#16640)
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/16640).
* #16870
* #16706
* #16641
* __->__ #16640
2026-04-13 23:11:49 -07:00
Thibault Sottiaux
05c5829923 [codex] drain mailbox only at request boundaries (#17749)
This changes multi-agent v2 mailbox handling so incoming inter-agent
messages no longer preempt an in-flight sampling stream at reasoning or
commentary output-item boundaries.
2026-04-13 22:09:51 -07:00
pakrym-oai
ad37389c18 [codex] Initialize ICU data for code mode V8 (#17709)
Link ICU data into code mode, otherwise locale-dependent methods cause a
panic and a crash.
2026-04-13 22:01:58 -07:00
pakrym-oai
3b24a9a532 Refactor plugin loading to async (#17747)
Simplifies skills migration.
2026-04-13 21:52:56 -07:00
xli-oai
ff584c5a4b [codex] Refactor marketplace add into shared core flow (#17717)
## Summary

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

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

## Why

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

## Validation

- `cargo test -p codex-core marketplace_add`
- `cargo test -p codex-cli marketplace_cmd`
- `just fix -p codex-core`
- `just fix -p codex-cli`
- `just fmt`
2026-04-13 20:37:11 -07:00
viyatb-oai
d9a385ac8c fix: pin inputs (#17471)
## Summary
- Pin Rust git patch dependencies to immutable revisions and make
cargo-deny reject unknown git and registry sources unless explicitly
allowlisted.
- Add checked-in SHA-256 coverage for the current rusty_v8 release
assets, wire those hashes into Bazel, and verify CI override downloads
before use.
- Add rusty_v8 MODULE.bazel update/check tooling plus a Bazel CI guard
so future V8 bumps cannot drift from the checked-in checksum manifest.
- Pin release/lint cargo installs and all external GitHub Actions refs
to immutable inputs.

## Future V8 bump flow
Run these after updating the resolved `v8` crate version and checksum
manifest:

```bash
python3 .github/scripts/rusty_v8_bazel.py update-module-bazel
python3 .github/scripts/rusty_v8_bazel.py check-module-bazel
```

The update command rewrites the matching `rusty_v8_<crate_version>`
`http_file` SHA-256 values in `MODULE.bazel` from
`third_party/v8/rusty_v8_<crate_version>.sha256`. The check command is
also wired into Bazel CI to block drift.

## Notes
- This intentionally excludes RustSec dependency upgrades and
bubblewrap-related changes per request.
- The branch was rebased onto the latest origin/main before opening the
PR.

## Validation
- cargo fetch --locked
- cargo deny check advisories
- cargo deny check
- cargo deny check sources
- python3 .github/scripts/rusty_v8_bazel.py check-module-bazel
- python3 .github/scripts/rusty_v8_bazel.py update-module-bazel
- python3 -m unittest discover -s .github/scripts -p
'test_rusty_v8_bazel.py'
- python3 -m py_compile .github/scripts/rusty_v8_bazel.py
.github/scripts/rusty_v8_module_bazel.py
.github/scripts/test_rusty_v8_bazel.py
- repo-wide GitHub Actions `uses:` audit: all external action refs are
pinned to 40-character SHAs
- yq eval on touched workflows and local actions
- git diff --check
- just bazel-lock-check

## Hash verification
- Confirmed `MODULE.bazel` hashes match
`third_party/v8/rusty_v8_146_4_0.sha256`.
- Confirmed GitHub release asset digests for denoland/rusty_v8
`v146.4.0` and openai/codex `rusty-v8-v146.4.0` match the checked-in
hashes.
- Streamed and SHA-256 hashed all 10 `MODULE.bazel` rusty_v8 asset URLs
locally; every downloaded byte stream matched both `MODULE.bazel` and
the checked-in manifest.

## Pin verification
- Confirmed signing-action pins match the peeled commits for their tag
comments: `sigstore/cosign-installer@v3.7.0`, `azure/login@v2`, and
`azure/trusted-signing-action@v0`.
- Pinned the remaining tag-based action refs in Bazel CI/setup:
`actions/setup-node@v6`, `facebook/install-dotslash@v2`,
`bazelbuild/setup-bazelisk@v3`, and `actions/cache/restore@v5`.
- Normalized all `bazelbuild/setup-bazelisk@v3` refs to the peeled
commit behind the annotated tag.
- Audited Cargo git dependencies: every manifest git dependency uses
`rev` only, every `Cargo.lock` git source has `?rev=<sha>#<same-sha>`,
and `cargo deny check sources` passes with `required-git-spec = "rev"`.
- Shallow-fetched each distinct git dependency repo at its pinned SHA
and verified Git reports each object as a commit.
2026-04-14 01:45:41 +00:00
pakrym-oai
0c8f3173e4 [codex] Remove unused Rust helpers (#17146)
## Summary

Removes high-confidence unused Rust helper functions and exports across
`codex-tui`, `codex-shell-command`, and utility crates.

The cleanup includes dead TUI helper methods, unused
path/string/elapsed/fuzzy-match utilities, an unused Windows PowerShell
lookup helper, and the unused terminal palette version counter. This
keeps the remaining public surface smaller without changing behavior.

## Validation

- `just fmt`
- `cargo test -p codex-tui -p codex-shell-command -p codex-utils-elapsed
-p codex-utils-fuzzy-match -p codex-utils-string -p codex-utils-path`
- `just fix -p codex-tui -p codex-shell-command -p codex-utils-elapsed
-p codex-utils-fuzzy-match -p codex-utils-string -p codex-utils-path`
- `git diff --check`
2026-04-13 18:27:00 -07:00
pakrym-oai
f3cbe3d385 [codex] Add symlink flag to fs metadata (#17719)
Add `is_symlink` to FsMetadata struct.
2026-04-13 17:46:56 -07:00
Won Park
495ed22dfb guardian timeout fix pr 3 - ux touch for timeouts (#17557)
This PR teaches the TUI to render guardian review timeouts as explicit
terminal history entries instead of dropping them from the live
timeline.
It adds timeout-specific history cells for command, patch, MCP tool, and
network approval reviews.
It also adds snapshot tests covering both the direct guardian event path
and the app-server notification path.
2026-04-13 17:43:19 -07:00
starr-openai
280a4a6d42 Stabilize exec-server filesystem tests in CI (#17671)
## Summary\n- add an exec-server package-local test helper binary that
can run exec-server and fs-helper flows\n- route exec-server filesystem
tests through that helper instead of cross-crate codex helper
binaries\n- stop relying on Bazel-only extra binary wiring for these
tests\n\n## Testing\n- not run (per repo guidance for codex changes)

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-13 16:53:42 -07:00
pakrym-oai
d4be06adea Add turn item injection API (#17703)
## Summary
- Add `turn/inject_items` app-server v2 request support for appending
raw Responses API items to a loaded thread history without starting a
turn.
- Generate JSON schema and TypeScript protocol artifacts for the new
params and empty response.
- Document the new endpoint and include a request/response example.
- Preserve compatibility with the typo alias `turn/injet_items` while
returning the canonical method name.

## Testing
- Not run (not requested)
2026-04-13 16:11:05 -07:00
josiah-openai
937dd3812d Add supports_parallel_tool_calls flag to included mcps (#17667)
## Why

For more advanced MCP usage, we want the model to be able to emit
parallel MCP tool calls and have Codex execute eligible ones
concurrently, instead of forcing all MCP calls through the serial block.

The main design choice was where to thread the config. I made this
server-level because parallel safety depends on the MCP server
implementation. Codex reads the flag from `mcp_servers`, threads the
opted-in server names into `ToolRouter`, and checks the parsed
`ToolPayload::Mcp { server, .. }` at execution time. That avoids relying
on model-visible tool names, which can be incomplete in
deferred/search-tool paths or ambiguous for similarly named
servers/tools.

## What was added

Added `supports_parallel_tool_calls` for MCP servers.

Before:

```toml
[mcp_servers.docs]
command = "docs-server"
```

After:

```toml
[mcp_servers.docs]
command = "docs-server"
supports_parallel_tool_calls = true
```

MCP calls remain serial by default. Only tools from opted-in servers are
eligible to run in parallel. Docs also now warn to enable this only when
the server’s tools are safe to run concurrently, especially around
shared state or read/write races.

## Testing

Tested with a local stdio MCP server exposing real delay tools. The
model/Responses side was mocked only to deterministically emit two MCP
calls in the same turn.

Each test called `query_with_delay` and `query_with_delay_2` with `{
"seconds": 25 }`.

| Build/config | Observed | Wall time |
| --- | --- | --- |
| main with flag enabled | serial | `58.79s` |
| PR with flag enabled | parallel | `31.73s` |
| PR without flag | serial | `56.70s` |

PR with flag enabled showed both tools start before either completed;
main and PR-without-flag completed the first delay before starting the
second.

Also added an integration test.

Additional checks:

- `cargo test -p codex-tools` passed
- `cargo test -p codex-core
mcp_parallel_support_uses_exact_payload_server` passed
- `git diff --check` passed
2026-04-13 15:16:34 -07:00
Ahmed Ibrahim
0e31dc0d4a change realtime tool description (#17699)
# External (non-OpenAI) Pull Request Requirements

Before opening this Pull Request, please read the dedicated
"Contributing" markdown file or your PR may be closed:
https://github.com/openai/codex/blob/main/docs/contributing.md

If your PR conforms to our contribution guidelines, replace this text
with a detailed and high quality description of your changes.

Include a link to a bug report or enhancement request.
2026-04-13 14:31:31 -07:00
Ahmed Ibrahim
ec0133f5f8 Cap realtime mirrored user turns (#17685)
Cap mirrored user text sent to realtime with the existing 300-token turn
budget while preserving the full model turn.

Adds integration coverage for capped realtime mirror payloads.

---------

Co-authored-by: Codex <noreply@openai.com>
2026-04-13 14:31:18 -07:00
Kevin Liu
ecdd733a48 Remove unnecessary tests (#17395)
# External (non-OpenAI) Pull Request Requirements

Before opening this Pull Request, please read the dedicated
"Contributing" markdown file or your PR may be closed:
https://github.com/openai/codex/blob/main/docs/contributing.md

If your PR conforms to our contribution guidelines, replace this text
with a detailed and high quality description of your changes.

Include a link to a bug report or enhancement request.
2026-04-13 21:02:12 +00:00
Kevin Liu
ec72b1ced9 Update phase 2 memory model to gpt-5.4 (#17384)
### Motivation
- Switch the default model used for memory Phase 2 (consolidation) to
the newer `gpt-5.4` model.

### Description
- Change the Phase 2 model constant from `"gpt-5.3-codex"` to
`"gpt-5.4"` in `codex-rs/core/src/memories/mod.rs`.

### Testing
- Ran `just fmt`, which completed successfully.
- Attempted `cargo test -p codex-core`, but the build failed in this
environment because the `codex-linux-sandbox` crate requires the system
`libcap` pkg-config entry and the required system packages could not be
installed, so the test run was blocked.

------
[Codex
Task](https://chatgpt.com/codex/cloud/tasks/task_i_69d977693b48832a967e78d73c66dc8e)
2026-04-13 20:59:03 +00:00
David Z Hao
7c43f8bb5e Fix tui compilation (#17691)
The recent release broke, codex suggested this as the fix

Source failure:
https://github.com/openai/codex/actions/runs/24362949066/job/71147202092

Probably from
ac82443d07

For why it got in:
```
The relevant setup:

.github/workflows/rust-ci.yml (line 1) runs on PRs, but for codex-rs it only does:

cargo fmt --check
cargo shear
argument-comment lint via Bazel
no cargo check, no cargo clippy over the workspace, no cargo test over codex-tui
.github/workflows/rust-ci-full.yml (line 1) runs on pushes to main and branches matching **full-ci**. That one does compile TUI because:

codex-rs/Cargo.toml includes "tui" as a workspace member
lint_build runs cargo clippy --target ... --tests --profile ...
the matrix includes both dev and release profiles
tests runs cargo nextest run ..., but only dev-profile tests
Release CI also compiles it indirectly. .github/workflows/rust-release.yml (line 235) builds --bin codex, and cli/Cargo.toml (line 46) depends on codex-tui.
```

Codex tested locally with `cargo check -p codex-tui --release` and was
able to repro, and verified that this fixed it
2026-04-13 21:43:33 +01:00
iceweasel-oai
7b5e1ad3dc only specify remote ports when the rule needs them (#17669)
Windows gives an error when you combine `protocol = ANY` with
`SetRemotePorts`
This fixes that
2026-04-13 12:28:26 -07:00
Ruslan Nigmatullin
a5507b59c4 app-server: Only unload threads which were unused for some time (#17398)
Currently app-server may unload actively running threads once the last
connection disconnects, which is not expected.
Instead track when was the last active turn & when there were any
subscribers the last time, also add 30 minute idleness/no subscribers
timer to reduce the churn.
2026-04-13 12:25:26 -07:00
jif-oai
d905376628 feat: Avoid reloading curated marketplaces for tool-suggest discovera… (#17638)
- stop `list_tool_suggest_discoverable_plugins()` from reloading the
curated marketplace for each discoverable plugin
- reuse a direct plugin-detail loader against the already-resolved
marketplace entry


The trigger was to stop those logs spamming:
```
d=019d81cf-6f69-7230-98aa-74294ff2dc5a}:submission_dispatch{otel.name="op.dispatch.user_input" submission.id="019d86c8-0a8e-7013-b442-109aabbf75c9" codex.op="user_input"}:turn{otel.name="session_task.turn" thread.id=019d81cf-6f69-7230-98aa-74294ff2dc5a turn.id=019d86c8-0a8e-7013-b442-109aabbf75c9 model=gpt-5.4}: ignoring interface.defaultPrompt: prompt must be at most 128 characters path=/Users/jif/.codex/.tmp/plugins/plugins/life-science-research/.codex-plugin/plugin.json
2026-04-13T12:27:30.402Z WARN  [019d81cf-6f69-7230-98aa-74294ff2dc5a] codex_core::plugins::manifest - session_loop{thread_id=019d81cf-6f69-7230-98aa-74294ff2dc5a}:submission_dispatch{otel.name="op.dispatch.user_input" submission.id="019d86c8-0a8e-7013-b442-109aabbf75c9" codex.op="user_input"}:turn{otel.name="session_task.turn" thread.id=019d81cf-6f69-7230-98aa-74294ff2dc5a turn.id=019d86c8-0a8e-7013-b442-109aabbf75c9 model=gpt-5.4}: ignoring interface.defaultPrompt: prompt must be at most 128 characters path=/Users/jif/.codex/.tmp/plugins/plugins/build-ios-apps/.codex-plugin/plugin.json
2026-04-13T12:27:30.402Z WARN  [019d81cf-6f69-7230-98aa-74294ff2dc5a] codex_core::plugins::manifest - session_loop{thread_id=019d81cf-6f69-7230-98aa-74294ff2dc5a}:submission_dispatch{otel.name="op.dispatch.user_input" submission.id="019d86c8-0a8e-7013-b442-109aabbf75c9" codex.op="user_input"}:turn{otel.name="session_task.turn" thread.id=019d81cf-6f69-7230-98aa-74294ff2dc5a turn.id=019d86c8-0a8e-7013-b442-109aabbf75c9 model=gpt-5.4}: ignoring interface.defaultPrompt: prompt must be at most 128 characters path=/Users/jif/.codex/.tmp/plugins/plugins/life-science-research/.codex-plugin/plugin.json
2026-04-13T12:27:30.405Z WARN  [019d81cf-6f69-7230-98aa-74294ff2dc5a] codex_core::plugins::manifest - session_loop{thread_id=019d81cf-6f69-7230-98aa-74294ff2dc5a}:submission_dispatch{otel.name="op.dispatch.user_input" submission.id="019d86c8-0a8e-7013-b442-109aabbf75c9" codex.op="user_input"}:turn{otel.name="session_task.turn" thread.id=019d81cf-6f69-7230-98aa-74294ff2dc5a turn.id=019d86c8-0a8e-7013-b442-109aabbf75c9 model=gpt-5.4}: ignoring interface.defaultPrompt: prompt must be at most 128 characters path=/Users/jif/.codex/.tmp/plugins/plugins/build-ios-apps/.codex-plugin/plugin.json
2026-04-13T12:27:30.406Z WARN  [019d81cf-6f69-7230-98aa-74294ff2dc5a] codex_core::plugins::manifest - session_loop{thread_id=019d81cf-6f69-7230-98aa-74294ff2dc5a}:submission_dispatch{otel.name="op.dispatch.user_input" submission.id="019d86c8-0a8e-7013-b442-109aabbf75c9" codex.op="user_input"}:turn{otel.name="session_task.turn" thread.id=019d81cf-6f69-7230-98aa-74294ff2dc5a turn.id=019d86c8-0a8e-7013-b442-109aabbf75c9 model=gpt-5.4}: ignoring interface.defaultPrompt: prompt must be at most 128 characters path=/Users/jif/.codex/.tmp/plugins/plugins/life-science-research/.codex-plugin/plugin.json
2026-04-13T12:27:30.408Z WARN  [019d81cf-6f69-7230-98aa-74294ff2dc5a] codex_core::plugins::manifest - session_loop{thread_id=019d81cf-6f69-7230-98aa-74294ff2dc5a}:submission_dispatch{otel.name="op.dispatch.user_input" submission.id="019d86c8-0a8e-7013-b442-109aabbf75c9" codex.op="user_input"}:turn{otel.name="session_task.turn" thread.id=019d81cf-6f69-7230-98aa-74294ff2dc5a turn.id=019d86c8-0a8e-7013-b442-109aabbf75c9 model=gpt-5.4}: ignoring interface.defaultPrompt: prompt must be at most 128 characters path=/Users/jif/.codex/.tmp/plugins/plugins/build-ios-apps/.codex-plugin/plugin.json
```
2026-04-13 19:08:43 +00:00
iceweasel-oai
0131f99fd5 Include legacy deny paths in elevated Windows sandbox setup (#17365)
## Summary

This updates the Windows elevated sandbox setup/refresh path to include
the legacy `compute_allow_paths(...).deny` protected children in the
same deny-write payload pipe added for split filesystem carveouts.

Concretely, elevated setup and elevated refresh now both build
deny-write payload paths from:

- explicit split-policy deny-write paths, preserving missing paths so
setup can materialize them before applying ACLs
- legacy `compute_allow_paths(...).deny`, which includes existing
`.git`, `.codex`, and `.agents` children under writable roots

This lets the elevated backend protect `.git` consistently with the
unelevated/restricted-token path, and removes the old janky hard-coded
`.codex` / `.agents` elevated setup helpers in favor of the shared
payload path.

## Root Cause

The landed split-carveout PR threaded a `deny_write_paths` pipe through
elevated setup/refresh, but the legacy workspace-write deny set from
`compute_allow_paths(...).deny` was not included in that payload. As a
result, elevated workspace-write did not apply the intended deny-write
ACLs for existing protected children like `<cwd>/.git`.

## Notes

The legacy protected children still only enter the deny set if they
already exist, because `compute_allow_paths` filters `.git`, `.codex`,
and `.agents` with `exists()`. Missing explicit split-policy deny paths
are preserved separately because setup intentionally materializes those
before applying ACLs.

## Validation

- `cargo fmt --check -p codex-windows-sandbox`
- `cargo test -p codex-windows-sandbox`
- `cargo build -p codex-cli -p codex-windows-sandbox --bins`
- Elevated `codex exec` smoke with `windows.sandbox='elevated'`: fresh
git repo, attempted append to `.git/config`, observed `Access is
denied`, marker not written, Deny ACE present on `.git`
- Unelevated `codex exec` smoke with `windows.sandbox='unelevated'`:
fresh git repo, attempted append to `.git/config`, observed `Access is
denied`, marker not written, Deny ACE present on `.git`
2026-04-13 10:49:42 -07:00
jif-oai
46a266cd6a feat: disable memory endpoint (#17626) 2026-04-13 18:29:49 +01:00
pakrym-oai
ac82443d07 Use AbsolutePathBuf in skill loading and codex_home (#17407)
Helps with FS migration later
2026-04-13 10:26:51 -07:00
Eric Traut
d25a9822a7 Do not fail thread start when trust persistence fails (#17595)
Addresses #17593

Problem: A regression introduced in
https://github.com/openai/codex/pull/16492 made thread/start fail when
Codex could not persist trusted project state, which crashes startup for
users with read-only config.toml.

Solution: Treat trusted project persistence as best effort and keep the
current thread's config trusted in memory when writing config.toml
fails.
2026-04-13 10:03:21 -07:00
Eric Traut
313ad29ad7 Fix TUI compaction item replay (#17657)
Problem: PR #17601 updated context-compaction replay to call a new
ChatWidget handler, but the handler was never implemented, breaking
codex-tui compilation on main.

Solution: Render context-compaction replay through the existing
info-message path, preserving the intended `Context compacted` UI marker
without adding a one-off handler.
2026-04-13 09:20:10 -07:00
Eric Traut
7c797c6544 Suppress duplicate compaction and terminal wait events (#17601)
Addresses #17514

Problem: PR #16966 made the TUI render the deprecated context-compaction
notification, while v2 could also receive legacy unified-exec
interaction items alongside terminal-interaction notifications, causing
duplicate "Context compacted" and "Waited for background terminal"
messages.

Solution: Suppress deprecated context-compaction notifications and
legacy unified-exec interaction command items from the app-server v2
projection, and render canonical context-compaction items through the
existing TUI info-event path.
2026-04-13 08:59:19 -07:00
Eric Traut
370be363f1 Wrap status reset timestamps in narrow layouts (#17481)
Addresses #17453

Problem: /status rate-limit reset timestamps can be truncated in narrow
layouts, leaving users with partial times or dates.

Solution: Let narrow rate-limit rows drop the fixed progress bar to
preserve the percent summary, and wrap reset timestamps onto
continuation lines instead of truncating them.
2026-04-13 08:53:37 -07:00
Eric Traut
ce5ad7b295 Emit plan-mode prompt notifications for questionnaires (#17417)
Addresses #17252

Problem: Plan-mode clarification questionnaires used the generic
user-input notification type, so configs listening for plan-mode-prompt
did not fire when request_user_input waited for an answer.

Solution: Map request_user_input prompts to the plan-mode-prompt
notification and remove the obsolete user-input TUI notification
variant.
2026-04-13 08:52:14 -07:00
Eric Traut
a5783f90c9 Fix custom tool output cleanup on stream failure (#17470)
Addresses #16255

Problem: Incomplete Responses streams could leave completed custom tool
outputs out of cleanup and retry prompts, making persisted history
inconsistent and retries stale.

Solution: Route stream and output-item errors through shared cleanup,
and rebuild retry prompts from fresh session history after the first
attempt.
2026-04-13 08:35:17 -07:00
friel-openai
776246c3f5 Make forked agent spawns keep parent model config (#17247)
## Summary

When a `spawn_agent` call does a full-history fork, keep the parent's
effective agent type and model configuration instead of applying child
role/model overrides.

This is the minimal config-inheritance slice of #16055. Prompt-cache key
inheritance and MCP tool-surface stability are split into follow-up PRs.

## Design

- Reject `agent_type`, `model`, and `reasoning_effort` for v1
`fork_context` spawns.
- Reject `agent_type`, `model`, and `reasoning_effort` for v2
`fork_turns = "all"` spawns.
- Keep v2 partial-history forks (`fork_turns = "N"`) configurable;
requested model/reasoning overrides and role config still apply there.
- Keep non-forked spawn behavior unchanged.

## Tests

- `cargo +1.93.1 test -p codex-core spawn_agent_fork_context --lib`
- `cargo +1.93.1 test -p codex-core multi_agent_v2_spawn_fork_turns
--lib`
- `cargo +1.93.1 test -p codex-core
multi_agent_v2_spawn_partial_fork_turns_allows_agent_type_override
--lib`
2026-04-13 15:28:40 +00:00
jif-oai
3f62b5cc61 fix: dedup compact (#17643) 2026-04-13 16:08:53 +01:00
jif-oai
49ca7c9f24 fix: stability exec server (#17640) 2026-04-13 14:52:12 +01:00
jif-oai
86bd0bc95c nit: change consolidation model (#17633) 2026-04-13 13:02:07 +01:00
jif-oai
bacb92b1d7 Build remote exec env from exec-server policy (#17216)
## Summary
- add an exec-server `envPolicy` field; when present, the server starts
from its own process env and applies the shell environment policy there
- keep `env` as the exact environment for local/embedded starts, but
make it an overlay for remote unified-exec starts
- move the shell-environment-policy builder into `codex-config` so Core
and exec-server share the inherit/filter/set/include behavior
- overlay only runtime/sandbox/network deltas from Core onto the
exec-server-derived env

## Why
Remote unified exec was materializing the shell env inside Core and
forwarding the whole map to exec-server, so remote processes could
inherit the orchestrator machine's `HOME`, `PATH`, etc. This keeps the
base env on the executor while preserving Core-owned runtime additions
like `CODEX_THREAD_ID`, unified-exec defaults, network proxy env, and
sandbox marker env.

## Validation
- `just fmt`
- `git diff --check`
- `cargo test -p codex-exec-server --lib`
- `cargo test -p codex-core --lib unified_exec::process_manager::tests`
- `cargo test -p codex-core --lib exec_env::tests`
- `cargo test -p codex-core --lib exec_env_tests` (compile-only; filter
matched 0 tests)
- `cargo test -p codex-config --lib shell_environment` (compile-only;
filter matched 0 tests)
- `just bazel-lock-update`

## Known local validation issue
- `just bazel-lock-check` is not runnable in this checkout: it invokes
`./scripts/check-module-bazel-lock.sh`, which is missing.

---------

Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: pakrym-oai <pakrym@openai.com>
2026-04-13 09:59:08 +01:00
jif-oai
4ffe6c2ce6 feat: ignore keyring on 0.0.0 (#17221)
To prevent the spammy: 
<img width="424" height="172" alt="Screenshot 2026-04-09 at 13 36 16"
src="https://github.com/user-attachments/assets/b5ece9e3-c561-422f-87ec-041e7bd6813d"
/>
2026-04-13 09:58:47 +01:00
Eric Traut
6550007cca Stabilize exec-server process tests (#17605)
Problem: After #17294 switched exec-server tests to launch the top-level
`codex exec-server` command, parallel remote exec-process cases can
flake while waiting for the child server's listen URL or transport
shutdown.

Solution: Serialize remote exec-server-backed process tests and harden
the harness so spawned servers are killed on drop and shutdown waits for
the child process to exit.
2026-04-13 00:31:13 -07:00
683 changed files with 34888 additions and 11113 deletions

View File

@@ -0,0 +1,59 @@
---
name: codex-pr-body
description: Update the title and body of one or more pull requests.
---
## Determining the PR(s)
When this skill is invoked, the PR(s) to update may be specified explicitly, but in the common case, the PR(s) to update will be inferred from the branch / commit that the user is currently working on. For ordinary Git usage (i.e., not Sapling as discussed below), you may have to use a combination of `git branch` and `gh pr view <branch> --repo openai/codex --json number --jq '.number'` to determine the PR associated with the current branch / commit.
## PR Body Contents
When invoked, use `gh` to edit the pull request body and title to reflect the contents of the specified PR. Make sure to check the existing pull request body to see if there is key information that should be preserved. For example, NEVER remove an image in the existing pull request body, as the author may have no way to recover it if you remove it.
It is critically important to explain _why_ the change is being made. If the current conversation in which this skill is invoked has discussed the motivation, be sure to capture this in the pull request body.
The body should also explain _what_ changed, but this should appear after the _why_.
Limit discussion to the _net change_ of the commit. It is generally frowned upon to discuss changes that were attempted but later undone in the course of the development of the pull request. When rewriting the pull request body, you may need to eliminate details such as these when they are no longer appropriate / of interest to future readers.
Avoid references to absolute paths on my local disk. When talking about a path that is within the repository, simply use the repo-relative path.
It is generally helpful to discuss how the change was verified. That said, it is unnecessary to mention things that CI checks automatically, e.g., do not include "ran `just fmt`" as part of the test plan. Though identifying the new tests that were purposely introduced to verify the new behavior introduced by the pull request is often appropriate.
Make use of Markdown to format the pull request professionally. Ensure "code things" appear in single backticks when referenced inline. Fenced code blocks are useful when referencing code or showing a shell transcript. Also, make use of GitHub permalinks when citing existing pieces of code that are relevant to the change.
Make sure to reference any relevant pull requests or issues, though there should be no need to reference the pull request in its own PR body.
If there is documentation that should be updated on https://developers.openai.com/codex as a result of this change, please note that in a separate section near the end of the pull request. Omit this section if there is no documentation that needs to be updated.
## Working with Stacks
Sometimes a pull request is composed of a stack of commits that build on one another. In these cases, the PR body should reflect the _net_ change introduced by the stack as a whole, rather than the individual commits that make up the stack.
Similarly, sometimes a user may be using a tool like Sapling to leverage _stacked pull requests_, in which case the `base` of the PR may be the a branch that is the `head` of another PR in the stack rather than `main`. In this case, be sure to discuss only the net change between the `base` and `head` of the PR that is being opened against that stacked base, rather than the changes relative to `main`.
## Sapling
If `.git/sl/store` is present, then this Git repository is governed by Sapling SCM (https://sapling-scm.com).
In Sapling, run the following to see if there is a GitHub pull request associated with the current revision:
```shell
sl log --template '{github_pull_request_url}' -r .
```
Alternatively, you can run `sl sl` to see the current development branch and whether there is a GitHub pull request associated with the current commit. For example, if the output were:
```
@ cb032b31cf 72 minutes ago mbolin #11412
╭─╯ tui: show non-file layer content in /debug-config
o fdd0cd1de9 Today at 20:09 origin/main
~
```
- `@` indicates the current commit is `cb032b31cf`
- it is a development branch containing a single commit branched off of `origin/main`
- it is associated with GitHub pull request #11412

View File

@@ -12,7 +12,7 @@ runs:
using: composite
steps:
- name: Install cosign
uses: sigstore/cosign-installer@v3.7.0
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
- name: Cosign Linux artifacts
shell: bash

View File

@@ -0,0 +1,44 @@
name: prepare-bazel-ci
description: Prepare a Bazel CI job with shared setup, repository cache restore, and execution logs.
inputs:
target:
description: Target triple used for setup and cache namespacing.
required: true
install-test-prereqs:
description: Install Node.js and DotSlash for Bazel-backed test jobs.
required: false
default: "false"
outputs:
repository-cache-path:
description: Filesystem path used for the Bazel repository cache.
value: ${{ steps.setup_bazel.outputs.repository-cache-path }}
runs:
using: composite
steps:
- name: Set up Bazel CI
id: setup_bazel
uses: ./.github/actions/setup-bazel-ci
with:
target: ${{ inputs.target }}
install-test-prereqs: ${{ inputs.install-test-prereqs }}
# Restore the Bazel repository cache explicitly so external dependencies
# do not need to be re-downloaded on every CI run. Keep restore failures
# non-fatal so transient cache-service errors degrade to a cold build
# instead of failing the job.
- name: Restore bazel repository cache
id: cache_bazel_repository_restore
continue-on-error: true
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: ${{ steps.setup_bazel.outputs.repository-cache-path }}
key: bazel-cache-${{ inputs.target }}-${{ hashFiles('MODULE.bazel', 'codex-rs/Cargo.lock', 'codex-rs/Cargo.toml') }}
restore-keys: |
bazel-cache-${{ inputs.target }}
- name: Set up Bazel execution logs
shell: bash
run: |
mkdir -p "${RUNNER_TEMP}/bazel-execution-logs"
echo "CODEX_BAZEL_EXECUTION_LOG_COMPACT_DIR=${RUNNER_TEMP}/bazel-execution-logs" >> "${GITHUB_ENV}"

View File

@@ -18,7 +18,7 @@ runs:
steps:
- name: Set up Node.js for js_repl tests
if: inputs.install-test-prereqs == 'true'
uses: actions/setup-node@v6
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with:
node-version-file: codex-rs/node-version.txt
@@ -26,7 +26,7 @@ runs:
# See https://github.com/openai/codex/pull/7617.
- name: Install DotSlash
if: inputs.install-test-prereqs == 'true'
uses: facebook/install-dotslash@v2
uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2
- name: Make DotSlash available in PATH (Unix)
if: inputs.install-test-prereqs == 'true' && runner.os != 'Windows'
@@ -39,7 +39,7 @@ runs:
run: Copy-Item (Get-Command dotslash).Source -Destination "$env:LOCALAPPDATA\Microsoft\WindowsApps\dotslash.exe"
- name: Set up Bazel
uses: bazelbuild/setup-bazelisk@v3
uses: bazelbuild/setup-bazelisk@b39c379c82683a5f25d34f0d062761f62693e0b2 # v3
- name: Configure Bazel repository cache
id: configure_bazel_repository_cache

View File

@@ -0,0 +1,49 @@
name: setup-rusty-v8-musl
description: Download and verify musl rusty_v8 artifacts for Cargo builds.
inputs:
target:
description: Rust musl target triple.
required: true
runs:
using: composite
steps:
- name: Configure musl 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}"
binding_dir="${RUNNER_TEMP}/rusty_v8"
archive_path="${binding_dir}/librusty_v8_release_${TARGET}.a.gz"
binding_path="${binding_dir}/src_binding_release_${TARGET}.rs"
checksums_path="${binding_dir}/rusty_v8_release_${TARGET}.sha256"
checksums_source="${GITHUB_WORKSPACE}/third_party/v8/rusty_v8_${version//./_}.sha256"
mkdir -p "${binding_dir}"
curl -fsSL "${base_url}/librusty_v8_release_${TARGET}.a.gz" -o "${archive_path}"
curl -fsSL "${base_url}/src_binding_release_${TARGET}.rs" -o "${binding_path}"
grep -E " (librusty_v8_release_${TARGET}[.]a[.]gz|src_binding_release_${TARGET}[.]rs)$" \
"${checksums_source}" > "${checksums_path}"
if [[ "$(wc -l < "${checksums_path}")" -ne 2 ]]; then
echo "Expected exactly two checksums for ${TARGET} in ${checksums_source}" >&2
exit 1
fi
(cd "${binding_dir}" && sha256sum -c "${checksums_path}")
echo "RUSTY_V8_ARCHIVE=${archive_path}" >> "${GITHUB_ENV}"
echo "RUSTY_V8_SRC_BINDING_PATH=${binding_path}" >> "${GITHUB_ENV}"

View File

@@ -27,14 +27,14 @@ runs:
using: composite
steps:
- name: Azure login for Trusted Signing (OIDC)
uses: azure/login@v2
uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v2
with:
client-id: ${{ inputs.client-id }}
tenant-id: ${{ inputs.tenant-id }}
subscription-id: ${{ inputs.subscription-id }}
- name: Sign Windows binaries with Azure Trusted Signing
uses: azure/trusted-signing-action@v0
uses: azure/trusted-signing-action@1d365fec12862c4aa68fcac418143d73f0cea293 # v0
with:
endpoint: ${{ inputs.endpoint }}
trusted-signing-account-name: ${{ inputs.account-name }}

View File

@@ -92,8 +92,13 @@ print_bazel_test_log_tails() {
for target in "${failed_targets[@]}"; do
local rel_path="${target#//}"
rel_path="${rel_path/:/\/}"
rel_path="${rel_path/://}"
local test_log="${testlogs_dir}/${rel_path}/test.log"
local reported_test_log
reported_test_log="$(grep -F "FAIL: ${target} " "$console_log" | sed -nE 's#.* \(see ([^)]+/test\.log)\).*#\1#p' | head -n 1 || true)"
if [[ -n "$reported_test_log" ]]; then
test_log="$reported_test_log"
fi
echo "::group::Bazel test log tail for ${target}"
if [[ -f "$test_log" ]]; then

View File

@@ -4,6 +4,7 @@ from __future__ import annotations
import argparse
import gzip
import hashlib
import re
import shutil
import subprocess
@@ -12,8 +13,16 @@ import tempfile
import tomllib
from pathlib import Path
from rusty_v8_module_bazel import (
RustyV8ChecksumError,
check_module_bazel,
update_module_bazel,
)
ROOT = Path(__file__).resolve().parents[2]
MODULE_BAZEL = ROOT / "MODULE.bazel"
RUSTY_V8_CHECKSUMS_DIR = ROOT / "third_party" / "v8"
MUSL_RUNTIME_ARCHIVE_LABELS = [
"@llvm//runtimes/libcxx:libcxx.static",
"@llvm//runtimes/libcxx:libcxxabi.static",
@@ -146,6 +155,24 @@ def resolved_v8_crate_version() -> str:
return matches[0]
def rusty_v8_checksum_manifest_path(version: str) -> Path:
return RUSTY_V8_CHECKSUMS_DIR / f"rusty_v8_{version.replace('.', '_')}.sha256"
def command_version(version: str | None) -> str:
if version is not None:
return version
return resolved_v8_crate_version()
def command_manifest_path(manifest: Path | None, version: str) -> Path:
if manifest is None:
return rusty_v8_checksum_manifest_path(version)
if manifest.is_absolute():
return manifest
return ROOT / manifest
def staged_archive_name(target: str, source_path: Path) -> str:
if source_path.suffix == ".lib":
return f"rusty_v8_release_{target}.lib.gz"
@@ -244,8 +271,18 @@ def stage_release_pair(
shutil.copyfile(binding_path, staged_binding)
staged_checksums = output_dir / f"rusty_v8_release_{target}.sha256"
with staged_checksums.open("w", encoding="utf-8") as checksums:
for path in [staged_library, staged_binding]:
digest = hashlib.sha256()
with path.open("rb") as artifact:
for chunk in iter(lambda: artifact.read(1024 * 1024), b""):
digest.update(chunk)
checksums.write(f"{digest.hexdigest()} {path.name}\n")
print(staged_library)
print(staged_binding)
print(staged_checksums)
def parse_args() -> argparse.Namespace:
@@ -264,6 +301,24 @@ def parse_args() -> argparse.Namespace:
subparsers.add_parser("resolved-v8-crate-version")
check_module_bazel_parser = subparsers.add_parser("check-module-bazel")
check_module_bazel_parser.add_argument("--version")
check_module_bazel_parser.add_argument("--manifest", type=Path)
check_module_bazel_parser.add_argument(
"--module-bazel",
type=Path,
default=MODULE_BAZEL,
)
update_module_bazel_parser = subparsers.add_parser("update-module-bazel")
update_module_bazel_parser.add_argument("--version")
update_module_bazel_parser.add_argument("--manifest", type=Path)
update_module_bazel_parser.add_argument(
"--module-bazel",
type=Path,
default=MODULE_BAZEL,
)
return parser.parse_args()
@@ -280,6 +335,22 @@ def main() -> int:
if args.command == "resolved-v8-crate-version":
print(resolved_v8_crate_version())
return 0
if args.command == "check-module-bazel":
version = command_version(args.version)
manifest_path = command_manifest_path(args.manifest, version)
try:
check_module_bazel(args.module_bazel, manifest_path, version)
except RustyV8ChecksumError as exc:
raise SystemExit(str(exc)) from exc
return 0
if args.command == "update-module-bazel":
version = command_version(args.version)
manifest_path = command_manifest_path(args.manifest, version)
try:
update_module_bazel(args.module_bazel, manifest_path, version)
except RustyV8ChecksumError as exc:
raise SystemExit(str(exc)) from exc
return 0
raise SystemExit(f"unsupported command: {args.command}")

230
.github/scripts/rusty_v8_module_bazel.py vendored Normal file
View File

@@ -0,0 +1,230 @@
#!/usr/bin/env python3
from __future__ import annotations
import re
from dataclasses import dataclass
from pathlib import Path
SHA256_RE = re.compile(r"[0-9a-f]{64}")
HTTP_FILE_BLOCK_RE = re.compile(r"(?ms)^http_file\(\n.*?^\)\n?")
class RustyV8ChecksumError(ValueError):
pass
@dataclass(frozen=True)
class RustyV8HttpFile:
start: int
end: int
block: str
name: str
downloaded_file_path: str
sha256: str | None
def parse_checksum_manifest(path: Path) -> dict[str, str]:
try:
lines = path.read_text(encoding="utf-8").splitlines()
except FileNotFoundError as exc:
raise RustyV8ChecksumError(f"missing checksum manifest: {path}") from exc
checksums: dict[str, str] = {}
for line_number, line in enumerate(lines, 1):
if not line.strip():
continue
parts = line.split()
if len(parts) != 2:
raise RustyV8ChecksumError(
f"{path}:{line_number}: expected '<sha256> <filename>'"
)
checksum, filename = parts
if not SHA256_RE.fullmatch(checksum):
raise RustyV8ChecksumError(
f"{path}:{line_number}: invalid SHA-256 digest for {filename}"
)
if not filename or filename in {".", ".."} or "/" in filename:
raise RustyV8ChecksumError(
f"{path}:{line_number}: expected a bare artifact filename"
)
if filename in checksums:
raise RustyV8ChecksumError(
f"{path}:{line_number}: duplicate checksum for {filename}"
)
checksums[filename] = checksum
if not checksums:
raise RustyV8ChecksumError(f"empty checksum manifest: {path}")
return checksums
def string_field(block: str, field: str) -> str | None:
# Matches one-line string fields inside http_file blocks, e.g. `sha256 = "...",`.
match = re.search(rf'^\s*{re.escape(field)}\s*=\s*"([^"]+)",\s*$', block, re.M)
if match:
return match.group(1)
return None
def rusty_v8_http_files(module_bazel: str, version: str) -> list[RustyV8HttpFile]:
version_slug = version.replace(".", "_")
name_prefix = f"rusty_v8_{version_slug}_"
entries = []
for match in HTTP_FILE_BLOCK_RE.finditer(module_bazel):
block = match.group(0)
name = string_field(block, "name")
if not name or not name.startswith(name_prefix):
continue
downloaded_file_path = string_field(block, "downloaded_file_path")
if not downloaded_file_path:
raise RustyV8ChecksumError(
f"MODULE.bazel {name} is missing downloaded_file_path"
)
entries.append(
RustyV8HttpFile(
start=match.start(),
end=match.end(),
block=block,
name=name,
downloaded_file_path=downloaded_file_path,
sha256=string_field(block, "sha256"),
)
)
return entries
def module_entry_set_errors(
entries: list[RustyV8HttpFile],
checksums: dict[str, str],
version: str,
) -> list[str]:
errors = []
if not entries:
errors.append(f"MODULE.bazel has no rusty_v8 http_file entries for {version}")
return errors
module_files: dict[str, RustyV8HttpFile] = {}
duplicate_files = set()
for entry in entries:
if entry.downloaded_file_path in module_files:
duplicate_files.add(entry.downloaded_file_path)
module_files[entry.downloaded_file_path] = entry
for filename in sorted(duplicate_files):
errors.append(f"MODULE.bazel has duplicate http_file entries for {filename}")
for filename in sorted(set(module_files) - set(checksums)):
entry = module_files[filename]
errors.append(f"MODULE.bazel {entry.name} has no checksum in the manifest")
for filename in sorted(set(checksums) - set(module_files)):
errors.append(f"manifest has {filename}, but MODULE.bazel has no http_file")
return errors
def module_checksum_errors(
entries: list[RustyV8HttpFile],
checksums: dict[str, str],
) -> list[str]:
errors = []
for entry in entries:
expected = checksums.get(entry.downloaded_file_path)
if expected is None:
continue
if entry.sha256 is None:
errors.append(f"MODULE.bazel {entry.name} is missing sha256")
elif entry.sha256 != expected:
errors.append(
f"MODULE.bazel {entry.name} has sha256 {entry.sha256}, "
f"expected {expected}"
)
return errors
def raise_checksum_errors(message: str, errors: list[str]) -> None:
if errors:
formatted_errors = "\n".join(f"- {error}" for error in errors)
raise RustyV8ChecksumError(f"{message}:\n{formatted_errors}")
def check_module_bazel_text(
module_bazel: str,
checksums: dict[str, str],
version: str,
) -> None:
entries = rusty_v8_http_files(module_bazel, version)
errors = [
*module_entry_set_errors(entries, checksums, version),
*module_checksum_errors(entries, checksums),
]
raise_checksum_errors("rusty_v8 MODULE.bazel checksum drift", errors)
def block_with_sha256(block: str, checksum: str) -> str:
sha256_line_re = re.compile(r'(?m)^(\s*)sha256\s*=\s*"[0-9a-f]+",\s*$')
if sha256_line_re.search(block):
return sha256_line_re.sub(
lambda match: f'{match.group(1)}sha256 = "{checksum}",',
block,
count=1,
)
downloaded_file_path_match = re.search(
r'(?m)^(\s*)downloaded_file_path\s*=\s*"[^"]+",\n',
block,
)
if not downloaded_file_path_match:
raise RustyV8ChecksumError("http_file block is missing downloaded_file_path")
insert_at = downloaded_file_path_match.end()
indent = downloaded_file_path_match.group(1)
return f'{block[:insert_at]}{indent}sha256 = "{checksum}",\n{block[insert_at:]}'
def update_module_bazel_text(
module_bazel: str,
checksums: dict[str, str],
version: str,
) -> str:
entries = rusty_v8_http_files(module_bazel, version)
errors = module_entry_set_errors(entries, checksums, version)
raise_checksum_errors("cannot update rusty_v8 MODULE.bazel checksums", errors)
updated = []
previous_end = 0
for entry in entries:
updated.append(module_bazel[previous_end : entry.start])
updated.append(
block_with_sha256(entry.block, checksums[entry.downloaded_file_path])
)
previous_end = entry.end
updated.append(module_bazel[previous_end:])
return "".join(updated)
def check_module_bazel(
module_bazel_path: Path,
manifest_path: Path,
version: str,
) -> None:
checksums = parse_checksum_manifest(manifest_path)
module_bazel = module_bazel_path.read_text(encoding="utf-8")
check_module_bazel_text(module_bazel, checksums, version)
print(f"{module_bazel_path} rusty_v8 {version} checksums match {manifest_path}")
def update_module_bazel(
module_bazel_path: Path,
manifest_path: Path,
version: str,
) -> None:
checksums = parse_checksum_manifest(manifest_path)
module_bazel = module_bazel_path.read_text(encoding="utf-8")
updated_module_bazel = update_module_bazel_text(module_bazel, checksums, version)
if updated_module_bazel == module_bazel:
print(f"{module_bazel_path} rusty_v8 {version} checksums are already current")
return
module_bazel_path.write_text(updated_module_bazel, encoding="utf-8")
print(f"updated {module_bazel_path} rusty_v8 {version} checksums")

126
.github/scripts/test_rusty_v8_bazel.py vendored Normal file
View File

@@ -0,0 +1,126 @@
#!/usr/bin/env python3
from __future__ import annotations
import textwrap
import unittest
import rusty_v8_module_bazel
class RustyV8BazelTest(unittest.TestCase):
def test_update_module_bazel_replaces_and_inserts_sha256(self) -> None:
module_bazel = textwrap.dedent(
"""\
http_file(
name = "rusty_v8_146_4_0_x86_64_unknown_linux_gnu_archive",
downloaded_file_path = "librusty_v8_release_x86_64-unknown-linux-gnu.a.gz",
sha256 = "0000000000000000000000000000000000000000000000000000000000000000",
urls = [
"https://example.test/librusty_v8_release_x86_64-unknown-linux-gnu.a.gz",
],
)
http_file(
name = "rusty_v8_146_4_0_x86_64_unknown_linux_musl_binding",
downloaded_file_path = "src_binding_release_x86_64-unknown-linux-musl.rs",
urls = [
"https://example.test/src_binding_release_x86_64-unknown-linux-musl.rs",
],
)
http_file(
name = "rusty_v8_145_0_0_x86_64_unknown_linux_gnu_archive",
downloaded_file_path = "librusty_v8_release_x86_64-unknown-linux-gnu.a.gz",
sha256 = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
urls = [
"https://example.test/old.gz",
],
)
"""
)
checksums = {
"librusty_v8_release_x86_64-unknown-linux-gnu.a.gz": (
"1111111111111111111111111111111111111111111111111111111111111111"
),
"src_binding_release_x86_64-unknown-linux-musl.rs": (
"2222222222222222222222222222222222222222222222222222222222222222"
),
}
updated = rusty_v8_module_bazel.update_module_bazel_text(
module_bazel,
checksums,
"146.4.0",
)
self.assertEqual(
textwrap.dedent(
"""\
http_file(
name = "rusty_v8_146_4_0_x86_64_unknown_linux_gnu_archive",
downloaded_file_path = "librusty_v8_release_x86_64-unknown-linux-gnu.a.gz",
sha256 = "1111111111111111111111111111111111111111111111111111111111111111",
urls = [
"https://example.test/librusty_v8_release_x86_64-unknown-linux-gnu.a.gz",
],
)
http_file(
name = "rusty_v8_146_4_0_x86_64_unknown_linux_musl_binding",
downloaded_file_path = "src_binding_release_x86_64-unknown-linux-musl.rs",
sha256 = "2222222222222222222222222222222222222222222222222222222222222222",
urls = [
"https://example.test/src_binding_release_x86_64-unknown-linux-musl.rs",
],
)
http_file(
name = "rusty_v8_145_0_0_x86_64_unknown_linux_gnu_archive",
downloaded_file_path = "librusty_v8_release_x86_64-unknown-linux-gnu.a.gz",
sha256 = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
urls = [
"https://example.test/old.gz",
],
)
"""
),
updated,
)
rusty_v8_module_bazel.check_module_bazel_text(updated, checksums, "146.4.0")
def test_check_module_bazel_rejects_manifest_drift(self) -> None:
module_bazel = textwrap.dedent(
"""\
http_file(
name = "rusty_v8_146_4_0_x86_64_unknown_linux_gnu_archive",
downloaded_file_path = "librusty_v8_release_x86_64-unknown-linux-gnu.a.gz",
sha256 = "1111111111111111111111111111111111111111111111111111111111111111",
urls = [
"https://example.test/librusty_v8_release_x86_64-unknown-linux-gnu.a.gz",
],
)
"""
)
checksums = {
"librusty_v8_release_x86_64-unknown-linux-gnu.a.gz": (
"1111111111111111111111111111111111111111111111111111111111111111"
),
"orphan.gz": (
"2222222222222222222222222222222222222222222222222222222222222222"
),
}
with self.assertRaisesRegex(
rusty_v8_module_bazel.RustyV8ChecksumError,
"manifest has orphan.gz",
):
rusty_v8_module_bazel.check_module_bazel_text(
module_bazel,
checksums,
"146.4.0",
)
if __name__ == "__main__":
unittest.main()

View File

@@ -51,38 +51,24 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Set up Bazel CI
id: setup_bazel
uses: ./.github/actions/setup-bazel-ci
- name: Check rusty_v8 MODULE.bazel checksums
if: matrix.os == 'ubuntu-24.04' && matrix.target == 'x86_64-unknown-linux-gnu'
shell: bash
run: |
python3 .github/scripts/rusty_v8_bazel.py check-module-bazel
python3 -m unittest discover -s .github/scripts -p test_rusty_v8_bazel.py
- name: Prepare Bazel CI
id: prepare_bazel
uses: ./.github/actions/prepare-bazel-ci
with:
target: ${{ matrix.target }}
install-test-prereqs: "true"
# Restore the Bazel repository cache explicitly so external dependencies
# do not need to be re-downloaded on every CI run. Keep restore failures
# non-fatal so transient cache-service errors degrade to a cold build
# instead of failing the job.
- name: Restore bazel repository cache
id: cache_bazel_repository_restore
continue-on-error: true
uses: actions/cache/restore@v5
with:
path: ${{ steps.setup_bazel.outputs.repository-cache-path }}
key: bazel-cache-${{ matrix.target }}-${{ hashFiles('MODULE.bazel', 'codex-rs/Cargo.lock', 'codex-rs/Cargo.toml') }}
restore-keys: |
bazel-cache-${{ matrix.target }}
- name: Check MODULE.bazel.lock is up to date
if: matrix.os == 'ubuntu-24.04' && matrix.target == 'x86_64-unknown-linux-gnu'
shell: bash
run: ./scripts/check-module-bazel-lock.sh
- name: Set up Bazel execution logs
shell: bash
run: |
mkdir -p "${RUNNER_TEMP}/bazel-execution-logs"
echo "CODEX_BAZEL_EXECUTION_LOG_COMPACT_DIR=${RUNNER_TEMP}/bazel-execution-logs" >> "${GITHUB_ENV}"
- name: bazel test //...
env:
BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }}
@@ -123,14 +109,14 @@ jobs:
path: ${{ runner.temp }}/bazel-execution-logs
if-no-files-found: ignore
# Save bazel repository cache explicitly; make non-fatal so cache uploading
# never fails the overall job. Only save when key wasn't hit.
# Save the Bazel repository cache after every non-cancelled run. Keep the
# upload non-fatal so cache service issues never fail the job itself.
- name: Save bazel repository cache
if: always() && !cancelled() && steps.cache_bazel_repository_restore.outputs.cache-hit != 'true'
if: always() && !cancelled()
continue-on-error: true
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: ${{ steps.setup_bazel.outputs.repository-cache-path }}
path: ${{ steps.prepare_bazel.outputs.repository-cache-path }}
key: bazel-cache-${{ matrix.target }}-${{ hashFiles('MODULE.bazel', 'codex-rs/Cargo.lock', 'codex-rs/Cargo.toml') }}
clippy:
@@ -155,32 +141,12 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Set up Bazel CI
id: setup_bazel
uses: ./.github/actions/setup-bazel-ci
- name: Prepare Bazel CI
id: prepare_bazel
uses: ./.github/actions/prepare-bazel-ci
with:
target: ${{ matrix.target }}
# Restore the Bazel repository cache explicitly so external dependencies
# do not need to be re-downloaded on every CI run. Keep restore failures
# non-fatal so transient cache-service errors degrade to a cold build
# instead of failing the job.
- name: Restore bazel repository cache
id: cache_bazel_repository_restore
continue-on-error: true
uses: actions/cache/restore@v5
with:
path: ${{ steps.setup_bazel.outputs.repository-cache-path }}
key: bazel-cache-${{ matrix.target }}-${{ hashFiles('MODULE.bazel', 'codex-rs/Cargo.lock', 'codex-rs/Cargo.toml') }}
restore-keys: |
bazel-cache-${{ matrix.target }}
- name: Set up Bazel execution logs
shell: bash
run: |
mkdir -p "${RUNNER_TEMP}/bazel-execution-logs"
echo "CODEX_BAZEL_EXECUTION_LOG_COMPACT_DIR=${RUNNER_TEMP}/bazel-execution-logs" >> "${GITHUB_ENV}"
- name: bazel build --config=clippy lint targets
env:
BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }}
@@ -220,12 +186,94 @@ jobs:
path: ${{ runner.temp }}/bazel-execution-logs
if-no-files-found: ignore
# Save bazel repository cache explicitly; make non-fatal so cache uploading
# never fails the overall job. Only save when key wasn't hit.
# Save the Bazel repository cache after every non-cancelled run. Keep the
# upload non-fatal so cache service issues never fail the job itself.
- name: Save bazel repository cache
if: always() && !cancelled()
continue-on-error: true
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: ${{ steps.setup_bazel.outputs.repository-cache-path }}
path: ${{ steps.prepare_bazel.outputs.repository-cache-path }}
key: bazel-cache-${{ matrix.target }}-${{ hashFiles('MODULE.bazel', 'codex-rs/Cargo.lock', 'codex-rs/Cargo.toml') }}
verify-release-build:
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-24.04
target: x86_64-unknown-linux-gnu
- os: macos-15-xlarge
target: aarch64-apple-darwin
- os: windows-latest
target: x86_64-pc-windows-gnullvm
runs-on: ${{ matrix.os }}
name: Verify release build on ${{ matrix.os }} for ${{ matrix.target }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Prepare Bazel CI
id: prepare_bazel
uses: ./.github/actions/prepare-bazel-ci
with:
target: ${{ matrix.target }}
- name: bazel build verify-release-build targets
env:
BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }}
shell: bash
run: |
# This job exists to compile Rust code behind
# `cfg(not(debug_assertions))` so PR CI catches failures that would
# otherwise show up only in a release build. We do not need the full
# optimizer and debug-info work that normally comes with a release
# build to get that signal, so keep Bazel in `fastbuild` and disable
# Rust debug assertions explicitly.
bazel_wrapper_args=()
if [[ "${RUNNER_OS}" == "Windows" ]]; then
bazel_wrapper_args+=(--windows-msvc-host-platform)
fi
bazel_build_args=(
--compilation_mode=fastbuild
--@rules_rust//rust/settings:extra_rustc_flag=-Cdebug-assertions=no
--@rules_rust//rust/settings:extra_exec_rustc_flag=-Cdebug-assertions=no
--build_metadata=COMMIT_SHA=${GITHUB_SHA}
--build_metadata=TAG_job=verify-release-build
--build_metadata=TAG_rust_debug_assertions=off
)
bazel_target_lines="$(bash ./scripts/list-bazel-release-targets.sh)"
bazel_targets=()
while IFS= read -r target; do
bazel_targets+=("${target}")
done <<< "${bazel_target_lines}"
./.github/scripts/run-bazel-ci.sh \
"${bazel_wrapper_args[@]}" \
-- \
build \
"${bazel_build_args[@]}" \
-- \
"${bazel_targets[@]}"
- name: Upload Bazel execution logs
if: always() && !cancelled()
continue-on-error: true
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with:
name: bazel-execution-logs-verify-release-build-${{ matrix.target }}
path: ${{ runner.temp }}/bazel-execution-logs
if-no-files-found: ignore
# Save the Bazel repository cache after every non-cancelled run. Keep the
# upload non-fatal so cache service issues never fail the job itself.
- name: Save bazel repository cache
if: always() && !cancelled()
continue-on-error: true
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: ${{ steps.prepare_bazel.outputs.repository-cache-path }}
key: bazel-cache-${{ matrix.target }}-${{ hashFiles('MODULE.bazel', 'codex-rs/Cargo.lock', 'codex-rs/Cargo.toml') }}

View File

@@ -43,6 +43,9 @@ jobs:
argument_comment_lint_package:
name: Argument comment lint package
runs-on: ubuntu-24.04
env:
CARGO_DYLINT_VERSION: 5.0.0
DYLINT_LINK_VERSION: 5.0.0
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
@@ -59,10 +62,13 @@ jobs:
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
key: argument-comment-lint-${{ runner.os }}-${{ hashFiles('tools/argument-comment-lint/Cargo.lock', 'tools/argument-comment-lint/rust-toolchain', '.github/workflows/rust-ci.yml', '.github/workflows/rust-ci-full.yml') }}
key: argument-comment-lint-${{ runner.os }}-${{ env.CARGO_DYLINT_VERSION }}-${{ env.DYLINT_LINK_VERSION }}-${{ hashFiles('tools/argument-comment-lint/Cargo.lock', 'tools/argument-comment-lint/rust-toolchain', '.github/workflows/rust-ci.yml', '.github/workflows/rust-ci-full.yml') }}
- name: Install cargo-dylint tooling
if: ${{ steps.cargo_dylint_cache.outputs.cache-hit != 'true' }}
run: cargo install --locked cargo-dylint dylint-link
shell: bash
run: |
cargo install --locked cargo-dylint --version "$CARGO_DYLINT_VERSION"
cargo install --locked dylint-link --version "$DYLINT_LINK_VERSION"
- name: Check Python wrapper syntax
run: python3 -m py_compile tools/argument-comment-lint/wrapper_common.py tools/argument-comment-lint/run.py tools/argument-comment-lint/run-prebuilt-linter.py tools/argument-comment-lint/test_wrapper_common.py
- name: Test Python wrapper helpers
@@ -415,22 +421,10 @@ jobs:
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
env:
TARGET: ${{ matrix.target }}
shell: bash
run: |
set -euo pipefail
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}"
archive="https://github.com/openai/codex/releases/download/rusty-v8-v${version}/librusty_v8_release_${TARGET}.a.gz"
binding_dir="${RUNNER_TEMP}/rusty_v8"
binding_path="${binding_dir}/src_binding_release_${TARGET}.rs"
mkdir -p "${binding_dir}"
curl -fsSL "${base_url}/src_binding_release_${TARGET}.rs" -o "${binding_path}"
echo "RUSTY_V8_ARCHIVE=${archive}" >> "$GITHUB_ENV"
echo "RUSTY_V8_SRC_BINDING_PATH=${binding_path}" >> "$GITHUB_ENV"
name: Configure musl rusty_v8 artifact overrides and verify checksums
uses: ./.github/actions/setup-rusty-v8-musl
with:
target: ${{ matrix.target }}
- name: Install cargo-chef
if: ${{ matrix.profile == 'release' }}
@@ -670,6 +664,7 @@ jobs:
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

View File

@@ -90,6 +90,9 @@ jobs:
runs-on: ubuntu-24.04
needs: changed
if: ${{ needs.changed.outputs.argument_comment_lint_package == 'true' }}
env:
CARGO_DYLINT_VERSION: 5.0.0
DYLINT_LINK_VERSION: 5.0.0
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
@@ -113,10 +116,13 @@ jobs:
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
key: argument-comment-lint-${{ runner.os }}-${{ hashFiles('tools/argument-comment-lint/Cargo.lock', 'tools/argument-comment-lint/rust-toolchain', '.github/workflows/rust-ci.yml', '.github/workflows/rust-ci-full.yml') }}
key: argument-comment-lint-${{ runner.os }}-${{ env.CARGO_DYLINT_VERSION }}-${{ env.DYLINT_LINK_VERSION }}-${{ hashFiles('tools/argument-comment-lint/Cargo.lock', 'tools/argument-comment-lint/rust-toolchain', '.github/workflows/rust-ci.yml', '.github/workflows/rust-ci-full.yml') }}
- name: Install cargo-dylint tooling
if: ${{ steps.cargo_dylint_cache.outputs.cache-hit != 'true' }}
run: cargo install --locked cargo-dylint dylint-link
shell: bash
run: |
cargo install --locked cargo-dylint --version "$CARGO_DYLINT_VERSION"
cargo install --locked dylint-link --version "$DYLINT_LINK_VERSION"
- name: Check Python wrapper syntax
run: python3 -m py_compile tools/argument-comment-lint/wrapper_common.py tools/argument-comment-lint/run.py tools/argument-comment-lint/run-prebuilt-linter.py tools/argument-comment-lint/test_wrapper_common.py
- name: Test Python wrapper helpers

View File

@@ -19,6 +19,9 @@ jobs:
name: Build - ${{ matrix.runner }} - ${{ matrix.target }}
runs-on: ${{ matrix.runs_on || matrix.runner }}
timeout-minutes: 60
env:
CARGO_DYLINT_VERSION: 5.0.0
DYLINT_LINK_VERSION: 5.0.0
strategy:
fail-fast: false
@@ -65,8 +68,8 @@ jobs:
shell: bash
run: |
install_root="${RUNNER_TEMP}/argument-comment-lint-tools"
cargo install --locked cargo-dylint --root "$install_root"
cargo install --locked dylint-link
cargo install --locked cargo-dylint --version "$CARGO_DYLINT_VERSION" --root "$install_root"
cargo install --locked dylint-link --version "$DYLINT_LINK_VERSION"
echo "INSTALL_ROOT=$install_root" >> "$GITHUB_ENV"
- name: Cargo build

View File

@@ -211,22 +211,10 @@ jobs:
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
env:
TARGET: ${{ matrix.target }}
shell: bash
run: |
set -euo pipefail
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}"
archive="https://github.com/openai/codex/releases/download/rusty-v8-v${version}/librusty_v8_release_${TARGET}.a.gz"
binding_dir="${RUNNER_TEMP}/rusty_v8"
binding_path="${binding_dir}/src_binding_release_${TARGET}.rs"
mkdir -p "${binding_dir}"
curl -fsSL "${base_url}/src_binding_release_${TARGET}.rs" -o "${binding_path}"
echo "RUSTY_V8_ARCHIVE=${archive}" >> "$GITHUB_ENV"
echo "RUSTY_V8_SRC_BINDING_PATH=${binding_path}" >> "$GITHUB_ENV"
name: Configure musl rusty_v8 artifact overrides and verify checksums
uses: ./.github/actions/setup-rusty-v8-musl
with:
target: ${{ matrix.target }}
- name: Cargo build
shell: bash

View File

@@ -78,7 +78,7 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Set up Bazel
uses: bazelbuild/setup-bazelisk@6ecf4fd8b7d1f9721785f1dd656a689acf9add47 # v3
uses: bazelbuild/setup-bazelisk@b39c379c82683a5f25d34f0d062761f62693e0b2 # v3
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6

View File

@@ -75,7 +75,7 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Set up Bazel
uses: bazelbuild/setup-bazelisk@6ecf4fd8b7d1f9721785f1dd656a689acf9add47 # v3
uses: bazelbuild/setup-bazelisk@b39c379c82683a5f25d34f0d062761f62693e0b2 # v3
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6

View File

@@ -423,6 +423,7 @@ http_archive(
http_file(
name = "rusty_v8_146_4_0_aarch64_apple_darwin_archive",
downloaded_file_path = "librusty_v8_release_aarch64-apple-darwin.a.gz",
sha256 = "bfe2c9be32a56c28546f0f965825ee68fbf606405f310cc4e17b448a568cf98a",
urls = [
"https://github.com/denoland/rusty_v8/releases/download/v146.4.0/librusty_v8_release_aarch64-apple-darwin.a.gz",
],
@@ -431,6 +432,7 @@ http_file(
http_file(
name = "rusty_v8_146_4_0_aarch64_unknown_linux_gnu_archive",
downloaded_file_path = "librusty_v8_release_aarch64-unknown-linux-gnu.a.gz",
sha256 = "dbf165b07c81bdb054bc046b43d23e69fcf7bcc1a4c1b5b4776983a71062ecd8",
urls = [
"https://github.com/denoland/rusty_v8/releases/download/v146.4.0/librusty_v8_release_aarch64-unknown-linux-gnu.a.gz",
],
@@ -439,6 +441,7 @@ http_file(
http_file(
name = "rusty_v8_146_4_0_aarch64_pc_windows_msvc_archive",
downloaded_file_path = "rusty_v8_release_aarch64-pc-windows-msvc.lib.gz",
sha256 = "ed13363659c6d08583ac8fdc40493445c5767d8b94955a4d5d7bb8d5a81f6bf8",
urls = [
"https://github.com/denoland/rusty_v8/releases/download/v146.4.0/rusty_v8_release_aarch64-pc-windows-msvc.lib.gz",
],
@@ -447,6 +450,7 @@ http_file(
http_file(
name = "rusty_v8_146_4_0_x86_64_apple_darwin_archive",
downloaded_file_path = "librusty_v8_release_x86_64-apple-darwin.a.gz",
sha256 = "630cd240f1bbecdb071417dc18387ab81cf67c549c1c515a0b4fcf9eba647bb7",
urls = [
"https://github.com/denoland/rusty_v8/releases/download/v146.4.0/librusty_v8_release_x86_64-apple-darwin.a.gz",
],
@@ -455,6 +459,7 @@ http_file(
http_file(
name = "rusty_v8_146_4_0_x86_64_unknown_linux_gnu_archive",
downloaded_file_path = "librusty_v8_release_x86_64-unknown-linux-gnu.a.gz",
sha256 = "e64b4d99e4ae293a2e846244a89b80178ba10382c13fb591c1fa6968f5291153",
urls = [
"https://github.com/denoland/rusty_v8/releases/download/v146.4.0/librusty_v8_release_x86_64-unknown-linux-gnu.a.gz",
],
@@ -463,6 +468,7 @@ http_file(
http_file(
name = "rusty_v8_146_4_0_x86_64_pc_windows_msvc_archive",
downloaded_file_path = "rusty_v8_release_x86_64-pc-windows-msvc.lib.gz",
sha256 = "90a9a2346acd3685a355e98df85c24dbe406cb124367d16259a4b5d522621862",
urls = [
"https://github.com/denoland/rusty_v8/releases/download/v146.4.0/rusty_v8_release_x86_64-pc-windows-msvc.lib.gz",
],
@@ -471,6 +477,7 @@ http_file(
http_file(
name = "rusty_v8_146_4_0_aarch64_unknown_linux_musl_archive",
downloaded_file_path = "librusty_v8_release_aarch64-unknown-linux-musl.a.gz",
sha256 = "27a08ed26c34297bfd93e514692ccc44b85f8b15c6aa39cf34e784f84fb37e8e",
urls = [
"https://github.com/openai/codex/releases/download/rusty-v8-v146.4.0/librusty_v8_release_aarch64-unknown-linux-musl.a.gz",
],
@@ -479,6 +486,7 @@ http_file(
http_file(
name = "rusty_v8_146_4_0_aarch64_unknown_linux_musl_binding",
downloaded_file_path = "src_binding_release_aarch64-unknown-linux-musl.rs",
sha256 = "09f8900ced8297c229246c7a50b2e0ec23c54d0a554f369619cc29863f38dd1a",
urls = [
"https://github.com/openai/codex/releases/download/rusty-v8-v146.4.0/src_binding_release_aarch64-unknown-linux-musl.rs",
],
@@ -487,6 +495,7 @@ http_file(
http_file(
name = "rusty_v8_146_4_0_x86_64_unknown_linux_musl_archive",
downloaded_file_path = "librusty_v8_release_x86_64-unknown-linux-musl.a.gz",
sha256 = "20d8271ad712323d352c1383c36e3c4b755abc41ece35819c49c75ec7134d2f8",
urls = [
"https://github.com/openai/codex/releases/download/rusty-v8-v146.4.0/librusty_v8_release_x86_64-unknown-linux-musl.a.gz",
],
@@ -495,6 +504,7 @@ http_file(
http_file(
name = "rusty_v8_146_4_0_x86_64_unknown_linux_musl_binding",
downloaded_file_path = "src_binding_release_x86_64-unknown-linux-musl.rs",
sha256 = "09f8900ced8297c229246c7a50b2e0ec23c54d0a554f369619cc29863f38dd1a",
urls = [
"https://github.com/openai/codex/releases/download/rusty-v8-v146.4.0/src_binding_release_x86_64-unknown-linux-musl.rs",
],

11
MODULE.bazel.lock generated

File diff suppressed because one or more lines are too long

View File

@@ -11,3 +11,7 @@ Our security program is managed through Bugcrowd, and we ask that any validated
## Vulnerability Disclosure Program
Our Vulnerability Program Guidelines are defined on our [Bugcrowd program page](https://bugcrowd.com/engagements/openai).
## How to operate CODEX safely
For details on Codex security boundaries, including sandboxing, approvals, and network controls, see [Agent approvals & security](https://developers.openai.com/codex/agent-approvals-security).

View File

@@ -4,20 +4,14 @@
# version_regex matches against the CLI version (env!("CARGO_PKG_VERSION")); omit to apply to all versions.
# target_app specify which app should display the announcement (cli, vsce, ...).
[[announcements]]
content = "Welcome to Codex! Check out the new onboarding flow."
from_date = "2024-10-01"
to_date = "2024-10-15"
target_app = "cli"
# Test announcement only for local build version until 2026-01-10 excluded (past)
# Test announcement only for local build version until 2027-05-10 excluded
[[announcements]]
content = "This is a test announcement"
version_regex = "^0\\.0\\.0$"
to_date = "2026-05-10"
to_date = "2027-05-10"
[[announcements]]
content = "**BREAKING NEWS**: `gpt-5.3-codex` is out! Upgrade to `0.98.0` for a faster, smarter, more steerable agent."
from_date = "2026-02-01"
to_date = "2026-02-16"
version_regex = "^0\\.(?:[0-9]|[1-8][0-9]|9[0-7])\\."
content = "Update Required - This version will no longer be supported starting May 8th. Please upgrade to the latest version (https://github.com/openai/codex/releases/latest) using your preferred package manager."
# Matches 0.x.y versions from 0.0.y through 0.119.y; excludes 0.120.0 and newer.
version_regex = "^0\\.(?:[0-9]|[1-9][0-9]|1[01][0-9])\\."
to_date = "2026-05-08"

View File

@@ -1,6 +1,10 @@
[advisories]
# Reviewed 2026-04-15. Keep this list in sync with ../deny.toml.
ignore = [
"RUSTSEC-2024-0388", # derivative 2.2.0 via starlark; upstream crate is unmaintained
"RUSTSEC-2025-0057", # fxhash 0.2.1 via starlark_map; upstream crate is unmaintained
"RUSTSEC-2024-0436", # paste 1.0.15 via starlark/ratatui; upstream crate is unmaintained
"RUSTSEC-2024-0320", # yaml-rust via syntect; remove when syntect drops or updates it
"RUSTSEC-2025-0141", # bincode via syntect; remove when syntect drops or updates it
"RUSTSEC-2026-0097", # rand 0.8.5 via age/codex-secrets and zbus/keyring; remove when transitive deps move to rand >=0.9.3
]

178
codex-rs/Cargo.lock generated
View File

@@ -1375,6 +1375,7 @@ dependencies = [
"codex-login",
"codex-plugin",
"codex-protocol",
"codex-utils-absolute-path",
"os_info",
"pretty_assertions",
"serde",
@@ -1445,6 +1446,7 @@ dependencies = [
"codex-cloud-requirements",
"codex-config",
"codex-core",
"codex-core-plugins",
"codex-exec-server",
"codex-features",
"codex-feedback",
@@ -1460,6 +1462,7 @@ dependencies = [
"codex-sandboxing",
"codex-shell-command",
"codex-state",
"codex-thread-store",
"codex-tools",
"codex-utils-absolute-path",
"codex-utils-cargo-bin",
@@ -1669,6 +1672,7 @@ dependencies = [
"assert_matches",
"clap",
"clap_complete",
"codex-api",
"codex-app-server",
"codex-app-server-protocol",
"codex-app-server-test-client",
@@ -1692,6 +1696,7 @@ dependencies = [
"codex-stdio-to-uds",
"codex-terminal-detection",
"codex-tui",
"codex-utils-absolute-path",
"codex-utils-cargo-bin",
"codex-utils-cli",
"codex-utils-path",
@@ -1726,7 +1731,7 @@ dependencies = [
"opentelemetry",
"opentelemetry_sdk",
"pretty_assertions",
"rand 0.9.2",
"rand 0.9.3",
"reqwest",
"rustls",
"rustls-native-certs",
@@ -1828,6 +1833,8 @@ name = "codex-code-mode"
version = "0.0.0"
dependencies = [
"async-trait",
"codex-protocol",
"deno_core_icudata",
"pretty_assertions",
"serde",
"serde_json",
@@ -1907,6 +1914,7 @@ dependencies = [
"codex-code-mode",
"codex-config",
"codex-connectors",
"codex-core-plugins",
"codex-core-skills",
"codex-exec-server",
"codex-execpolicy",
@@ -1932,6 +1940,8 @@ dependencies = [
"codex-shell-escalation",
"codex-state",
"codex-terminal-detection",
"codex-test-binary-support",
"codex-thread-store",
"codex-tools",
"codex-utils-absolute-path",
"codex-utils-cache",
@@ -1953,6 +1963,7 @@ dependencies = [
"ctor 0.6.3",
"dirs",
"dunce",
"ed25519-dalek",
"env-flags",
"eventsource-stream",
"futures",
@@ -1970,7 +1981,7 @@ dependencies = [
"opentelemetry_sdk",
"predicates",
"pretty_assertions",
"rand 0.9.2",
"rand 0.9.3",
"regex-lite",
"reqwest",
"rmcp",
@@ -2004,6 +2015,33 @@ dependencies = [
"zstd 0.13.3",
]
[[package]]
name = "codex-core-plugins"
version = "0.0.0"
dependencies = [
"codex-app-server-protocol",
"codex-config",
"codex-core-skills",
"codex-exec-server",
"codex-git-utils",
"codex-login",
"codex-plugin",
"codex-protocol",
"codex-utils-absolute-path",
"codex-utils-plugins",
"dirs",
"pretty_assertions",
"reqwest",
"serde",
"serde_json",
"tempfile",
"thiserror 2.0.18",
"tokio",
"toml 0.9.11+spec-1.1.0",
"tracing",
"url",
]
[[package]]
name = "codex-core-skills"
version = "0.0.0"
@@ -2012,6 +2050,7 @@ dependencies = [
"codex-analytics",
"codex-app-server-protocol",
"codex-config",
"codex-exec-server",
"codex-instructions",
"codex-login",
"codex-otel",
@@ -2098,15 +2137,18 @@ dependencies = [
"async-trait",
"base64 0.22.1",
"codex-app-server-protocol",
"codex-config",
"codex-protocol",
"codex-sandboxing",
"codex-test-binary-support",
"codex-utils-absolute-path",
"codex-utils-cargo-bin",
"codex-utils-pty",
"ctor 0.6.3",
"futures",
"pretty_assertions",
"serde",
"serde_json",
"serial_test",
"tempfile",
"test-case",
"thiserror 2.0.18",
@@ -2231,6 +2273,7 @@ dependencies = [
"chrono",
"codex-config",
"codex-protocol",
"codex-utils-absolute-path",
"futures",
"pretty_assertions",
"regex",
@@ -2241,6 +2284,15 @@ dependencies = [
"tokio",
]
[[package]]
name = "codex-install-context"
version = "0.0.0"
dependencies = [
"codex-utils-home-dir",
"pretty_assertions",
"tempfile",
]
[[package]]
name = "codex-instructions"
version = "0.0.0"
@@ -2318,7 +2370,7 @@ dependencies = [
"once_cell",
"os_info",
"pretty_assertions",
"rand 0.9.2",
"rand 0.9.3",
"regex-lite",
"reqwest",
"serde",
@@ -2344,11 +2396,13 @@ dependencies = [
"async-channel",
"codex-async-utils",
"codex-config",
"codex-exec-server",
"codex-login",
"codex-otel",
"codex-plugin",
"codex-protocol",
"codex-rmcp-client",
"codex-utils-absolute-path",
"codex-utils-plugins",
"futures",
"pretty_assertions",
@@ -2379,6 +2433,7 @@ dependencies = [
"codex-models-manager",
"codex-protocol",
"codex-shell-command",
"codex-utils-absolute-path",
"codex-utils-cli",
"codex-utils-json-to-toml",
"core_test_support",
@@ -2629,6 +2684,7 @@ dependencies = [
"axum",
"codex-client",
"codex-config",
"codex-exec-server",
"codex-keyring-store",
"codex-protocol",
"codex-utils-cargo-bin",
@@ -2684,6 +2740,8 @@ dependencies = [
name = "codex-sandboxing"
version = "0.0.0"
dependencies = [
"anyhow",
"async-trait",
"codex-network-proxy",
"codex-protocol",
"codex-utils-absolute-path",
@@ -2692,6 +2750,7 @@ dependencies = [
"pretty_assertions",
"serde_json",
"tempfile",
"tokio",
"tracing",
"url",
"which 8.0.0",
@@ -2708,7 +2767,7 @@ dependencies = [
"codex-keyring-store",
"keyring",
"pretty_assertions",
"rand 0.9.2",
"rand 0.9.3",
"regex",
"schemars 0.8.22",
"serde",
@@ -2810,6 +2869,33 @@ dependencies = [
"tracing",
]
[[package]]
name = "codex-test-binary-support"
version = "0.0.0"
dependencies = [
"codex-arg0",
"tempfile",
]
[[package]]
name = "codex-thread-store"
version = "0.0.0"
dependencies = [
"async-trait",
"chrono",
"codex-git-utils",
"codex-protocol",
"codex-rollout",
"codex-state",
"pretty_assertions",
"serde",
"serde_json",
"tempfile",
"thiserror 2.0.18",
"tokio",
"uuid",
]
[[package]]
name = "codex-tools"
version = "0.0.0"
@@ -2850,6 +2936,7 @@ dependencies = [
"codex-feedback",
"codex-file-search",
"codex-git-utils",
"codex-install-context",
"codex-login",
"codex-mcp",
"codex-model-provider-info",
@@ -2888,7 +2975,7 @@ dependencies = [
"pathdiff",
"pretty_assertions",
"pulldown-cmark",
"rand 0.9.2",
"rand 0.9.3",
"ratatui",
"ratatui-macros",
"regex-lite",
@@ -2987,6 +3074,7 @@ version = "0.0.0"
name = "codex-utils-home-dir"
version = "0.0.0"
dependencies = [
"codex-utils-absolute-path",
"dirs",
"pretty_assertions",
"tempfile",
@@ -3046,10 +3134,13 @@ dependencies = [
name = "codex-utils-plugins"
version = "0.0.0"
dependencies = [
"codex-exec-server",
"codex-login",
"codex-utils-absolute-path",
"serde",
"serde_json",
"tempfile",
"tokio",
]
[[package]]
@@ -3523,7 +3614,7 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crossterm"
version = "0.28.1"
source = "git+https://github.com/nornagon/crossterm?branch=nornagon%2Fcolor-query#87db8bfa6dc99427fd3b071681b07fc31c6ce995"
source = "git+https://github.com/nornagon/crossterm?rev=87db8bfa6dc99427fd3b071681b07fc31c6ce995#87db8bfa6dc99427fd3b071681b07fc31c6ce995"
dependencies = [
"bitflags 2.10.0",
"crossterm_winapi",
@@ -3617,6 +3708,7 @@ dependencies = [
"cfg-if",
"cpufeatures",
"curve25519-dalek-derive",
"digest",
"fiat-crypto",
"rustc_version",
"subtle",
@@ -3886,6 +3978,12 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26bf8fc351c5ed29b5c2f0cbbac1b209b74f60ecd62e675a998df72c49af5204"
[[package]]
name = "deno_core_icudata"
version = "0.77.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9efff8990a82c1ae664292507e1a5c6749ddd2312898cdf9cd7cb1fd4bc64c6"
[[package]]
name = "der"
version = "0.7.10"
@@ -4087,7 +4185,7 @@ dependencies = [
"libc",
"option-ext",
"redox_users 0.5.2",
"windows-sys 0.59.0",
"windows-sys 0.61.2",
]
[[package]]
@@ -4191,6 +4289,30 @@ version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
[[package]]
name = "ed25519"
version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
dependencies = [
"pkcs8",
"signature",
]
[[package]]
name = "ed25519-dalek"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9"
dependencies = [
"curve25519-dalek",
"ed25519",
"serde",
"sha2",
"subtle",
"zeroize",
]
[[package]]
name = "either"
version = "1.15.0"
@@ -5120,7 +5242,7 @@ dependencies = [
"idna",
"ipnet",
"once_cell",
"rand 0.9.2",
"rand 0.9.3",
"ring",
"thiserror 2.0.18",
"tinyvec",
@@ -5142,7 +5264,7 @@ dependencies = [
"moka",
"once_cell",
"parking_lot",
"rand 0.9.2",
"rand 0.9.3",
"resolv-conf",
"smallvec",
"thiserror 2.0.18",
@@ -6689,7 +6811,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
"windows-sys 0.59.0",
"windows-sys 0.61.2",
]
[[package]]
@@ -7288,7 +7410,7 @@ dependencies = [
"futures-util",
"opentelemetry",
"percent-encoding",
"rand 0.9.2",
"rand 0.9.3",
"thiserror 2.0.18",
"tokio",
"tokio-stream",
@@ -7837,7 +7959,7 @@ checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40"
dependencies = [
"bitflags 2.10.0",
"num-traits",
"rand 0.9.2",
"rand 0.9.3",
"rand_chacha 0.9.0",
"rand_xorshift",
"regex-syntax 0.8.8",
@@ -8008,7 +8130,7 @@ dependencies = [
"bytes",
"getrandom 0.3.4",
"lru-slab",
"rand 0.9.2",
"rand 0.9.3",
"ring",
"rustc-hash 2.1.1",
"rustls",
@@ -8140,7 +8262,7 @@ dependencies = [
"rama-http-types",
"rama-net",
"rama-utils",
"rand 0.9.2",
"rand 0.9.3",
"serde",
"serde_html_form",
"serde_json",
@@ -8210,7 +8332,7 @@ dependencies = [
"rama-macros",
"rama-net",
"rama-utils",
"rand 0.9.2",
"rand 0.9.3",
"serde",
"sha1",
]
@@ -8238,7 +8360,7 @@ dependencies = [
"rama-error",
"rama-macros",
"rama-utils",
"rand 0.9.2",
"rand 0.9.3",
"serde",
"serde_json",
"sync_wrapper",
@@ -8312,7 +8434,7 @@ dependencies = [
"rama-http-types",
"rama-net",
"rama-utils",
"rand 0.9.2",
"rand 0.9.3",
"tokio",
]
@@ -8392,9 +8514,9 @@ dependencies = [
[[package]]
name = "rand"
version = "0.9.2"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
checksum = "7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.5",
@@ -8450,7 +8572,7 @@ dependencies = [
[[package]]
name = "ratatui"
version = "0.29.0"
source = "git+https://github.com/nornagon/ratatui?branch=nornagon-v0.29.0-patch#9b2ad1298408c45918ee9f8241a6f95498cdbed2"
source = "git+https://github.com/nornagon/ratatui?rev=9b2ad1298408c45918ee9f8241a6f95498cdbed2#9b2ad1298408c45918ee9f8241a6f95498cdbed2"
dependencies = [
"bitflags 2.10.0",
"cassowary",
@@ -8709,7 +8831,7 @@ dependencies = [
"pastey",
"pin-project-lite",
"process-wrap",
"rand 0.9.2",
"rand 0.9.3",
"reqwest",
"rmcp-macros",
"schemars 1.2.1",
@@ -8916,9 +9038,9 @@ dependencies = [
[[package]]
name = "rustls-webpki"
version = "0.103.10"
version = "0.103.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef"
checksum = "8279bb85272c9f10811ae6a6c547ff594d6a7f3c6c6b02ee9726d1d0dcfcdd06"
dependencies = [
"aws-lc-rs",
"ring",
@@ -9293,7 +9415,7 @@ version = "0.46.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26ab054c34b87f96c3e4701bea1888317cde30cc7e4a6136d2c48454ab96661c"
dependencies = [
"rand 0.9.2",
"rand 0.9.3",
"sentry-types",
"serde",
"serde_json",
@@ -9341,7 +9463,7 @@ checksum = "eecbd63e9d15a26a40675ed180d376fcb434635d2e33de1c24003f61e3e2230d"
dependencies = [
"debugid",
"hex",
"rand 0.9.2",
"rand 0.9.3",
"serde",
"serde_json",
"thiserror 2.0.18",
@@ -11126,7 +11248,7 @@ dependencies = [
"http 1.4.0",
"httparse",
"log",
"rand 0.9.2",
"rand 0.9.3",
"rustls",
"rustls-pki-types",
"sha1",
@@ -11813,7 +11935,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.48.0",
"windows-sys 0.61.2",
]
[[package]]

View File

@@ -13,6 +13,7 @@ members = [
"arg0",
"feedback",
"features",
"install-context",
"codex-backend-openapi-models",
"code-mode",
"cloud-requirements",
@@ -27,6 +28,7 @@ members = [
"shell-escalation",
"skills",
"core",
"core-plugins",
"core-skills",
"hooks",
"instructions",
@@ -86,6 +88,8 @@ members = [
"codex-api",
"state",
"terminal-detection",
"test-binary-support",
"thread-store",
"codex-experimental-api-macros",
"plugin",
]
@@ -125,6 +129,7 @@ codex-code-mode = { path = "code-mode" }
codex-config = { path = "config" }
codex-connectors = { path = "connectors" }
codex-core = { path = "core" }
codex-core-plugins = { path = "core-plugins" }
codex-core-skills = { path = "core-skills" }
codex-exec = { path = "exec" }
codex-exec-server = { path = "exec-server" }
@@ -132,6 +137,7 @@ codex-execpolicy = { path = "execpolicy" }
codex-experimental-api-macros = { path = "codex-experimental-api-macros" }
codex-features = { path = "features" }
codex-feedback = { path = "feedback" }
codex-install-context = { path = "install-context" }
codex-file-search = { path = "file-search" }
codex-git-utils = { path = "git-utils" }
codex-hooks = { path = "hooks" }
@@ -163,6 +169,8 @@ codex-skills = { path = "skills" }
codex-state = { path = "state" }
codex-stdio-to-uds = { path = "stdio-to-uds" }
codex-terminal-detection = { path = "terminal-detection" }
codex-test-binary-support = { path = "test-binary-support" }
codex-thread-store = { path = "thread-store" }
codex-tools = { path = "tools" }
codex-tui = { path = "tui" }
codex-utils-absolute-path = { path = "utils/absolute-path" }
@@ -218,11 +226,13 @@ crossbeam-channel = "0.5.15"
crossterm = "0.28.1"
csv = "1.3.1"
ctor = "0.6.3"
deno_core_icudata = "0.77.0"
derive_more = "2"
diffy = "0.4.2"
dirs = "6"
dotenvy = "0.15.7"
dunce = "1.0.4"
ed25519-dalek = { version = "2.2.0", features = ["pkcs8"] }
encoding_rs = "0.8.35"
env-flags = "0.1.1"
env_logger = "0.11.9"
@@ -427,8 +437,8 @@ opt-level = 0
[patch.crates-io]
# Uncomment to debug local changes.
# ratatui = { path = "../../ratatui" }
crossterm = { git = "https://github.com/nornagon/crossterm", branch = "nornagon/color-query" }
ratatui = { git = "https://github.com/nornagon/ratatui", branch = "nornagon-v0.29.0-patch" }
crossterm = { git = "https://github.com/nornagon/crossterm", rev = "87db8bfa6dc99427fd3b071681b07fc31c6ce995" }
ratatui = { git = "https://github.com/nornagon/ratatui", rev = "9b2ad1298408c45918ee9f8241a6f95498cdbed2" }
tokio-tungstenite = { git = "https://github.com/openai-oss-forks/tokio-tungstenite", rev = "132f5b39c862e3a970f731d709608b3e6276d5f6" }
tungstenite = { git = "https://github.com/openai-oss-forks/tungstenite-rs", rev = "9200079d3b54a1ff51072e24d81fd354f085156f" }

View File

@@ -1,6 +1,6 @@
# Codex CLI (Rust Implementation)
We provide Codex CLI as a standalone, native executable to ensure a zero-dependency install.
We provide Codex CLI as a standalone executable to ensure a zero-dependency install.
## Installing Codex

View File

@@ -29,4 +29,5 @@ tokio = { workspace = true, features = [
tracing = { workspace = true, features = ["log"] }
[dev-dependencies]
codex-utils-absolute-path = { workspace = true }
pretty_assertions = { workspace = true }

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,7 @@ use crate::events::TrackEventRequest;
use crate::events::TrackEventsRequest;
use crate::events::current_runtime_metadata;
use crate::facts::AnalyticsFact;
use crate::facts::AnalyticsJsonRpcError;
use crate::facts::AppInvocation;
use crate::facts::AppMentionedInput;
use crate::facts::AppUsedInput;
@@ -14,9 +15,15 @@ use crate::facts::SkillInvocation;
use crate::facts::SkillInvokedInput;
use crate::facts::SubAgentThreadStartedInput;
use crate::facts::TrackEventsContext;
use crate::facts::TurnResolvedConfigFact;
use crate::facts::TurnTokenUsageFact;
use crate::reducer::AnalyticsReducer;
use codex_app_server_protocol::ClientRequest;
use codex_app_server_protocol::ClientResponse;
use codex_app_server_protocol::InitializeParams;
use codex_app_server_protocol::JSONRPCErrorError;
use codex_app_server_protocol::RequestId;
use codex_app_server_protocol::ServerNotification;
use codex_login::AuthManager;
use codex_login::default_client::create_client;
use codex_plugin::PluginTelemetryMetadata;
@@ -167,6 +174,14 @@ impl AnalyticsEventsClient {
)));
}
pub fn track_request(&self, connection_id: u64, request_id: RequestId, request: ClientRequest) {
self.record_fact(AnalyticsFact::Request {
connection_id,
request_id,
request: Box::new(request),
});
}
pub fn track_app_used(&self, tracking: TrackEventsContext, app: AppInvocation) {
if !self.queue.should_enqueue_app_used(&tracking, &app) {
return;
@@ -191,6 +206,18 @@ impl AnalyticsEventsClient {
)));
}
pub fn track_turn_resolved_config(&self, fact: TurnResolvedConfigFact) {
self.record_fact(AnalyticsFact::Custom(
CustomAnalyticsFact::TurnResolvedConfig(Box::new(fact)),
));
}
pub fn track_turn_token_usage(&self, fact: TurnTokenUsageFact) {
self.record_fact(AnalyticsFact::Custom(CustomAnalyticsFact::TurnTokenUsage(
Box::new(fact),
)));
}
pub fn track_plugin_installed(&self, plugin: PluginTelemetryMetadata) {
self.record_fact(AnalyticsFact::Custom(
CustomAnalyticsFact::PluginStateChanged(PluginStateChangedInput {
@@ -240,6 +267,25 @@ impl AnalyticsEventsClient {
response: Box::new(response),
});
}
pub fn track_error_response(
&self,
connection_id: u64,
request_id: RequestId,
error: JSONRPCErrorError,
error_type: Option<AnalyticsJsonRpcError>,
) {
self.record_fact(AnalyticsFact::ErrorResponse {
connection_id,
request_id,
error,
error_type,
});
}
pub fn track_notification(&self, notification: ServerNotification) {
self.record_fact(AnalyticsFact::Notification(Box::new(notification)));
}
}
async fn send_track_events(

View File

@@ -3,13 +3,18 @@ use crate::facts::CodexCompactionEvent;
use crate::facts::InvocationType;
use crate::facts::PluginState;
use crate::facts::SubAgentThreadStartedInput;
use crate::facts::ThreadInitializationMode;
use crate::facts::TrackEventsContext;
use crate::facts::TurnStatus;
use crate::facts::TurnSteerRejectionReason;
use crate::facts::TurnSteerResult;
use crate::facts::TurnSubmissionType;
use codex_app_server_protocol::CodexErrorInfo;
use codex_login::default_client::originator;
use codex_plugin::PluginTelemetryMetadata;
use codex_protocol::approvals::NetworkApprovalProtocol;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::SandboxPermissions;
use codex_protocol::protocol::SessionSource;
use codex_protocol::protocol::SubAgentSource;
use serde::Serialize;
@@ -21,14 +26,6 @@ pub enum AppServerRpcTransport {
InProcess,
}
#[derive(Clone, Copy, Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum ThreadInitializationMode {
New,
Forked,
Resumed,
}
#[derive(Serialize)]
pub(crate) struct TrackEventsRequest {
pub(crate) events: Vec<TrackEventRequest>,
@@ -43,6 +40,8 @@ pub(crate) enum TrackEventRequest {
AppMentioned(CodexAppMentionedEventRequest),
AppUsed(CodexAppUsedEventRequest),
Compaction(Box<CodexCompactionEventRequest>),
TurnEvent(Box<CodexTurnEventRequest>),
TurnSteer(CodexTurnSteerEventRequest),
PluginUsed(CodexPluginUsedEventRequest),
PluginInstalled(CodexPluginEventRequest),
PluginUninstalled(CodexPluginEventRequest),
@@ -330,6 +329,84 @@ pub(crate) struct CodexCompactionEventRequest {
pub(crate) event_params: CodexCompactionEventParams,
}
#[derive(Serialize)]
pub(crate) struct CodexTurnEventParams {
pub(crate) thread_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.
pub(crate) submission_type: Option<TurnSubmissionType>,
pub(crate) app_server_client: CodexAppServerClientMetadata,
pub(crate) runtime: CodexRuntimeMetadata,
pub(crate) ephemeral: bool,
pub(crate) thread_source: Option<String>,
pub(crate) initialization_mode: ThreadInitializationMode,
pub(crate) subagent_source: Option<String>,
pub(crate) parent_thread_id: Option<String>,
pub(crate) model: Option<String>,
pub(crate) model_provider: String,
pub(crate) sandbox_policy: Option<&'static str>,
pub(crate) reasoning_effort: Option<String>,
pub(crate) reasoning_summary: Option<String>,
pub(crate) service_tier: String,
pub(crate) approval_policy: String,
pub(crate) approvals_reviewer: String,
pub(crate) sandbox_network_access: bool,
pub(crate) collaboration_mode: Option<&'static str>,
pub(crate) personality: Option<String>,
pub(crate) num_input_images: usize,
pub(crate) is_first_turn: bool,
pub(crate) status: Option<TurnStatus>,
pub(crate) turn_error: Option<CodexErrorInfo>,
pub(crate) steer_count: Option<usize>,
// TODO(rhan-oai): Populate these once tool-call accounting is emitted from
// core; the schema is reserved but these fields are currently always None.
pub(crate) total_tool_call_count: Option<usize>,
pub(crate) shell_command_count: Option<usize>,
pub(crate) file_change_count: Option<usize>,
pub(crate) mcp_tool_call_count: Option<usize>,
pub(crate) dynamic_tool_call_count: Option<usize>,
pub(crate) subagent_tool_call_count: Option<usize>,
pub(crate) web_search_count: Option<usize>,
pub(crate) image_generation_count: Option<usize>,
pub(crate) input_tokens: Option<i64>,
pub(crate) cached_input_tokens: Option<i64>,
pub(crate) output_tokens: Option<i64>,
pub(crate) reasoning_output_tokens: Option<i64>,
pub(crate) total_tokens: Option<i64>,
pub(crate) duration_ms: Option<u64>,
pub(crate) started_at: Option<u64>,
pub(crate) completed_at: Option<u64>,
}
#[derive(Serialize)]
pub(crate) struct CodexTurnEventRequest {
pub(crate) event_type: &'static str,
pub(crate) event_params: CodexTurnEventParams,
}
#[derive(Serialize)]
pub(crate) struct CodexTurnSteerEventParams {
pub(crate) thread_id: String,
pub(crate) expected_turn_id: Option<String>,
pub(crate) accepted_turn_id: Option<String>,
pub(crate) app_server_client: CodexAppServerClientMetadata,
pub(crate) runtime: CodexRuntimeMetadata,
pub(crate) thread_source: Option<String>,
pub(crate) subagent_source: Option<String>,
pub(crate) parent_thread_id: Option<String>,
pub(crate) num_input_images: usize,
pub(crate) result: TurnSteerResult,
pub(crate) rejection_reason: Option<TurnSteerRejectionReason>,
pub(crate) created_at: u64,
}
#[derive(Serialize)]
pub(crate) struct CodexTurnSteerEventRequest {
pub(crate) event_type: &'static str,
pub(crate) event_params: CodexTurnSteerEventParams,
}
#[derive(Serialize)]
pub(crate) struct CodexPluginMetadata {
pub(crate) plugin_id: Option<String>,
@@ -452,14 +529,6 @@ pub(crate) fn codex_plugin_used_metadata(
}
}
pub(crate) fn thread_source_name(thread_source: &SessionSource) -> Option<&'static str> {
match thread_source {
SessionSource::Cli | SessionSource::VSCode | SessionSource::Exec => Some("user"),
SessionSource::SubAgent(_) => Some("subagent"),
SessionSource::Mcp | SessionSource::Custom(_) | SessionSource::Unknown => None,
}
}
pub(crate) fn current_runtime_metadata() -> CodexRuntimeMetadata {
let os_info = os_info::get();
CodexRuntimeMetadata {

View File

@@ -4,11 +4,22 @@ use crate::events::GuardianReviewEventParams;
use codex_app_server_protocol::ClientRequest;
use codex_app_server_protocol::ClientResponse;
use codex_app_server_protocol::InitializeParams;
use codex_app_server_protocol::JSONRPCErrorError;
use codex_app_server_protocol::RequestId;
use codex_app_server_protocol::ServerNotification;
use codex_plugin::PluginTelemetryMetadata;
use codex_protocol::config_types::ApprovalsReviewer;
use codex_protocol::config_types::ModeKind;
use codex_protocol::config_types::Personality;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::config_types::ServiceTier;
use codex_protocol::openai_models::ReasoningEffort;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::SandboxPolicy;
use codex_protocol::protocol::SessionSource;
use codex_protocol::protocol::SkillScope;
use codex_protocol::protocol::SubAgentSource;
use codex_protocol::protocol::TokenUsage;
use serde::Serialize;
use std::path::PathBuf;
@@ -31,6 +42,126 @@ pub fn build_track_events_context(
}
}
#[derive(Clone, Copy, Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum TurnSubmissionType {
Default,
Queued,
}
#[derive(Clone)]
pub struct TurnResolvedConfigFact {
pub turn_id: String,
pub thread_id: String,
pub num_input_images: usize,
pub submission_type: Option<TurnSubmissionType>,
pub ephemeral: bool,
pub session_source: SessionSource,
pub model: String,
pub model_provider: String,
pub sandbox_policy: SandboxPolicy,
pub reasoning_effort: Option<ReasoningEffort>,
pub reasoning_summary: Option<ReasoningSummary>,
pub service_tier: Option<ServiceTier>,
pub approval_policy: AskForApproval,
pub approvals_reviewer: ApprovalsReviewer,
pub sandbox_network_access: bool,
pub collaboration_mode: ModeKind,
pub personality: Option<Personality>,
pub is_first_turn: bool,
}
#[derive(Clone, Copy, Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ThreadInitializationMode {
New,
Forked,
Resumed,
}
#[derive(Clone)]
pub struct TurnTokenUsageFact {
pub turn_id: String,
pub thread_id: String,
pub token_usage: TokenUsage,
}
#[derive(Clone, Copy, Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum TurnStatus {
Completed,
Failed,
Interrupted,
}
#[derive(Clone, Copy, Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum TurnSteerResult {
Accepted,
Rejected,
}
#[derive(Clone, Copy, Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum TurnSteerRejectionReason {
NoActiveTurn,
ExpectedTurnMismatch,
NonSteerableReview,
NonSteerableCompact,
EmptyInput,
InputTooLarge,
}
#[derive(Clone)]
pub struct CodexTurnSteerEvent {
pub expected_turn_id: Option<String>,
pub accepted_turn_id: Option<String>,
pub num_input_images: usize,
pub result: TurnSteerResult,
pub rejection_reason: Option<TurnSteerRejectionReason>,
pub created_at: u64,
}
#[derive(Clone, Copy, Debug)]
pub enum AnalyticsJsonRpcError {
TurnSteer(TurnSteerRequestError),
Input(InputError),
}
#[derive(Clone, Copy, Debug)]
pub enum TurnSteerRequestError {
NoActiveTurn,
ExpectedTurnMismatch,
NonSteerableReview,
NonSteerableCompact,
}
#[derive(Clone, Copy, Debug)]
pub enum InputError {
Empty,
TooLarge,
}
impl From<TurnSteerRequestError> for TurnSteerRejectionReason {
fn from(error: TurnSteerRequestError) -> Self {
match error {
TurnSteerRequestError::NoActiveTurn => Self::NoActiveTurn,
TurnSteerRequestError::ExpectedTurnMismatch => Self::ExpectedTurnMismatch,
TurnSteerRequestError::NonSteerableReview => Self::NonSteerableReview,
TurnSteerRequestError::NonSteerableCompact => Self::NonSteerableCompact,
}
}
}
impl From<InputError> for TurnSteerRejectionReason {
fn from(error: InputError) -> Self {
match error {
InputError::Empty => Self::EmptyInput,
InputError::TooLarge => Self::InputTooLarge,
}
}
}
#[derive(Clone, Debug)]
pub struct SkillInvocation {
pub skill_name: String,
@@ -146,6 +277,12 @@ pub(crate) enum AnalyticsFact {
connection_id: u64,
response: Box<ClientResponse>,
},
ErrorResponse {
connection_id: u64,
request_id: RequestId,
error: JSONRPCErrorError,
error_type: Option<AnalyticsJsonRpcError>,
},
Notification(Box<ServerNotification>),
// Facts that do not naturally exist on the app-server protocol surface, or
// would require non-trivial protocol reshaping on this branch.
@@ -156,6 +293,8 @@ pub(crate) enum CustomAnalyticsFact {
SubAgentThreadStarted(SubAgentThreadStartedInput),
Compaction(Box<CodexCompactionEvent>),
GuardianReview(Box<GuardianReviewEventParams>),
TurnResolvedConfig(Box<TurnResolvedConfigFact>),
TurnTokenUsage(Box<TurnTokenUsageFact>),
SkillInvoked(SkillInvokedInput),
AppMentioned(AppMentionedInput),
AppUsed(AppUsedInput),

View File

@@ -3,6 +3,9 @@ mod events;
mod facts;
mod reducer;
use std::time::SystemTime;
use std::time::UNIX_EPOCH;
pub use client::AnalyticsEventsClient;
pub use events::AppServerRpcTransport;
pub use events::GuardianApprovalRequestSource;
@@ -16,19 +19,36 @@ pub use events::GuardianReviewSessionKind;
pub use events::GuardianReviewTerminalStatus;
pub use events::GuardianReviewUserAuthorization;
pub use events::GuardianReviewedAction;
pub use facts::AnalyticsJsonRpcError;
pub use facts::AppInvocation;
pub use facts::CodexCompactionEvent;
pub use facts::CodexTurnSteerEvent;
pub use facts::CompactionImplementation;
pub use facts::CompactionPhase;
pub use facts::CompactionReason;
pub use facts::CompactionStatus;
pub use facts::CompactionStrategy;
pub use facts::CompactionTrigger;
pub use facts::InputError;
pub use facts::InvocationType;
pub use facts::SkillInvocation;
pub use facts::SubAgentThreadStartedInput;
pub use facts::ThreadInitializationMode;
pub use facts::TrackEventsContext;
pub use facts::TurnResolvedConfigFact;
pub use facts::TurnStatus;
pub use facts::TurnSteerRejectionReason;
pub use facts::TurnSteerRequestError;
pub use facts::TurnSteerResult;
pub use facts::TurnTokenUsageFact;
pub use facts::build_track_events_context;
#[cfg(test)]
mod analytics_client_tests;
pub fn now_unix_seconds() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
}

View File

@@ -6,12 +6,15 @@ use crate::events::CodexCompactionEventRequest;
use crate::events::CodexPluginEventRequest;
use crate::events::CodexPluginUsedEventRequest;
use crate::events::CodexRuntimeMetadata;
use crate::events::CodexTurnEventParams;
use crate::events::CodexTurnEventRequest;
use crate::events::CodexTurnSteerEventParams;
use crate::events::CodexTurnSteerEventRequest;
use crate::events::GuardianReviewEventParams;
use crate::events::GuardianReviewEventPayload;
use crate::events::GuardianReviewEventRequest;
use crate::events::SkillInvocationEventParams;
use crate::events::SkillInvocationEventRequest;
use crate::events::ThreadInitializationMode;
use crate::events::ThreadInitializedEvent;
use crate::events::ThreadInitializedEventParams;
use crate::events::TrackEventRequest;
@@ -23,8 +26,8 @@ use crate::events::plugin_state_event_type;
use crate::events::subagent_parent_thread_id;
use crate::events::subagent_source_name;
use crate::events::subagent_thread_started_event_request;
use crate::events::thread_source_name;
use crate::facts::AnalyticsFact;
use crate::facts::AnalyticsJsonRpcError;
use crate::facts::AppMentionedInput;
use crate::facts::AppUsedInput;
use crate::facts::CodexCompactionEvent;
@@ -34,19 +37,39 @@ use crate::facts::PluginStateChangedInput;
use crate::facts::PluginUsedInput;
use crate::facts::SkillInvokedInput;
use crate::facts::SubAgentThreadStartedInput;
use crate::facts::ThreadInitializationMode;
use crate::facts::TurnResolvedConfigFact;
use crate::facts::TurnStatus;
use crate::facts::TurnSteerRejectionReason;
use crate::facts::TurnSteerResult;
use crate::facts::TurnTokenUsageFact;
use crate::now_unix_seconds;
use codex_app_server_protocol::ClientRequest;
use codex_app_server_protocol::ClientResponse;
use codex_app_server_protocol::CodexErrorInfo;
use codex_app_server_protocol::InitializeParams;
use codex_app_server_protocol::RequestId;
use codex_app_server_protocol::ServerNotification;
use codex_app_server_protocol::TurnSteerResponse;
use codex_app_server_protocol::UserInput;
use codex_git_utils::collect_git_info;
use codex_git_utils::get_git_repo_root;
use codex_login::default_client::originator;
use codex_protocol::config_types::ModeKind;
use codex_protocol::config_types::Personality;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::protocol::SandboxPolicy;
use codex_protocol::protocol::SessionSource;
use codex_protocol::protocol::SkillScope;
use codex_protocol::protocol::TokenUsage;
use sha1::Digest;
use std::collections::HashMap;
use std::path::Path;
#[derive(Default)]
pub(crate) struct AnalyticsReducer {
requests: HashMap<(u64, RequestId), RequestState>,
turns: HashMap<String, TurnState>,
connections: HashMap<u64, ConnectionState>,
thread_connections: HashMap<String, u64>,
thread_metadata: HashMap<String, ThreadMetadataState>,
@@ -60,12 +83,16 @@ struct ConnectionState {
#[derive(Clone)]
struct ThreadMetadataState {
thread_source: Option<&'static str>,
initialization_mode: ThreadInitializationMode,
subagent_source: Option<String>,
parent_thread_id: Option<String>,
}
impl ThreadMetadataState {
fn from_session_source(session_source: &SessionSource) -> Self {
fn from_thread_metadata(
session_source: &SessionSource,
initialization_mode: ThreadInitializationMode,
) -> Self {
let (subagent_source, parent_thread_id) = match session_source {
SessionSource::SubAgent(subagent_source) => (
Some(subagent_source_name(subagent_source)),
@@ -79,13 +106,50 @@ impl ThreadMetadataState {
| SessionSource::Unknown => (None, None),
};
Self {
thread_source: thread_source_name(session_source),
thread_source: session_source.thread_source_name(),
initialization_mode,
subagent_source,
parent_thread_id,
}
}
}
enum RequestState {
TurnStart(PendingTurnStartState),
TurnSteer(PendingTurnSteerState),
}
struct PendingTurnStartState {
thread_id: String,
num_input_images: usize,
}
struct PendingTurnSteerState {
thread_id: String,
expected_turn_id: String,
num_input_images: usize,
created_at: u64,
}
#[derive(Clone)]
struct CompletedTurnState {
status: Option<TurnStatus>,
turn_error: Option<CodexErrorInfo>,
completed_at: u64,
duration_ms: Option<u64>,
}
struct TurnState {
connection_id: Option<u64>,
thread_id: Option<String>,
num_input_images: Option<usize>,
resolved_config: Option<TurnResolvedConfigFact>,
started_at: Option<u64>,
token_usage: Option<TokenUsage>,
completed: Option<CompletedTurnState>,
steer_count: usize,
}
impl AnalyticsReducer {
pub(crate) async fn ingest(&mut self, input: AnalyticsFact, out: &mut Vec<TrackEventRequest>) {
match input {
@@ -105,17 +169,29 @@ impl AnalyticsReducer {
);
}
AnalyticsFact::Request {
connection_id: _connection_id,
request_id: _request_id,
request: _request,
} => {}
connection_id,
request_id,
request,
} => {
self.ingest_request(connection_id, request_id, *request);
}
AnalyticsFact::Response {
connection_id,
response,
} => {
self.ingest_response(connection_id, *response, out);
}
AnalyticsFact::Notification(_notification) => {}
AnalyticsFact::ErrorResponse {
connection_id,
request_id,
error: _,
error_type,
} => {
self.ingest_error_response(connection_id, request_id, error_type, out);
}
AnalyticsFact::Notification(notification) => {
self.ingest_notification(*notification, out);
}
AnalyticsFact::Custom(input) => match input {
CustomAnalyticsFact::SubAgentThreadStarted(input) => {
self.ingest_subagent_thread_started(input, out);
@@ -126,6 +202,12 @@ impl AnalyticsReducer {
CustomAnalyticsFact::GuardianReview(input) => {
self.ingest_guardian_review(*input, out);
}
CustomAnalyticsFact::TurnResolvedConfig(input) => {
self.ingest_turn_resolved_config(*input, out);
}
CustomAnalyticsFact::TurnTokenUsage(input) => {
self.ingest_turn_token_usage(*input, out);
}
CustomAnalyticsFact::SkillInvoked(input) => {
self.ingest_skill_invoked(input, out).await;
}
@@ -216,6 +298,82 @@ impl AnalyticsReducer {
)));
}
fn ingest_request(
&mut self,
connection_id: u64,
request_id: RequestId,
request: ClientRequest,
) {
match request {
ClientRequest::TurnStart { params, .. } => {
self.requests.insert(
(connection_id, request_id),
RequestState::TurnStart(PendingTurnStartState {
thread_id: params.thread_id,
num_input_images: num_input_images(&params.input),
}),
);
}
ClientRequest::TurnSteer { params, .. } => {
self.requests.insert(
(connection_id, request_id),
RequestState::TurnSteer(PendingTurnSteerState {
thread_id: params.thread_id,
expected_turn_id: params.expected_turn_id,
num_input_images: num_input_images(&params.input),
created_at: now_unix_seconds(),
}),
);
}
_ => {}
}
}
fn ingest_turn_resolved_config(
&mut self,
input: TurnResolvedConfigFact,
out: &mut Vec<TrackEventRequest>,
) {
let turn_id = input.turn_id.clone();
let thread_id = input.thread_id.clone();
let num_input_images = input.num_input_images;
let turn_state = self.turns.entry(turn_id.clone()).or_insert(TurnState {
connection_id: None,
thread_id: None,
num_input_images: None,
resolved_config: None,
started_at: None,
token_usage: None,
completed: None,
steer_count: 0,
});
turn_state.thread_id = Some(thread_id);
turn_state.num_input_images = Some(num_input_images);
turn_state.resolved_config = Some(input);
self.maybe_emit_turn_event(&turn_id, out);
}
fn ingest_turn_token_usage(
&mut self,
input: TurnTokenUsageFact,
out: &mut Vec<TrackEventRequest>,
) {
let turn_id = input.turn_id.clone();
let turn_state = self.turns.entry(turn_id.clone()).or_insert(TurnState {
connection_id: None,
thread_id: None,
num_input_images: None,
resolved_config: None,
started_at: None,
token_usage: None,
completed: None,
steer_count: 0,
});
turn_state.thread_id = Some(input.thread_id);
turn_state.token_usage = Some(input.token_usage);
self.maybe_emit_turn_event(&turn_id, out);
}
async fn ingest_skill_invoked(
&mut self,
input: SkillInvokedInput,
@@ -316,30 +474,193 @@ impl AnalyticsReducer {
response: ClientResponse,
out: &mut Vec<TrackEventRequest>,
) {
let (thread, model, initialization_mode) = match response {
ClientResponse::ThreadStart { response, .. } => (
response.thread,
response.model,
ThreadInitializationMode::New,
),
ClientResponse::ThreadResume { response, .. } => (
response.thread,
response.model,
ThreadInitializationMode::Resumed,
),
ClientResponse::ThreadFork { response, .. } => (
response.thread,
response.model,
ThreadInitializationMode::Forked,
),
_ => return,
match response {
ClientResponse::ThreadStart { response, .. } => {
self.emit_thread_initialized(
connection_id,
response.thread,
response.model,
ThreadInitializationMode::New,
out,
);
}
ClientResponse::ThreadResume { response, .. } => {
self.emit_thread_initialized(
connection_id,
response.thread,
response.model,
ThreadInitializationMode::Resumed,
out,
);
}
ClientResponse::ThreadFork { response, .. } => {
self.emit_thread_initialized(
connection_id,
response.thread,
response.model,
ThreadInitializationMode::Forked,
out,
);
}
ClientResponse::TurnStart {
request_id,
response,
} => {
let turn_id = response.turn.id;
let Some(RequestState::TurnStart(pending_request)) =
self.requests.remove(&(connection_id, request_id))
else {
return;
};
let turn_state = self.turns.entry(turn_id.clone()).or_insert(TurnState {
connection_id: None,
thread_id: None,
num_input_images: None,
resolved_config: None,
started_at: None,
token_usage: None,
completed: None,
steer_count: 0,
});
turn_state.connection_id = Some(connection_id);
turn_state.thread_id = Some(pending_request.thread_id);
turn_state.num_input_images = Some(pending_request.num_input_images);
self.maybe_emit_turn_event(&turn_id, out);
}
ClientResponse::TurnSteer {
request_id,
response,
} => {
self.ingest_turn_steer_response(connection_id, request_id, response, out);
}
_ => {}
}
}
fn ingest_error_response(
&mut self,
connection_id: u64,
request_id: RequestId,
error_type: Option<AnalyticsJsonRpcError>,
out: &mut Vec<TrackEventRequest>,
) {
let Some(request) = self.requests.remove(&(connection_id, request_id)) else {
return;
};
self.ingest_request_error_response(connection_id, request, error_type, out);
}
fn ingest_request_error_response(
&mut self,
connection_id: u64,
request: RequestState,
error_type: Option<AnalyticsJsonRpcError>,
out: &mut Vec<TrackEventRequest>,
) {
match request {
RequestState::TurnStart(_) => {}
RequestState::TurnSteer(pending_request) => {
self.ingest_turn_steer_error_response(
connection_id,
pending_request,
error_type,
out,
);
}
}
}
fn ingest_turn_steer_error_response(
&mut self,
connection_id: u64,
pending_request: PendingTurnSteerState,
error_type: Option<AnalyticsJsonRpcError>,
out: &mut Vec<TrackEventRequest>,
) {
self.emit_turn_steer_event(
connection_id,
pending_request,
/*accepted_turn_id*/ None,
TurnSteerResult::Rejected,
rejection_reason_from_error_type(error_type),
out,
);
}
fn ingest_notification(
&mut self,
notification: ServerNotification,
out: &mut Vec<TrackEventRequest>,
) {
match notification {
ServerNotification::TurnStarted(notification) => {
let turn_state = self.turns.entry(notification.turn.id).or_insert(TurnState {
connection_id: None,
thread_id: None,
num_input_images: None,
resolved_config: None,
started_at: None,
token_usage: None,
completed: None,
steer_count: 0,
});
turn_state.started_at = notification
.turn
.started_at
.and_then(|started_at| u64::try_from(started_at).ok());
}
ServerNotification::TurnCompleted(notification) => {
let turn_state =
self.turns
.entry(notification.turn.id.clone())
.or_insert(TurnState {
connection_id: None,
thread_id: None,
num_input_images: None,
resolved_config: None,
started_at: None,
token_usage: None,
completed: None,
steer_count: 0,
});
turn_state.completed = Some(CompletedTurnState {
status: analytics_turn_status(notification.turn.status),
turn_error: notification
.turn
.error
.and_then(|error| error.codex_error_info),
completed_at: notification
.turn
.completed_at
.and_then(|completed_at| u64::try_from(completed_at).ok())
.unwrap_or_default(),
duration_ms: notification
.turn
.duration_ms
.and_then(|duration_ms| u64::try_from(duration_ms).ok()),
});
let turn_id = notification.turn.id;
self.maybe_emit_turn_event(&turn_id, out);
}
_ => {}
}
}
fn emit_thread_initialized(
&mut self,
connection_id: u64,
thread: codex_app_server_protocol::Thread,
model: String,
initialization_mode: ThreadInitializationMode,
out: &mut Vec<TrackEventRequest>,
) {
let thread_source: SessionSource = thread.source.into();
let thread_id = thread.id;
let Some(connection_state) = self.connections.get(&connection_id) else {
return;
};
let thread_metadata = ThreadMetadataState::from_session_source(&thread_source);
let thread_metadata =
ThreadMetadataState::from_thread_metadata(&thread_source, initialization_mode);
self.thread_connections
.insert(thread_id.clone(), connection_id);
self.thread_metadata
@@ -403,6 +724,275 @@ impl AnalyticsReducer {
},
)));
}
fn ingest_turn_steer_response(
&mut self,
connection_id: u64,
request_id: RequestId,
response: TurnSteerResponse,
out: &mut Vec<TrackEventRequest>,
) {
let Some(RequestState::TurnSteer(pending_request)) =
self.requests.remove(&(connection_id, request_id))
else {
return;
};
if let Some(turn_state) = self.turns.get_mut(&response.turn_id) {
turn_state.steer_count += 1;
}
self.emit_turn_steer_event(
connection_id,
pending_request,
Some(response.turn_id),
TurnSteerResult::Accepted,
/*rejection_reason*/ None,
out,
);
}
fn emit_turn_steer_event(
&mut self,
connection_id: u64,
pending_request: PendingTurnSteerState,
accepted_turn_id: Option<String>,
result: TurnSteerResult,
rejection_reason: Option<TurnSteerRejectionReason>,
out: &mut Vec<TrackEventRequest>,
) {
let Some(connection_state) = self.connections.get(&connection_id) else {
return;
};
let Some(thread_metadata) = self.thread_metadata.get(&pending_request.thread_id) else {
tracing::warn!(
thread_id = %pending_request.thread_id,
"dropping turn steer analytics event: missing thread lifecycle metadata"
);
return;
};
out.push(TrackEventRequest::TurnSteer(CodexTurnSteerEventRequest {
event_type: "codex_turn_steer_event",
event_params: CodexTurnSteerEventParams {
thread_id: pending_request.thread_id,
expected_turn_id: Some(pending_request.expected_turn_id),
accepted_turn_id,
app_server_client: connection_state.app_server_client.clone(),
runtime: connection_state.runtime.clone(),
thread_source: thread_metadata.thread_source.map(str::to_string),
subagent_source: thread_metadata.subagent_source.clone(),
parent_thread_id: thread_metadata.parent_thread_id.clone(),
num_input_images: pending_request.num_input_images,
result,
rejection_reason,
created_at: pending_request.created_at,
},
}));
}
fn maybe_emit_turn_event(&mut self, turn_id: &str, out: &mut Vec<TrackEventRequest>) {
let Some(turn_state) = self.turns.get(turn_id) else {
return;
};
if turn_state.thread_id.is_none()
|| turn_state.num_input_images.is_none()
|| turn_state.resolved_config.is_none()
|| turn_state.completed.is_none()
{
return;
}
let connection_metadata = turn_state
.connection_id
.and_then(|connection_id| self.connections.get(&connection_id))
.map(|connection_state| {
(
connection_state.app_server_client.clone(),
connection_state.runtime.clone(),
)
});
let Some((app_server_client, runtime)) = connection_metadata else {
if let Some(connection_id) = turn_state.connection_id {
tracing::warn!(
turn_id,
connection_id,
"dropping turn analytics event: missing connection metadata"
);
}
return;
};
let Some(thread_id) = turn_state.thread_id.as_ref() else {
return;
};
let Some(thread_metadata) = self.thread_metadata.get(thread_id) else {
tracing::warn!(
thread_id,
turn_id,
"dropping turn analytics event: missing thread lifecycle metadata"
);
return;
};
out.push(TrackEventRequest::TurnEvent(Box::new(
CodexTurnEventRequest {
event_type: "codex_turn_event",
event_params: codex_turn_event_params(
app_server_client,
runtime,
turn_id.to_string(),
turn_state,
thread_metadata,
),
},
)));
self.turns.remove(turn_id);
}
}
fn codex_turn_event_params(
app_server_client: CodexAppServerClientMetadata,
runtime: CodexRuntimeMetadata,
turn_id: String,
turn_state: &TurnState,
thread_metadata: &ThreadMetadataState,
) -> CodexTurnEventParams {
let (Some(thread_id), Some(num_input_images), Some(resolved_config), Some(completed)) = (
turn_state.thread_id.clone(),
turn_state.num_input_images,
turn_state.resolved_config.clone(),
turn_state.completed.clone(),
) else {
unreachable!("turn event params require a fully populated turn state");
};
let started_at = turn_state.started_at;
let TurnResolvedConfigFact {
turn_id: _resolved_turn_id,
thread_id: _resolved_thread_id,
num_input_images: _resolved_num_input_images,
submission_type,
ephemeral,
session_source: _session_source,
model,
model_provider,
sandbox_policy,
reasoning_effort,
reasoning_summary,
service_tier,
approval_policy,
approvals_reviewer,
sandbox_network_access,
collaboration_mode,
personality,
is_first_turn,
} = resolved_config;
let token_usage = turn_state.token_usage.clone();
CodexTurnEventParams {
thread_id,
turn_id,
app_server_client,
runtime,
submission_type,
ephemeral,
thread_source: thread_metadata.thread_source.map(str::to_string),
initialization_mode: thread_metadata.initialization_mode,
subagent_source: thread_metadata.subagent_source.clone(),
parent_thread_id: thread_metadata.parent_thread_id.clone(),
model: Some(model),
model_provider,
sandbox_policy: Some(sandbox_policy_mode(&sandbox_policy)),
reasoning_effort: reasoning_effort.map(|value| value.to_string()),
reasoning_summary: reasoning_summary_mode(reasoning_summary),
service_tier: service_tier
.map(|value| value.to_string())
.unwrap_or_else(|| "default".to_string()),
approval_policy: approval_policy.to_string(),
approvals_reviewer: approvals_reviewer.to_string(),
sandbox_network_access,
collaboration_mode: Some(collaboration_mode_mode(collaboration_mode)),
personality: personality_mode(personality),
num_input_images,
is_first_turn,
status: completed.status,
turn_error: completed.turn_error,
steer_count: Some(turn_state.steer_count),
total_tool_call_count: None,
shell_command_count: None,
file_change_count: None,
mcp_tool_call_count: None,
dynamic_tool_call_count: None,
subagent_tool_call_count: None,
web_search_count: None,
image_generation_count: None,
input_tokens: token_usage
.as_ref()
.map(|token_usage| token_usage.input_tokens),
cached_input_tokens: token_usage
.as_ref()
.map(|token_usage| token_usage.cached_input_tokens),
output_tokens: token_usage
.as_ref()
.map(|token_usage| token_usage.output_tokens),
reasoning_output_tokens: token_usage
.as_ref()
.map(|token_usage| token_usage.reasoning_output_tokens),
total_tokens: token_usage
.as_ref()
.map(|token_usage| token_usage.total_tokens),
duration_ms: completed.duration_ms,
started_at,
completed_at: Some(completed.completed_at),
}
}
fn sandbox_policy_mode(sandbox_policy: &SandboxPolicy) -> &'static str {
match sandbox_policy {
SandboxPolicy::DangerFullAccess => "full_access",
SandboxPolicy::ReadOnly { .. } => "read_only",
SandboxPolicy::WorkspaceWrite { .. } => "workspace_write",
SandboxPolicy::ExternalSandbox { .. } => "external_sandbox",
}
}
fn collaboration_mode_mode(mode: ModeKind) -> &'static str {
match mode {
ModeKind::Plan => "plan",
ModeKind::Default | ModeKind::PairProgramming | ModeKind::Execute => "default",
}
}
fn reasoning_summary_mode(summary: Option<ReasoningSummary>) -> Option<String> {
match summary {
Some(ReasoningSummary::None) | None => None,
Some(summary) => Some(summary.to_string()),
}
}
fn personality_mode(personality: Option<Personality>) -> Option<String> {
match personality {
Some(Personality::None) | None => None,
Some(personality) => Some(personality.to_string()),
}
}
fn analytics_turn_status(status: codex_app_server_protocol::TurnStatus) -> Option<TurnStatus> {
match status {
codex_app_server_protocol::TurnStatus::Completed => Some(TurnStatus::Completed),
codex_app_server_protocol::TurnStatus::Failed => Some(TurnStatus::Failed),
codex_app_server_protocol::TurnStatus::Interrupted => Some(TurnStatus::Interrupted),
codex_app_server_protocol::TurnStatus::InProgress => None,
}
}
fn num_input_images(input: &[UserInput]) -> usize {
input
.iter()
.filter(|item| matches!(item, UserInput::Image { .. } | UserInput::LocalImage { .. }))
.count()
}
fn rejection_reason_from_error_type(
error_type: Option<AnalyticsJsonRpcError>,
) -> Option<TurnSteerRejectionReason> {
match error_type? {
AnalyticsJsonRpcError::TurnSteer(error) => Some(error.into()),
AnalyticsJsonRpcError::Input(error) => Some(error.into()),
}
}
pub(crate) fn skill_id_for_local_skill(

View File

@@ -28,6 +28,7 @@ use std::time::Duration;
pub use codex_app_server::in_process::DEFAULT_IN_PROCESS_CHANNEL_CAPACITY;
pub use codex_app_server::in_process::InProcessServerEvent;
use codex_app_server::in_process::InProcessStartArgs;
use codex_app_server::in_process::LogDbLayer;
use codex_app_server_protocol::ClientInfo;
use codex_app_server_protocol::ClientNotification;
use codex_app_server_protocol::ClientRequest;
@@ -354,6 +355,8 @@ pub struct InProcessClientStartArgs {
pub cloud_requirements: CloudRequirementsLoader,
/// Feedback sink used by app-server/core telemetry and logs.
pub feedback: CodexFeedback,
/// SQLite tracing layer used to flush recently emitted logs before feedback upload.
pub log_db: Option<LogDbLayer>,
/// Environment manager used by core execution and filesystem operations.
pub environment_manager: Arc<EnvironmentManager>,
/// Startup warnings emitted after initialize succeeds.
@@ -405,6 +408,7 @@ impl InProcessClientStartArgs {
loader_overrides: self.loader_overrides,
cloud_requirements: self.cloud_requirements,
feedback: self.feedback,
log_db: self.log_db,
environment_manager: self.environment_manager,
config_warnings: self.config_warnings,
session_source: self.session_source,
@@ -967,6 +971,7 @@ mod tests {
match ConfigBuilder::default().build().await {
Ok(config) => config,
Err(_) => Config::load_default_with_cli_overrides(Vec::new())
.await
.expect("default config should load"),
}
}
@@ -982,6 +987,7 @@ mod tests {
loader_overrides: LoaderOverrides::default(),
cloud_requirements: CloudRequirementsLoader::default(),
feedback: CodexFeedback::new(),
log_db: None,
environment_manager: Arc::new(EnvironmentManager::new(/*exec_server_url*/ None)),
config_warnings: Vec::new(),
session_source,
@@ -1994,6 +2000,7 @@ mod tests {
loader_overrides: LoaderOverrides::default(),
cloud_requirements: CloudRequirementsLoader::default(),
feedback: CodexFeedback::new(),
log_db: None,
environment_manager: environment_manager.clone(),
config_warnings: Vec::new(),
session_source: SessionSource::Exec,

View File

@@ -605,6 +605,16 @@
"description": {
"type": "string"
},
"details": {
"anyOf": [
{
"$ref": "#/definitions/MigrationDetails"
},
{
"type": "null"
}
]
},
"itemType": {
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
}
@@ -620,6 +630,7 @@
"AGENTS_MD",
"CONFIG",
"SKILLS",
"PLUGINS",
"MCP_SERVER_CONFIG"
],
"type": "string"
@@ -1233,6 +1244,32 @@
}
]
},
"MarketplaceAddParams": {
"properties": {
"refName": {
"type": [
"string",
"null"
]
},
"source": {
"type": "string"
},
"sparsePaths": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
}
},
"required": [
"source"
],
"type": "object"
},
"McpResourceReadParams": {
"properties": {
"server": {
@@ -1333,6 +1370,20 @@
}
]
},
"MigrationDetails": {
"properties": {
"plugins": {
"items": {
"$ref": "#/definitions/PluginsMigration"
},
"type": "array"
}
},
"required": [
"plugins"
],
"type": "object"
},
"ModeKind": {
"description": "Initial collaboration mode to use when the TUI starts.",
"enum": [
@@ -1452,6 +1503,24 @@
],
"type": "object"
},
"PluginsMigration": {
"properties": {
"marketplaceName": {
"type": "string"
},
"pluginNames": {
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"marketplaceName",
"pluginNames"
],
"type": "object"
},
"ReadOnlyAccess": {
"oneOf": [
{
@@ -1499,6 +1568,13 @@
}
]
},
"RealtimeOutputModality": {
"enum": [
"text",
"audio"
],
"type": "string"
},
"RealtimeVoice": {
"enum": [
"alloy",
@@ -2715,6 +2791,23 @@
],
"type": "object"
},
"ThreadInjectItemsParams": {
"properties": {
"items": {
"description": "Raw Responses API items to append to the thread's model-visible history.",
"items": true,
"type": "array"
},
"threadId": {
"type": "string"
}
},
"required": [
"items",
"threadId"
],
"type": "object"
},
"ThreadListParams": {
"properties": {
"archived": {
@@ -2809,6 +2902,13 @@
},
"type": "object"
},
"ThreadMemoryMode": {
"enum": [
"enabled",
"disabled"
],
"type": "string"
},
"ThreadMetadataGitInfoUpdateParams": {
"properties": {
"branch": {
@@ -3952,6 +4052,31 @@
"title": "Thread/readRequest",
"type": "object"
},
{
"description": "Append raw Responses API items to the thread history without starting a user turn.",
"properties": {
"id": {
"$ref": "#/definitions/RequestId"
},
"method": {
"enum": [
"thread/inject_items"
],
"title": "Thread/injectItemsRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadInjectItemsParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "Thread/injectItemsRequest",
"type": "object"
},
{
"properties": {
"id": {
@@ -3976,6 +4101,30 @@
"title": "Skills/listRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/RequestId"
},
"method": {
"enum": [
"marketplace/add"
],
"title": "Marketplace/addRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/MarketplaceAddParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "Marketplace/addRequest",
"type": "object"
},
{
"properties": {
"id": {

View File

@@ -75,7 +75,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -326,11 +326,15 @@
]
},
"cwd": {
"description": "The command's working directory.",
"type": [
"string",
"null"
]
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
],
"description": "The command's working directory."
},
"itemId": {
"type": "string"

View File

@@ -608,7 +608,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -1180,7 +1180,7 @@
"type": "string"
},
"cwd": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"source": {
"$ref": "#/definitions/GuardianCommandSource"
@@ -1211,7 +1211,7 @@
"type": "array"
},
"cwd": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"program": {
"type": "string"
@@ -1240,11 +1240,11 @@
{
"properties": {
"cwd": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"files": {
"items": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": "array"
},
@@ -1518,7 +1518,7 @@
"$ref": "#/definitions/HookScope"
},
"sourcePath": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"startedAt": {
"format": "int64",
@@ -2461,8 +2461,12 @@
"type": "integer"
},
"cwd": {
"description": "Working directory captured for the thread.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "Working directory captured for the thread."
},
"ephemeral": {
"description": "Whether the thread is ephemeral and should not be materialized on disk.",
@@ -2769,8 +2773,12 @@
"type": "array"
},
"cwd": {
"description": "The command's working directory.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "The command's working directory."
},
"durationMs": {
"description": "The duration of the command execution in milliseconds.",
@@ -2883,6 +2891,12 @@
"id": {
"type": "string"
},
"mcpAppResourceUri": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
@@ -3099,7 +3113,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -3132,9 +3146,13 @@
]
},
"savedPath": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"status": {
@@ -3384,13 +3402,35 @@
],
"type": "object"
},
"ThreadRealtimeTranscriptUpdatedNotification": {
"ThreadRealtimeTranscriptDeltaNotification": {
"description": "EXPERIMENTAL - flat transcript delta emitted whenever realtime transcript text changes.",
"properties": {
"delta": {
"description": "Live transcript delta from the realtime event.",
"type": "string"
},
"role": {
"type": "string"
},
"threadId": {
"type": "string"
}
},
"required": [
"delta",
"role",
"threadId"
],
"type": "object"
},
"ThreadRealtimeTranscriptDoneNotification": {
"description": "EXPERIMENTAL - final transcript text emitted when realtime completes a transcript part.",
"properties": {
"role": {
"type": "string"
},
"text": {
"description": "Final complete text for the transcript part.",
"type": "string"
},
"threadId": {
@@ -4949,20 +4989,40 @@
"properties": {
"method": {
"enum": [
"thread/realtime/transcriptUpdated"
"thread/realtime/transcript/delta"
],
"title": "Thread/realtime/transcriptUpdatedNotificationMethod",
"title": "Thread/realtime/transcript/deltaNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadRealtimeTranscriptUpdatedNotification"
"$ref": "#/definitions/ThreadRealtimeTranscriptDeltaNotification"
}
},
"required": [
"method",
"params"
],
"title": "Thread/realtime/transcriptUpdatedNotification",
"title": "Thread/realtime/transcript/deltaNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"thread/realtime/transcript/done"
],
"title": "Thread/realtime/transcript/doneNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadRealtimeTranscriptDoneNotification"
}
},
"required": [
"method",
"params"
],
"title": "Thread/realtime/transcript/doneNotification",
"type": "object"
},
{

View File

@@ -141,7 +141,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -346,11 +346,15 @@
]
},
"cwd": {
"description": "The command's working directory.",
"type": [
"string",
"null"
]
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
],
"description": "The command's working directory."
},
"itemId": {
"type": "string"

View File

@@ -578,6 +578,31 @@
"title": "Thread/readRequest",
"type": "object"
},
{
"description": "Append raw Responses API items to the thread history without starting a user turn.",
"properties": {
"id": {
"$ref": "#/definitions/v2/RequestId"
},
"method": {
"enum": [
"thread/inject_items"
],
"title": "Thread/injectItemsRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/v2/ThreadInjectItemsParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "Thread/injectItemsRequest",
"type": "object"
},
{
"properties": {
"id": {
@@ -602,6 +627,30 @@
"title": "Skills/listRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/v2/RequestId"
},
"method": {
"enum": [
"marketplace/add"
],
"title": "Marketplace/addRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/v2/MarketplaceAddParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "Marketplace/addRequest",
"type": "object"
},
{
"properties": {
"id": {
@@ -1792,11 +1841,15 @@
]
},
"cwd": {
"description": "The command's working directory.",
"type": [
"string",
"null"
]
"anyOf": [
{
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
{
"type": "null"
}
],
"description": "The command's working directory."
},
"itemId": {
"type": "string"
@@ -4330,20 +4383,40 @@
"properties": {
"method": {
"enum": [
"thread/realtime/transcriptUpdated"
"thread/realtime/transcript/delta"
],
"title": "Thread/realtime/transcriptUpdatedNotificationMethod",
"title": "Thread/realtime/transcript/deltaNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/v2/ThreadRealtimeTranscriptUpdatedNotification"
"$ref": "#/definitions/v2/ThreadRealtimeTranscriptDeltaNotification"
}
},
"required": [
"method",
"params"
],
"title": "Thread/realtime/transcriptUpdatedNotification",
"title": "Thread/realtime/transcript/deltaNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"thread/realtime/transcript/done"
],
"title": "Thread/realtime/transcript/doneNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/v2/ThreadRealtimeTranscriptDoneNotification"
}
},
"required": [
"method",
"params"
],
"title": "Thread/realtime/transcript/doneNotification",
"type": "object"
},
{
@@ -5881,7 +5954,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -7436,6 +7509,16 @@
"description": {
"type": "string"
},
"details": {
"anyOf": [
{
"$ref": "#/definitions/v2/MigrationDetails"
},
{
"type": "null"
}
]
},
"itemType": {
"$ref": "#/definitions/v2/ExternalAgentConfigMigrationItemType"
}
@@ -7451,6 +7534,7 @@
"AGENTS_MD",
"CONFIG",
"SKILLS",
"PLUGINS",
"MCP_SERVER_CONFIG"
],
"type": "string"
@@ -7688,11 +7772,15 @@
"type": "integer"
},
"isDirectory": {
"description": "Whether the path currently resolves to a directory.",
"description": "Whether the path resolves to a directory.",
"type": "boolean"
},
"isFile": {
"description": "Whether the path currently resolves to a regular file.",
"description": "Whether the path resolves to a regular file.",
"type": "boolean"
},
"isSymlink": {
"description": "Whether the path itself is a symbolic link.",
"type": "boolean"
},
"modifiedAtMs": {
@@ -7705,6 +7793,7 @@
"createdAtMs",
"isDirectory",
"isFile",
"isSymlink",
"modifiedAtMs"
],
"title": "FsGetMetadataResponse",
@@ -8169,7 +8258,7 @@
"type": "string"
},
"cwd": {
"type": "string"
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
"source": {
"$ref": "#/definitions/v2/GuardianCommandSource"
@@ -8200,7 +8289,7 @@
"type": "array"
},
"cwd": {
"type": "string"
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
"program": {
"type": "string"
@@ -8229,11 +8318,11 @@
{
"properties": {
"cwd": {
"type": "string"
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
"files": {
"items": {
"type": "string"
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
"type": "array"
},
@@ -8509,7 +8598,7 @@
"$ref": "#/definitions/v2/HookScope"
},
"sourcePath": {
"type": "string"
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
"startedAt": {
"format": "int64",
@@ -9030,6 +9119,55 @@
"title": "LogoutAccountResponse",
"type": "object"
},
"MarketplaceAddParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"refName": {
"type": [
"string",
"null"
]
},
"source": {
"type": "string"
},
"sparsePaths": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
}
},
"required": [
"source"
],
"title": "MarketplaceAddParams",
"type": "object"
},
"MarketplaceAddResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"alreadyAdded": {
"type": "boolean"
},
"installedRoot": {
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
"marketplaceName": {
"type": "string"
}
},
"required": [
"alreadyAdded",
"installedRoot",
"marketplaceName"
],
"title": "MarketplaceAddResponse",
"type": "object"
},
"MarketplaceInterface": {
"properties": {
"displayName": {
@@ -9424,6 +9562,20 @@
}
]
},
"MigrationDetails": {
"properties": {
"plugins": {
"items": {
"$ref": "#/definitions/v2/PluginsMigration"
},
"type": "array"
}
},
"required": [
"plugins"
],
"type": "object"
},
"ModeKind": {
"description": "Initial collaboration mode to use when the TUI starts.",
"enum": [
@@ -9706,12 +9858,6 @@
"null"
]
},
"dangerFullAccessDenylistOnly": {
"type": [
"boolean",
"null"
]
},
"dangerouslyAllowAllUnixSockets": {
"type": [
"boolean",
@@ -10352,6 +10498,24 @@
"title": "PluginUninstallResponse",
"type": "object"
},
"PluginsMigration": {
"properties": {
"marketplaceName": {
"type": "string"
},
"pluginNames": {
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"marketplaceName",
"pluginNames"
],
"type": "object"
},
"ProfileV2": {
"additionalProperties": true,
"properties": {
@@ -10615,6 +10779,13 @@
],
"type": "string"
},
"RealtimeOutputModality": {
"enum": [
"text",
"audio"
],
"type": "string"
},
"RealtimeVoice": {
"enum": [
"alloy",
@@ -12034,15 +12205,23 @@
]
},
"iconLarge": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"iconSmall": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"shortDescription": {
@@ -12086,7 +12265,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
"scope": {
"$ref": "#/definitions/v2/SkillScope"
@@ -12139,7 +12318,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
"shortDescription": {
"type": [
@@ -12523,8 +12702,12 @@
"type": "integer"
},
"cwd": {
"description": "Working directory captured for the thread.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/v2/AbsolutePathBuf"
}
],
"description": "Working directory captured for the thread."
},
"ephemeral": {
"description": "Whether the thread is ephemeral and should not be materialized on disk.",
@@ -12804,13 +12987,13 @@
"description": "Reviewer currently used for approval requests on this thread."
},
"cwd": {
"type": "string"
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
"instructionSources": {
"default": [],
"description": "Instruction source files currently loaded for this thread.",
"items": {
"type": "string"
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
"type": "array"
},
@@ -12862,6 +13045,30 @@
"ThreadId": {
"type": "string"
},
"ThreadInjectItemsParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"items": {
"description": "Raw Responses API items to append to the thread's model-visible history.",
"items": true,
"type": "array"
},
"threadId": {
"type": "string"
}
},
"required": [
"items",
"threadId"
],
"title": "ThreadInjectItemsParams",
"type": "object"
},
"ThreadInjectItemsResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ThreadInjectItemsResponse",
"type": "object"
},
"ThreadItem": {
"oneOf": [
{
@@ -13044,8 +13251,12 @@
"type": "array"
},
"cwd": {
"description": "The command's working directory.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/v2/AbsolutePathBuf"
}
],
"description": "The command's working directory."
},
"durationMs": {
"description": "The duration of the command execution in milliseconds.",
@@ -13158,6 +13369,12 @@
"id": {
"type": "string"
},
"mcpAppResourceUri": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
@@ -13374,7 +13591,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -13407,9 +13624,13 @@
]
},
"savedPath": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"status": {
@@ -13647,6 +13868,13 @@
"title": "ThreadLoadedListResponse",
"type": "object"
},
"ThreadMemoryMode": {
"enum": [
"enabled",
"disabled"
],
"type": "string"
},
"ThreadMetadataGitInfoUpdateParams": {
"properties": {
"branch": {
@@ -13954,14 +14182,38 @@
"title": "ThreadRealtimeStartedNotification",
"type": "object"
},
"ThreadRealtimeTranscriptUpdatedNotification": {
"ThreadRealtimeTranscriptDeltaNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "EXPERIMENTAL - flat transcript delta emitted whenever realtime transcript text changes.",
"properties": {
"delta": {
"description": "Live transcript delta from the realtime event.",
"type": "string"
},
"role": {
"type": "string"
},
"threadId": {
"type": "string"
}
},
"required": [
"delta",
"role",
"threadId"
],
"title": "ThreadRealtimeTranscriptDeltaNotification",
"type": "object"
},
"ThreadRealtimeTranscriptDoneNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "EXPERIMENTAL - final transcript text emitted when realtime completes a transcript part.",
"properties": {
"role": {
"type": "string"
},
"text": {
"description": "Final complete text for the transcript part.",
"type": "string"
},
"threadId": {
@@ -13973,7 +14225,7 @@
"text",
"threadId"
],
"title": "ThreadRealtimeTranscriptUpdatedNotification",
"title": "ThreadRealtimeTranscriptDoneNotification",
"type": "object"
},
"ThreadResumeParams": {
@@ -14101,13 +14353,13 @@
"description": "Reviewer currently used for approval requests on this thread."
},
"cwd": {
"type": "string"
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
"instructionSources": {
"default": [],
"description": "Instruction source files currently loaded for this thread.",
"items": {
"type": "string"
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
"type": "array"
},
@@ -14400,13 +14652,13 @@
"description": "Reviewer currently used for approval requests on this thread."
},
"cwd": {
"type": "string"
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
"instructionSources": {
"default": [],
"description": "Instruction source files currently loaded for this thread.",
"items": {
"type": "string"
"$ref": "#/definitions/v2/AbsolutePathBuf"
},
"type": "array"
},

View File

@@ -1160,6 +1160,31 @@
"title": "Thread/readRequest",
"type": "object"
},
{
"description": "Append raw Responses API items to the thread history without starting a user turn.",
"properties": {
"id": {
"$ref": "#/definitions/RequestId"
},
"method": {
"enum": [
"thread/inject_items"
],
"title": "Thread/injectItemsRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadInjectItemsParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "Thread/injectItemsRequest",
"type": "object"
},
{
"properties": {
"id": {
@@ -1184,6 +1209,30 @@
"title": "Skills/listRequest",
"type": "object"
},
{
"properties": {
"id": {
"$ref": "#/definitions/RequestId"
},
"method": {
"enum": [
"marketplace/add"
],
"title": "Marketplace/addRequestMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/MarketplaceAddParams"
}
},
"required": [
"id",
"method",
"params"
],
"title": "Marketplace/addRequest",
"type": "object"
},
{
"properties": {
"id": {
@@ -2522,7 +2571,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -4077,6 +4126,16 @@
"description": {
"type": "string"
},
"details": {
"anyOf": [
{
"$ref": "#/definitions/MigrationDetails"
},
{
"type": "null"
}
]
},
"itemType": {
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
}
@@ -4092,6 +4151,7 @@
"AGENTS_MD",
"CONFIG",
"SKILLS",
"PLUGINS",
"MCP_SERVER_CONFIG"
],
"type": "string"
@@ -4329,11 +4389,15 @@
"type": "integer"
},
"isDirectory": {
"description": "Whether the path currently resolves to a directory.",
"description": "Whether the path resolves to a directory.",
"type": "boolean"
},
"isFile": {
"description": "Whether the path currently resolves to a regular file.",
"description": "Whether the path resolves to a regular file.",
"type": "boolean"
},
"isSymlink": {
"description": "Whether the path itself is a symbolic link.",
"type": "boolean"
},
"modifiedAtMs": {
@@ -4346,6 +4410,7 @@
"createdAtMs",
"isDirectory",
"isFile",
"isSymlink",
"modifiedAtMs"
],
"title": "FsGetMetadataResponse",
@@ -4921,7 +4986,7 @@
"type": "string"
},
"cwd": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"source": {
"$ref": "#/definitions/GuardianCommandSource"
@@ -4952,7 +5017,7 @@
"type": "array"
},
"cwd": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"program": {
"type": "string"
@@ -4981,11 +5046,11 @@
{
"properties": {
"cwd": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"files": {
"items": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": "array"
},
@@ -5261,7 +5326,7 @@
"$ref": "#/definitions/HookScope"
},
"sourcePath": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"startedAt": {
"format": "int64",
@@ -5826,6 +5891,55 @@
"title": "LogoutAccountResponse",
"type": "object"
},
"MarketplaceAddParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"refName": {
"type": [
"string",
"null"
]
},
"source": {
"type": "string"
},
"sparsePaths": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
}
},
"required": [
"source"
],
"title": "MarketplaceAddParams",
"type": "object"
},
"MarketplaceAddResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"alreadyAdded": {
"type": "boolean"
},
"installedRoot": {
"$ref": "#/definitions/AbsolutePathBuf"
},
"marketplaceName": {
"type": "string"
}
},
"required": [
"alreadyAdded",
"installedRoot",
"marketplaceName"
],
"title": "MarketplaceAddResponse",
"type": "object"
},
"MarketplaceInterface": {
"properties": {
"displayName": {
@@ -6220,6 +6334,20 @@
}
]
},
"MigrationDetails": {
"properties": {
"plugins": {
"items": {
"$ref": "#/definitions/PluginsMigration"
},
"type": "array"
}
},
"required": [
"plugins"
],
"type": "object"
},
"ModeKind": {
"description": "Initial collaboration mode to use when the TUI starts.",
"enum": [
@@ -6502,12 +6630,6 @@
"null"
]
},
"dangerFullAccessDenylistOnly": {
"type": [
"boolean",
"null"
]
},
"dangerouslyAllowAllUnixSockets": {
"type": [
"boolean",
@@ -7148,6 +7270,24 @@
"title": "PluginUninstallResponse",
"type": "object"
},
"PluginsMigration": {
"properties": {
"marketplaceName": {
"type": "string"
},
"pluginNames": {
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"marketplaceName",
"pluginNames"
],
"type": "object"
},
"ProfileV2": {
"additionalProperties": true,
"properties": {
@@ -7411,6 +7551,13 @@
],
"type": "string"
},
"RealtimeOutputModality": {
"enum": [
"text",
"audio"
],
"type": "string"
},
"RealtimeVoice": {
"enum": [
"alloy",
@@ -9580,20 +9727,40 @@
"properties": {
"method": {
"enum": [
"thread/realtime/transcriptUpdated"
"thread/realtime/transcript/delta"
],
"title": "Thread/realtime/transcriptUpdatedNotificationMethod",
"title": "Thread/realtime/transcript/deltaNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadRealtimeTranscriptUpdatedNotification"
"$ref": "#/definitions/ThreadRealtimeTranscriptDeltaNotification"
}
},
"required": [
"method",
"params"
],
"title": "Thread/realtime/transcriptUpdatedNotification",
"title": "Thread/realtime/transcript/deltaNotification",
"type": "object"
},
{
"properties": {
"method": {
"enum": [
"thread/realtime/transcript/done"
],
"title": "Thread/realtime/transcript/doneNotificationMethod",
"type": "string"
},
"params": {
"$ref": "#/definitions/ThreadRealtimeTranscriptDoneNotification"
}
},
"required": [
"method",
"params"
],
"title": "Thread/realtime/transcript/doneNotification",
"type": "object"
},
{
@@ -9882,15 +10049,23 @@
]
},
"iconLarge": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"iconSmall": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"shortDescription": {
@@ -9934,7 +10109,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"scope": {
"$ref": "#/definitions/SkillScope"
@@ -9987,7 +10162,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"shortDescription": {
"type": [
@@ -10371,8 +10546,12 @@
"type": "integer"
},
"cwd": {
"description": "Working directory captured for the thread.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "Working directory captured for the thread."
},
"ephemeral": {
"description": "Whether the thread is ephemeral and should not be materialized on disk.",
@@ -10652,13 +10831,13 @@
"description": "Reviewer currently used for approval requests on this thread."
},
"cwd": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"instructionSources": {
"default": [],
"description": "Instruction source files currently loaded for this thread.",
"items": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": "array"
},
@@ -10710,6 +10889,30 @@
"ThreadId": {
"type": "string"
},
"ThreadInjectItemsParams": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"items": {
"description": "Raw Responses API items to append to the thread's model-visible history.",
"items": true,
"type": "array"
},
"threadId": {
"type": "string"
}
},
"required": [
"items",
"threadId"
],
"title": "ThreadInjectItemsParams",
"type": "object"
},
"ThreadInjectItemsResponse": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ThreadInjectItemsResponse",
"type": "object"
},
"ThreadItem": {
"oneOf": [
{
@@ -10892,8 +11095,12 @@
"type": "array"
},
"cwd": {
"description": "The command's working directory.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "The command's working directory."
},
"durationMs": {
"description": "The duration of the command execution in milliseconds.",
@@ -11006,6 +11213,12 @@
"id": {
"type": "string"
},
"mcpAppResourceUri": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
@@ -11222,7 +11435,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -11255,9 +11468,13 @@
]
},
"savedPath": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"status": {
@@ -11495,6 +11712,13 @@
"title": "ThreadLoadedListResponse",
"type": "object"
},
"ThreadMemoryMode": {
"enum": [
"enabled",
"disabled"
],
"type": "string"
},
"ThreadMetadataGitInfoUpdateParams": {
"properties": {
"branch": {
@@ -11802,14 +12026,38 @@
"title": "ThreadRealtimeStartedNotification",
"type": "object"
},
"ThreadRealtimeTranscriptUpdatedNotification": {
"ThreadRealtimeTranscriptDeltaNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "EXPERIMENTAL - flat transcript delta emitted whenever realtime transcript text changes.",
"properties": {
"delta": {
"description": "Live transcript delta from the realtime event.",
"type": "string"
},
"role": {
"type": "string"
},
"threadId": {
"type": "string"
}
},
"required": [
"delta",
"role",
"threadId"
],
"title": "ThreadRealtimeTranscriptDeltaNotification",
"type": "object"
},
"ThreadRealtimeTranscriptDoneNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "EXPERIMENTAL - final transcript text emitted when realtime completes a transcript part.",
"properties": {
"role": {
"type": "string"
},
"text": {
"description": "Final complete text for the transcript part.",
"type": "string"
},
"threadId": {
@@ -11821,7 +12069,7 @@
"text",
"threadId"
],
"title": "ThreadRealtimeTranscriptUpdatedNotification",
"title": "ThreadRealtimeTranscriptDoneNotification",
"type": "object"
},
"ThreadResumeParams": {
@@ -11949,13 +12197,13 @@
"description": "Reviewer currently used for approval requests on this thread."
},
"cwd": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"instructionSources": {
"default": [],
"description": "Instruction source files currently loaded for this thread.",
"items": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": "array"
},
@@ -12248,13 +12496,13 @@
"description": "Reviewer currently used for approval requests on this thread."
},
"cwd": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"instructionSources": {
"default": [],
"description": "Instruction source files currently loaded for this thread.",
"items": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": "array"
},

View File

@@ -151,12 +151,6 @@
"null"
]
},
"dangerFullAccessDenylistOnly": {
"type": [
"boolean",
"null"
]
},
"dangerouslyAllowAllUnixSockets": {
"type": [
"boolean",

View File

@@ -13,6 +13,16 @@
"description": {
"type": "string"
},
"details": {
"anyOf": [
{
"$ref": "#/definitions/MigrationDetails"
},
{
"type": "null"
}
]
},
"itemType": {
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
}
@@ -28,9 +38,42 @@
"AGENTS_MD",
"CONFIG",
"SKILLS",
"PLUGINS",
"MCP_SERVER_CONFIG"
],
"type": "string"
},
"MigrationDetails": {
"properties": {
"plugins": {
"items": {
"$ref": "#/definitions/PluginsMigration"
},
"type": "array"
}
},
"required": [
"plugins"
],
"type": "object"
},
"PluginsMigration": {
"properties": {
"marketplaceName": {
"type": "string"
},
"pluginNames": {
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"marketplaceName",
"pluginNames"
],
"type": "object"
}
},
"properties": {

View File

@@ -13,6 +13,16 @@
"description": {
"type": "string"
},
"details": {
"anyOf": [
{
"$ref": "#/definitions/MigrationDetails"
},
{
"type": "null"
}
]
},
"itemType": {
"$ref": "#/definitions/ExternalAgentConfigMigrationItemType"
}
@@ -28,9 +38,42 @@
"AGENTS_MD",
"CONFIG",
"SKILLS",
"PLUGINS",
"MCP_SERVER_CONFIG"
],
"type": "string"
},
"MigrationDetails": {
"properties": {
"plugins": {
"items": {
"$ref": "#/definitions/PluginsMigration"
},
"type": "array"
}
},
"required": [
"plugins"
],
"type": "object"
},
"PluginsMigration": {
"properties": {
"marketplaceName": {
"type": "string"
},
"pluginNames": {
"items": {
"type": "string"
},
"type": "array"
}
},
"required": [
"marketplaceName",
"pluginNames"
],
"type": "object"
}
},
"properties": {

View File

@@ -8,11 +8,15 @@
"type": "integer"
},
"isDirectory": {
"description": "Whether the path currently resolves to a directory.",
"description": "Whether the path resolves to a directory.",
"type": "boolean"
},
"isFile": {
"description": "Whether the path currently resolves to a regular file.",
"description": "Whether the path resolves to a regular file.",
"type": "boolean"
},
"isSymlink": {
"description": "Whether the path itself is a symbolic link.",
"type": "boolean"
},
"modifiedAtMs": {
@@ -25,6 +29,7 @@
"createdAtMs",
"isDirectory",
"isFile",
"isSymlink",
"modifiedAtMs"
],
"title": "FsGetMetadataResponse",

View File

@@ -1,6 +1,10 @@
{
"$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"
},
"HookEventName": {
"enum": [
"preToolUse",
@@ -103,7 +107,7 @@
"$ref": "#/definitions/HookScope"
},
"sourcePath": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"startedAt": {
"format": "int64",

View File

@@ -1,6 +1,10 @@
{
"$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"
},
"HookEventName": {
"enum": [
"preToolUse",
@@ -103,7 +107,7 @@
"$ref": "#/definitions/HookScope"
},
"sourcePath": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"startedAt": {
"format": "int64",

View File

@@ -1,6 +1,10 @@
{
"$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"
},
"ByteRange": {
"properties": {
"end": {
@@ -78,7 +82,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -665,8 +669,12 @@
"type": "array"
},
"cwd": {
"description": "The command's working directory.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "The command's working directory."
},
"durationMs": {
"description": "The duration of the command execution in milliseconds.",
@@ -779,6 +787,12 @@
"id": {
"type": "string"
},
"mcpAppResourceUri": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
@@ -995,7 +1009,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -1028,9 +1042,13 @@
]
},
"savedPath": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"status": {

View File

@@ -1,6 +1,10 @@
{
"$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"
},
"AutoReviewDecisionSource": {
"description": "[UNSTABLE] Source that produced a terminal guardian approval review decision.",
"enum": [
@@ -54,7 +58,7 @@
"type": "string"
},
"cwd": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"source": {
"$ref": "#/definitions/GuardianCommandSource"
@@ -85,7 +89,7 @@
"type": "array"
},
"cwd": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"program": {
"type": "string"
@@ -114,11 +118,11 @@
{
"properties": {
"cwd": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"files": {
"items": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": "array"
},

View File

@@ -1,6 +1,10 @@
{
"$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"
},
"GuardianApprovalReview": {
"description": "[UNSTABLE] Temporary guardian approval review payload used by `item/autoApprovalReview/*` notifications. This shape is expected to change soon.",
"properties": {
@@ -47,7 +51,7 @@
"type": "string"
},
"cwd": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"source": {
"$ref": "#/definitions/GuardianCommandSource"
@@ -78,7 +82,7 @@
"type": "array"
},
"cwd": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"program": {
"type": "string"
@@ -107,11 +111,11 @@
{
"properties": {
"cwd": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"files": {
"items": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": "array"
},

View File

@@ -1,6 +1,10 @@
{
"$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"
},
"ByteRange": {
"properties": {
"end": {
@@ -78,7 +82,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -665,8 +669,12 @@
"type": "array"
},
"cwd": {
"description": "The command's working directory.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "The command's working directory."
},
"durationMs": {
"description": "The duration of the command execution in milliseconds.",
@@ -779,6 +787,12 @@
"id": {
"type": "string"
},
"mcpAppResourceUri": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
@@ -995,7 +1009,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -1028,9 +1042,13 @@
]
},
"savedPath": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"status": {

View File

@@ -0,0 +1,28 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"refName": {
"type": [
"string",
"null"
]
},
"source": {
"type": "string"
},
"sparsePaths": {
"items": {
"type": "string"
},
"type": [
"array",
"null"
]
}
},
"required": [
"source"
],
"title": "MarketplaceAddParams",
"type": "object"
}

View File

@@ -0,0 +1,27 @@
{
"$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"
}
},
"properties": {
"alreadyAdded": {
"type": "boolean"
},
"installedRoot": {
"$ref": "#/definitions/AbsolutePathBuf"
},
"marketplaceName": {
"type": "string"
}
},
"required": [
"alreadyAdded",
"installedRoot",
"marketplaceName"
],
"title": "MarketplaceAddResponse",
"type": "object"
}

View File

@@ -293,15 +293,23 @@
]
},
"iconLarge": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"iconSmall": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"shortDescription": {
@@ -335,7 +343,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"shortDescription": {
"type": [

View File

@@ -1,6 +1,10 @@
{
"$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"
},
"ByteRange": {
"properties": {
"end": {
@@ -214,7 +218,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -808,8 +812,12 @@
"type": "array"
},
"cwd": {
"description": "The command's working directory.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "The command's working directory."
},
"durationMs": {
"description": "The duration of the command execution in milliseconds.",
@@ -922,6 +930,12 @@
"id": {
"type": "string"
},
"mcpAppResourceUri": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
@@ -1138,7 +1152,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -1171,9 +1185,13 @@
]
},
"savedPath": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"status": {

View File

@@ -1,6 +1,10 @@
{
"$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"
},
"SkillDependencies": {
"properties": {
"tools": {
@@ -51,15 +55,23 @@
]
},
"iconLarge": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"iconSmall": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"shortDescription": {
@@ -103,7 +115,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"scope": {
"$ref": "#/definitions/SkillScope"

View File

@@ -279,7 +279,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -1036,8 +1036,12 @@
"type": "integer"
},
"cwd": {
"description": "Working directory captured for the thread.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "Working directory captured for the thread."
},
"ephemeral": {
"description": "Whether the thread is ephemeral and should not be materialized on disk.",
@@ -1322,8 +1326,12 @@
"type": "array"
},
"cwd": {
"description": "The command's working directory.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "The command's working directory."
},
"durationMs": {
"description": "The duration of the command execution in milliseconds.",
@@ -1436,6 +1444,12 @@
"id": {
"type": "string"
},
"mcpAppResourceUri": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
@@ -1652,7 +1666,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -1685,9 +1699,13 @@
]
},
"savedPath": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"status": {
@@ -2185,13 +2203,13 @@
"description": "Reviewer currently used for approval requests on this thread."
},
"cwd": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"instructionSources": {
"default": [],
"description": "Instruction source files currently loaded for this thread.",
"items": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": "array"
},

View File

@@ -0,0 +1,19 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"items": {
"description": "Raw Responses API items to append to the thread's model-visible history.",
"items": true,
"type": "array"
},
"threadId": {
"type": "string"
}
},
"required": [
"items",
"threadId"
],
"title": "ThreadInjectItemsParams",
"type": "object"
}

View File

@@ -0,0 +1,5 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ThreadInjectItemsResponse",
"type": "object"
}

View File

@@ -1,6 +1,10 @@
{
"$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"
},
"AgentPath": {
"type": "string"
},
@@ -217,7 +221,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -794,8 +798,12 @@
"type": "integer"
},
"cwd": {
"description": "Working directory captured for the thread.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "Working directory captured for the thread."
},
"ephemeral": {
"description": "Whether the thread is ephemeral and should not be materialized on disk.",
@@ -1080,8 +1088,12 @@
"type": "array"
},
"cwd": {
"description": "The command's working directory.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "The command's working directory."
},
"durationMs": {
"description": "The duration of the command execution in milliseconds.",
@@ -1194,6 +1206,12 @@
"id": {
"type": "string"
},
"mcpAppResourceUri": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
@@ -1410,7 +1428,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -1443,9 +1461,13 @@
]
},
"savedPath": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"status": {

View File

@@ -1,6 +1,10 @@
{
"$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"
},
"AgentPath": {
"type": "string"
},
@@ -217,7 +221,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -794,8 +798,12 @@
"type": "integer"
},
"cwd": {
"description": "Working directory captured for the thread.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "Working directory captured for the thread."
},
"ephemeral": {
"description": "Whether the thread is ephemeral and should not be materialized on disk.",
@@ -1080,8 +1088,12 @@
"type": "array"
},
"cwd": {
"description": "The command's working directory.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "The command's working directory."
},
"durationMs": {
"description": "The duration of the command execution in milliseconds.",
@@ -1194,6 +1206,12 @@
"id": {
"type": "string"
},
"mcpAppResourceUri": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
@@ -1410,7 +1428,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -1443,9 +1461,13 @@
]
},
"savedPath": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"status": {

View File

@@ -1,6 +1,10 @@
{
"$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"
},
"AgentPath": {
"type": "string"
},
@@ -217,7 +221,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -794,8 +798,12 @@
"type": "integer"
},
"cwd": {
"description": "Working directory captured for the thread.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "Working directory captured for the thread."
},
"ephemeral": {
"description": "Whether the thread is ephemeral and should not be materialized on disk.",
@@ -1080,8 +1088,12 @@
"type": "array"
},
"cwd": {
"description": "The command's working directory.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "The command's working directory."
},
"durationMs": {
"description": "The duration of the command execution in milliseconds.",
@@ -1194,6 +1206,12 @@
"id": {
"type": "string"
},
"mcpAppResourceUri": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
@@ -1410,7 +1428,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -1443,9 +1461,13 @@
]
},
"savedPath": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"status": {

View File

@@ -2,10 +2,11 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "EXPERIMENTAL - flat transcript delta emitted whenever realtime transcript text changes.",
"properties": {
"role": {
"delta": {
"description": "Live transcript delta from the realtime event.",
"type": "string"
},
"text": {
"role": {
"type": "string"
},
"threadId": {
@@ -13,10 +14,10 @@
}
},
"required": [
"delta",
"role",
"text",
"threadId"
],
"title": "ThreadRealtimeTranscriptUpdatedNotification",
"title": "ThreadRealtimeTranscriptDeltaNotification",
"type": "object"
}

View File

@@ -0,0 +1,23 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "EXPERIMENTAL - final transcript text emitted when realtime completes a transcript part.",
"properties": {
"role": {
"type": "string"
},
"text": {
"description": "Final complete text for the transcript part.",
"type": "string"
},
"threadId": {
"type": "string"
}
},
"required": [
"role",
"text",
"threadId"
],
"title": "ThreadRealtimeTranscriptDoneNotification",
"type": "object"
}

View File

@@ -279,7 +279,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -1036,8 +1036,12 @@
"type": "integer"
},
"cwd": {
"description": "Working directory captured for the thread.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "Working directory captured for the thread."
},
"ephemeral": {
"description": "Whether the thread is ephemeral and should not be materialized on disk.",
@@ -1322,8 +1326,12 @@
"type": "array"
},
"cwd": {
"description": "The command's working directory.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "The command's working directory."
},
"durationMs": {
"description": "The duration of the command execution in milliseconds.",
@@ -1436,6 +1444,12 @@
"id": {
"type": "string"
},
"mcpAppResourceUri": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
@@ -1652,7 +1666,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -1685,9 +1699,13 @@
]
},
"savedPath": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"status": {
@@ -2185,13 +2203,13 @@
"description": "Reviewer currently used for approval requests on this thread."
},
"cwd": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"instructionSources": {
"default": [],
"description": "Instruction source files currently loaded for this thread.",
"items": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": "array"
},

View File

@@ -1,6 +1,10 @@
{
"$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"
},
"AgentPath": {
"type": "string"
},
@@ -217,7 +221,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -794,8 +798,12 @@
"type": "integer"
},
"cwd": {
"description": "Working directory captured for the thread.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "Working directory captured for the thread."
},
"ephemeral": {
"description": "Whether the thread is ephemeral and should not be materialized on disk.",
@@ -1080,8 +1088,12 @@
"type": "array"
},
"cwd": {
"description": "The command's working directory.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "The command's working directory."
},
"durationMs": {
"description": "The duration of the command execution in milliseconds.",
@@ -1194,6 +1206,12 @@
"id": {
"type": "string"
},
"mcpAppResourceUri": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
@@ -1410,7 +1428,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -1443,9 +1461,13 @@
]
},
"savedPath": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"status": {

View File

@@ -279,7 +279,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -1036,8 +1036,12 @@
"type": "integer"
},
"cwd": {
"description": "Working directory captured for the thread.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "Working directory captured for the thread."
},
"ephemeral": {
"description": "Whether the thread is ephemeral and should not be materialized on disk.",
@@ -1322,8 +1326,12 @@
"type": "array"
},
"cwd": {
"description": "The command's working directory.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "The command's working directory."
},
"durationMs": {
"description": "The duration of the command execution in milliseconds.",
@@ -1436,6 +1444,12 @@
"id": {
"type": "string"
},
"mcpAppResourceUri": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
@@ -1652,7 +1666,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -1685,9 +1699,13 @@
]
},
"savedPath": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"status": {
@@ -2185,13 +2203,13 @@
"description": "Reviewer currently used for approval requests on this thread."
},
"cwd": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"instructionSources": {
"default": [],
"description": "Instruction source files currently loaded for this thread.",
"items": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": "array"
},

View File

@@ -1,6 +1,10 @@
{
"$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"
},
"AgentPath": {
"type": "string"
},
@@ -217,7 +221,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -794,8 +798,12 @@
"type": "integer"
},
"cwd": {
"description": "Working directory captured for the thread.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "Working directory captured for the thread."
},
"ephemeral": {
"description": "Whether the thread is ephemeral and should not be materialized on disk.",
@@ -1080,8 +1088,12 @@
"type": "array"
},
"cwd": {
"description": "The command's working directory.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "The command's working directory."
},
"durationMs": {
"description": "The duration of the command execution in milliseconds.",
@@ -1194,6 +1206,12 @@
"id": {
"type": "string"
},
"mcpAppResourceUri": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
@@ -1410,7 +1428,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -1443,9 +1461,13 @@
]
},
"savedPath": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"status": {

View File

@@ -1,6 +1,10 @@
{
"$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"
},
"AgentPath": {
"type": "string"
},
@@ -217,7 +221,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -794,8 +798,12 @@
"type": "integer"
},
"cwd": {
"description": "Working directory captured for the thread.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "Working directory captured for the thread."
},
"ephemeral": {
"description": "Whether the thread is ephemeral and should not be materialized on disk.",
@@ -1080,8 +1088,12 @@
"type": "array"
},
"cwd": {
"description": "The command's working directory.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "The command's working directory."
},
"durationMs": {
"description": "The duration of the command execution in milliseconds.",
@@ -1194,6 +1206,12 @@
"id": {
"type": "string"
},
"mcpAppResourceUri": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
@@ -1410,7 +1428,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -1443,9 +1461,13 @@
]
},
"savedPath": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"status": {

View File

@@ -1,6 +1,10 @@
{
"$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"
},
"ByteRange": {
"properties": {
"end": {
@@ -214,7 +218,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -808,8 +812,12 @@
"type": "array"
},
"cwd": {
"description": "The command's working directory.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "The command's working directory."
},
"durationMs": {
"description": "The duration of the command execution in milliseconds.",
@@ -922,6 +930,12 @@
"id": {
"type": "string"
},
"mcpAppResourceUri": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
@@ -1138,7 +1152,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -1171,9 +1185,13 @@
]
},
"savedPath": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"status": {

View File

@@ -1,6 +1,10 @@
{
"$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"
},
"ByteRange": {
"properties": {
"end": {
@@ -214,7 +218,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -808,8 +812,12 @@
"type": "array"
},
"cwd": {
"description": "The command's working directory.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "The command's working directory."
},
"durationMs": {
"description": "The duration of the command execution in milliseconds.",
@@ -922,6 +930,12 @@
"id": {
"type": "string"
},
"mcpAppResourceUri": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
@@ -1138,7 +1152,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -1171,9 +1185,13 @@
]
},
"savedPath": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"status": {

View File

@@ -1,6 +1,10 @@
{
"$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"
},
"ByteRange": {
"properties": {
"end": {
@@ -214,7 +218,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -808,8 +812,12 @@
"type": "array"
},
"cwd": {
"description": "The command's working directory.",
"type": "string"
"allOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
}
],
"description": "The command's working directory."
},
"durationMs": {
"description": "The duration of the command execution in milliseconds.",
@@ -922,6 +930,12 @@
"id": {
"type": "string"
},
"mcpAppResourceUri": {
"type": [
"string",
"null"
]
},
"result": {
"anyOf": [
{
@@ -1138,7 +1152,7 @@
"type": "string"
},
"path": {
"type": "string"
"$ref": "#/definitions/AbsolutePathBuf"
},
"type": {
"enum": [
@@ -1171,9 +1185,13 @@
]
},
"savedPath": {
"type": [
"string",
"null"
"anyOf": [
{
"$ref": "#/definitions/AbsolutePathBuf"
},
{
"type": "null"
}
]
},
"status": {

File diff suppressed because one or more lines are too long

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 RealtimeOutputModality = "text" | "audio";

View File

@@ -43,7 +43,8 @@ import type { ThreadRealtimeItemAddedNotification } from "./v2/ThreadRealtimeIte
import type { ThreadRealtimeOutputAudioDeltaNotification } from "./v2/ThreadRealtimeOutputAudioDeltaNotification";
import type { ThreadRealtimeSdpNotification } from "./v2/ThreadRealtimeSdpNotification";
import type { ThreadRealtimeStartedNotification } from "./v2/ThreadRealtimeStartedNotification";
import type { ThreadRealtimeTranscriptUpdatedNotification } from "./v2/ThreadRealtimeTranscriptUpdatedNotification";
import type { ThreadRealtimeTranscriptDeltaNotification } from "./v2/ThreadRealtimeTranscriptDeltaNotification";
import type { ThreadRealtimeTranscriptDoneNotification } from "./v2/ThreadRealtimeTranscriptDoneNotification";
import type { ThreadStartedNotification } from "./v2/ThreadStartedNotification";
import type { ThreadStatusChangedNotification } from "./v2/ThreadStatusChangedNotification";
import type { ThreadTokenUsageUpdatedNotification } from "./v2/ThreadTokenUsageUpdatedNotification";
@@ -58,4 +59,4 @@ import type { WindowsWorldWritableWarningNotification } from "./v2/WindowsWorldW
/**
* Notification sent from the server to the client.
*/
export type ServerNotification = { "method": "error", "params": ErrorNotification } | { "method": "thread/started", "params": ThreadStartedNotification } | { "method": "thread/status/changed", "params": ThreadStatusChangedNotification } | { "method": "thread/archived", "params": ThreadArchivedNotification } | { "method": "thread/unarchived", "params": ThreadUnarchivedNotification } | { "method": "thread/closed", "params": ThreadClosedNotification } | { "method": "skills/changed", "params": SkillsChangedNotification } | { "method": "thread/name/updated", "params": ThreadNameUpdatedNotification } | { "method": "thread/tokenUsage/updated", "params": ThreadTokenUsageUpdatedNotification } | { "method": "turn/started", "params": TurnStartedNotification } | { "method": "hook/started", "params": HookStartedNotification } | { "method": "turn/completed", "params": TurnCompletedNotification } | { "method": "hook/completed", "params": HookCompletedNotification } | { "method": "turn/diff/updated", "params": TurnDiffUpdatedNotification } | { "method": "turn/plan/updated", "params": TurnPlanUpdatedNotification } | { "method": "item/started", "params": ItemStartedNotification } | { "method": "item/autoApprovalReview/started", "params": ItemGuardianApprovalReviewStartedNotification } | { "method": "item/autoApprovalReview/completed", "params": ItemGuardianApprovalReviewCompletedNotification } | { "method": "item/completed", "params": ItemCompletedNotification } | { "method": "rawResponseItem/completed", "params": RawResponseItemCompletedNotification } | { "method": "item/agentMessage/delta", "params": AgentMessageDeltaNotification } | { "method": "item/plan/delta", "params": PlanDeltaNotification } | { "method": "command/exec/outputDelta", "params": CommandExecOutputDeltaNotification } | { "method": "item/commandExecution/outputDelta", "params": CommandExecutionOutputDeltaNotification } | { "method": "item/commandExecution/terminalInteraction", "params": TerminalInteractionNotification } | { "method": "item/fileChange/outputDelta", "params": FileChangeOutputDeltaNotification } | { "method": "serverRequest/resolved", "params": ServerRequestResolvedNotification } | { "method": "item/mcpToolCall/progress", "params": McpToolCallProgressNotification } | { "method": "mcpServer/oauthLogin/completed", "params": McpServerOauthLoginCompletedNotification } | { "method": "mcpServer/startupStatus/updated", "params": McpServerStatusUpdatedNotification } | { "method": "account/updated", "params": AccountUpdatedNotification } | { "method": "account/rateLimits/updated", "params": AccountRateLimitsUpdatedNotification } | { "method": "app/list/updated", "params": AppListUpdatedNotification } | { "method": "fs/changed", "params": FsChangedNotification } | { "method": "item/reasoning/summaryTextDelta", "params": ReasoningSummaryTextDeltaNotification } | { "method": "item/reasoning/summaryPartAdded", "params": ReasoningSummaryPartAddedNotification } | { "method": "item/reasoning/textDelta", "params": ReasoningTextDeltaNotification } | { "method": "thread/compacted", "params": ContextCompactedNotification } | { "method": "model/rerouted", "params": ModelReroutedNotification } | { "method": "deprecationNotice", "params": DeprecationNoticeNotification } | { "method": "configWarning", "params": ConfigWarningNotification } | { "method": "fuzzyFileSearch/sessionUpdated", "params": FuzzyFileSearchSessionUpdatedNotification } | { "method": "fuzzyFileSearch/sessionCompleted", "params": FuzzyFileSearchSessionCompletedNotification } | { "method": "thread/realtime/started", "params": ThreadRealtimeStartedNotification } | { "method": "thread/realtime/itemAdded", "params": ThreadRealtimeItemAddedNotification } | { "method": "thread/realtime/transcriptUpdated", "params": ThreadRealtimeTranscriptUpdatedNotification } | { "method": "thread/realtime/outputAudio/delta", "params": ThreadRealtimeOutputAudioDeltaNotification } | { "method": "thread/realtime/sdp", "params": ThreadRealtimeSdpNotification } | { "method": "thread/realtime/error", "params": ThreadRealtimeErrorNotification } | { "method": "thread/realtime/closed", "params": ThreadRealtimeClosedNotification } | { "method": "windows/worldWritableWarning", "params": WindowsWorldWritableWarningNotification } | { "method": "windowsSandbox/setupCompleted", "params": WindowsSandboxSetupCompletedNotification } | { "method": "account/login/completed", "params": AccountLoginCompletedNotification };
export type ServerNotification = { "method": "error", "params": ErrorNotification } | { "method": "thread/started", "params": ThreadStartedNotification } | { "method": "thread/status/changed", "params": ThreadStatusChangedNotification } | { "method": "thread/archived", "params": ThreadArchivedNotification } | { "method": "thread/unarchived", "params": ThreadUnarchivedNotification } | { "method": "thread/closed", "params": ThreadClosedNotification } | { "method": "skills/changed", "params": SkillsChangedNotification } | { "method": "thread/name/updated", "params": ThreadNameUpdatedNotification } | { "method": "thread/tokenUsage/updated", "params": ThreadTokenUsageUpdatedNotification } | { "method": "turn/started", "params": TurnStartedNotification } | { "method": "hook/started", "params": HookStartedNotification } | { "method": "turn/completed", "params": TurnCompletedNotification } | { "method": "hook/completed", "params": HookCompletedNotification } | { "method": "turn/diff/updated", "params": TurnDiffUpdatedNotification } | { "method": "turn/plan/updated", "params": TurnPlanUpdatedNotification } | { "method": "item/started", "params": ItemStartedNotification } | { "method": "item/autoApprovalReview/started", "params": ItemGuardianApprovalReviewStartedNotification } | { "method": "item/autoApprovalReview/completed", "params": ItemGuardianApprovalReviewCompletedNotification } | { "method": "item/completed", "params": ItemCompletedNotification } | { "method": "rawResponseItem/completed", "params": RawResponseItemCompletedNotification } | { "method": "item/agentMessage/delta", "params": AgentMessageDeltaNotification } | { "method": "item/plan/delta", "params": PlanDeltaNotification } | { "method": "command/exec/outputDelta", "params": CommandExecOutputDeltaNotification } | { "method": "item/commandExecution/outputDelta", "params": CommandExecutionOutputDeltaNotification } | { "method": "item/commandExecution/terminalInteraction", "params": TerminalInteractionNotification } | { "method": "item/fileChange/outputDelta", "params": FileChangeOutputDeltaNotification } | { "method": "serverRequest/resolved", "params": ServerRequestResolvedNotification } | { "method": "item/mcpToolCall/progress", "params": McpToolCallProgressNotification } | { "method": "mcpServer/oauthLogin/completed", "params": McpServerOauthLoginCompletedNotification } | { "method": "mcpServer/startupStatus/updated", "params": McpServerStatusUpdatedNotification } | { "method": "account/updated", "params": AccountUpdatedNotification } | { "method": "account/rateLimits/updated", "params": AccountRateLimitsUpdatedNotification } | { "method": "app/list/updated", "params": AppListUpdatedNotification } | { "method": "fs/changed", "params": FsChangedNotification } | { "method": "item/reasoning/summaryTextDelta", "params": ReasoningSummaryTextDeltaNotification } | { "method": "item/reasoning/summaryPartAdded", "params": ReasoningSummaryPartAddedNotification } | { "method": "item/reasoning/textDelta", "params": ReasoningTextDeltaNotification } | { "method": "thread/compacted", "params": ContextCompactedNotification } | { "method": "model/rerouted", "params": ModelReroutedNotification } | { "method": "deprecationNotice", "params": DeprecationNoticeNotification } | { "method": "configWarning", "params": ConfigWarningNotification } | { "method": "fuzzyFileSearch/sessionUpdated", "params": FuzzyFileSearchSessionUpdatedNotification } | { "method": "fuzzyFileSearch/sessionCompleted", "params": FuzzyFileSearchSessionCompletedNotification } | { "method": "thread/realtime/started", "params": ThreadRealtimeStartedNotification } | { "method": "thread/realtime/itemAdded", "params": ThreadRealtimeItemAddedNotification } | { "method": "thread/realtime/transcript/delta", "params": ThreadRealtimeTranscriptDeltaNotification } | { "method": "thread/realtime/transcript/done", "params": ThreadRealtimeTranscriptDoneNotification } | { "method": "thread/realtime/outputAudio/delta", "params": ThreadRealtimeOutputAudioDeltaNotification } | { "method": "thread/realtime/sdp", "params": ThreadRealtimeSdpNotification } | { "method": "thread/realtime/error", "params": ThreadRealtimeErrorNotification } | { "method": "thread/realtime/closed", "params": ThreadRealtimeClosedNotification } | { "method": "windows/worldWritableWarning", "params": WindowsWorldWritableWarningNotification } | { "method": "windowsSandbox/setupCompleted", "params": WindowsSandboxSetupCompletedNotification } | { "method": "account/login/completed", "params": AccountLoginCompletedNotification };

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 ThreadMemoryMode = "enabled" | "disabled";

View File

@@ -49,6 +49,7 @@ export type { ParsedCommand } from "./ParsedCommand";
export type { Personality } from "./Personality";
export type { PlanType } from "./PlanType";
export type { RealtimeConversationVersion } from "./RealtimeConversationVersion";
export type { RealtimeOutputModality } from "./RealtimeOutputModality";
export type { RealtimeVoice } from "./RealtimeVoice";
export type { RealtimeVoicesList } from "./RealtimeVoicesList";
export type { ReasoningEffort } from "./ReasoningEffort";
@@ -68,6 +69,7 @@ export type { SessionSource } from "./SessionSource";
export type { Settings } from "./Settings";
export type { SubAgentSource } from "./SubAgentSource";
export type { ThreadId } from "./ThreadId";
export type { ThreadMemoryMode } from "./ThreadMemoryMode";
export type { Tool } from "./Tool";
export type { Verbosity } from "./Verbosity";
export type { WebSearchAction } from "./WebSearchAction";

View File

@@ -1,5 +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 { AbsolutePathBuf } from "../AbsolutePathBuf";
export type CommandAction = { "type": "read", command: string, name: string, path: string, } | { "type": "listFiles", command: string, path: string | null, } | { "type": "search", command: string, query: string | null, path: string | null, } | { "type": "unknown", command: string, };
export type CommandAction = { "type": "read", command: string, name: string, path: AbsolutePathBuf, } | { "type": "listFiles", command: string, path: string | null, } | { "type": "search", command: string, query: string | null, path: string | null, } | { "type": "unknown", command: string, };

View File

@@ -1,6 +1,7 @@
// 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 { AbsolutePathBuf } from "../AbsolutePathBuf";
import type { AdditionalPermissionProfile } from "./AdditionalPermissionProfile";
import type { CommandAction } from "./CommandAction";
import type { CommandExecutionApprovalDecision } from "./CommandExecutionApprovalDecision";
@@ -34,7 +35,7 @@ command?: string | null,
/**
* The command's working directory.
*/
cwd?: string | null,
cwd?: AbsolutePathBuf | null,
/**
* Best-effort parsed command actions for friendly display.
*/

View File

@@ -2,9 +2,10 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { ExternalAgentConfigMigrationItemType } from "./ExternalAgentConfigMigrationItemType";
import type { MigrationDetails } from "./MigrationDetails";
export type ExternalAgentConfigMigrationItem = { itemType: ExternalAgentConfigMigrationItemType, description: string,
/**
* Null or empty means home-scoped migration; non-empty means repo-scoped migration.
*/
cwd: string | null, };
cwd: string | null, details: MigrationDetails | 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 ExternalAgentConfigMigrationItemType = "AGENTS_MD" | "CONFIG" | "SKILLS" | "MCP_SERVER_CONFIG";
export type ExternalAgentConfigMigrationItemType = "AGENTS_MD" | "CONFIG" | "SKILLS" | "PLUGINS" | "MCP_SERVER_CONFIG";

View File

@@ -7,13 +7,17 @@
*/
export type FsGetMetadataResponse = {
/**
* Whether the path currently resolves to a directory.
* Whether the path resolves to a directory.
*/
isDirectory: boolean,
/**
* Whether the path currently resolves to a regular file.
* Whether the path resolves to a regular file.
*/
isFile: boolean,
/**
* Whether the path itself is a symbolic link.
*/
isSymlink: boolean,
/**
* File creation time in Unix milliseconds when available, otherwise `0`.
*/

View File

@@ -1,7 +1,8 @@
// 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 { AbsolutePathBuf } from "../AbsolutePathBuf";
import type { GuardianCommandSource } from "./GuardianCommandSource";
import type { NetworkApprovalProtocol } from "./NetworkApprovalProtocol";
export type GuardianApprovalReviewAction = { "type": "command", source: GuardianCommandSource, command: string, cwd: string, } | { "type": "execve", source: GuardianCommandSource, program: string, argv: Array<string>, cwd: string, } | { "type": "applyPatch", cwd: string, files: Array<string>, } | { "type": "networkAccess", target: string, host: string, protocol: NetworkApprovalProtocol, port: number, } | { "type": "mcpToolCall", server: string, toolName: string, connectorId: string | null, connectorName: string | null, toolTitle: string | null, };
export type GuardianApprovalReviewAction = { "type": "command", source: GuardianCommandSource, command: string, cwd: AbsolutePathBuf, } | { "type": "execve", source: GuardianCommandSource, program: string, argv: Array<string>, cwd: AbsolutePathBuf, } | { "type": "applyPatch", cwd: AbsolutePathBuf, files: Array<AbsolutePathBuf>, } | { "type": "networkAccess", target: string, host: string, protocol: NetworkApprovalProtocol, port: number, } | { "type": "mcpToolCall", server: string, toolName: string, connectorId: string | null, connectorName: string | null, toolTitle: string | null, };

View File

@@ -1,6 +1,7 @@
// 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 { AbsolutePathBuf } from "../AbsolutePathBuf";
import type { HookEventName } from "./HookEventName";
import type { HookExecutionMode } from "./HookExecutionMode";
import type { HookHandlerType } from "./HookHandlerType";
@@ -8,4 +9,4 @@ import type { HookOutputEntry } from "./HookOutputEntry";
import type { HookRunStatus } from "./HookRunStatus";
import type { HookScope } from "./HookScope";
export type HookRunSummary = { id: string, eventName: HookEventName, handlerType: HookHandlerType, executionMode: HookExecutionMode, scope: HookScope, sourcePath: string, displayOrder: bigint, status: HookRunStatus, statusMessage: string | null, startedAt: bigint, completedAt: bigint | null, durationMs: bigint | null, entries: Array<HookOutputEntry>, };
export type HookRunSummary = { id: string, eventName: HookEventName, handlerType: HookHandlerType, executionMode: HookExecutionMode, scope: HookScope, sourcePath: AbsolutePathBuf, displayOrder: bigint, status: HookRunStatus, statusMessage: string | null, startedAt: bigint, completedAt: bigint | null, durationMs: bigint | null, entries: Array<HookOutputEntry>, };

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 MarketplaceAddParams = { source: string, refName?: string | null, sparsePaths?: Array<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 { AbsolutePathBuf } from "../AbsolutePathBuf";
export type MarketplaceAddResponse = { marketplaceName: string, installedRoot: AbsolutePathBuf, alreadyAdded: boolean, };

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 { PluginsMigration } from "./PluginsMigration";
export type MigrationDetails = { plugins: Array<PluginsMigration>, };

View File

@@ -29,4 +29,4 @@ unixSockets: { [key in string]?: NetworkUnixSocketPermission } | null,
/**
* Legacy compatibility view derived from `unix_sockets`.
*/
allowUnixSockets: Array<string> | null, allowLocalBinding: boolean | null, dangerFullAccessDenylistOnly: boolean | null, };
allowUnixSockets: Array<string> | null, allowLocalBinding: boolean | null, };

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 PluginsMigration = { marketplaceName: string, pluginNames: Array<string>, };

View File

@@ -1,5 +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 { AbsolutePathBuf } from "../AbsolutePathBuf";
export type SkillInterface = { displayName?: string, shortDescription?: string, iconSmall?: string, iconLarge?: string, brandColor?: string, defaultPrompt?: string, };
export type SkillInterface = { displayName?: string, shortDescription?: string, iconSmall?: AbsolutePathBuf, iconLarge?: AbsolutePathBuf, brandColor?: string, defaultPrompt?: string, };

View File

@@ -1,6 +1,7 @@
// 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 { AbsolutePathBuf } from "../AbsolutePathBuf";
import type { SkillDependencies } from "./SkillDependencies";
import type { SkillInterface } from "./SkillInterface";
import type { SkillScope } from "./SkillScope";
@@ -9,4 +10,4 @@ export type SkillMetadata = { name: string, description: string,
/**
* Legacy short_description from SKILL.md. Prefer SKILL.json interface.short_description.
*/
shortDescription?: string, interface?: SkillInterface, dependencies?: SkillDependencies, path: string, scope: SkillScope, enabled: boolean, };
shortDescription?: string, interface?: SkillInterface, dependencies?: SkillDependencies, path: AbsolutePathBuf, scope: SkillScope, enabled: boolean, };

View File

@@ -1,6 +1,7 @@
// 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 { AbsolutePathBuf } from "../AbsolutePathBuf";
import type { SkillInterface } from "./SkillInterface";
export type SkillSummary = { name: string, description: string, shortDescription: string | null, interface: SkillInterface | null, path: string, enabled: boolean, };
export type SkillSummary = { name: string, description: string, shortDescription: string | null, interface: SkillInterface | null, path: AbsolutePathBuf, enabled: boolean, };

View File

@@ -1,6 +1,7 @@
// 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 { AbsolutePathBuf } from "../AbsolutePathBuf";
import type { GitInfo } from "./GitInfo";
import type { SessionSource } from "./SessionSource";
import type { ThreadStatus } from "./ThreadStatus";
@@ -42,7 +43,7 @@ path: string | null,
/**
* Working directory captured for the thread.
*/
cwd: string,
cwd: AbsolutePathBuf,
/**
* Version of the CLI that created the thread.
*/

View File

@@ -1,6 +1,7 @@
// 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 { AbsolutePathBuf } from "../AbsolutePathBuf";
import type { ReasoningEffort } from "../ReasoningEffort";
import type { ServiceTier } from "../ServiceTier";
import type { ApprovalsReviewer } from "./ApprovalsReviewer";
@@ -8,11 +9,11 @@ import type { AskForApproval } from "./AskForApproval";
import type { SandboxPolicy } from "./SandboxPolicy";
import type { Thread } from "./Thread";
export type ThreadForkResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: ServiceTier | null, cwd: string,
export type ThreadForkResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: ServiceTier | null, cwd: AbsolutePathBuf,
/**
* Instruction source files currently loaded for this thread.
*/
instructionSources: Array<string>, approvalPolicy: AskForApproval,
instructionSources: Array<AbsolutePathBuf>, approvalPolicy: AskForApproval,
/**
* Reviewer currently used for approval requests on this thread.
*/

View File

@@ -0,0 +1,10 @@
// 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 { JsonValue } from "../serde_json/JsonValue";
export type ThreadInjectItemsParams = { threadId: string,
/**
* Raw Responses API items to append to the thread's model-visible history.
*/
items: Array<JsonValue>, };

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 ThreadInjectItemsResponse = Record<string, never>;

View File

@@ -1,6 +1,7 @@
// 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 { AbsolutePathBuf } from "../AbsolutePathBuf";
import type { MessagePhase } from "../MessagePhase";
import type { ReasoningEffort } from "../ReasoningEffort";
import type { JsonValue } from "../serde_json/JsonValue";
@@ -30,7 +31,7 @@ command: string,
/**
* The command's working directory.
*/
cwd: string,
cwd: AbsolutePathBuf,
/**
* Identifier for the underlying PTY process (when available).
*/
@@ -52,7 +53,7 @@ exitCode: number | null,
/**
* The duration of the command execution in milliseconds.
*/
durationMs: number | null, } | { "type": "fileChange", id: string, changes: Array<FileUpdateChange>, status: PatchApplyStatus, } | { "type": "mcpToolCall", id: string, server: string, tool: string, status: McpToolCallStatus, arguments: JsonValue, result: McpToolCallResult | null, error: McpToolCallError | null,
durationMs: number | null, } | { "type": "fileChange", id: string, changes: Array<FileUpdateChange>, status: PatchApplyStatus, } | { "type": "mcpToolCall", id: string, server: string, tool: string, status: McpToolCallStatus, arguments: JsonValue, mcpAppResourceUri?: string, result: McpToolCallResult | null, error: McpToolCallError | null,
/**
* The duration of the MCP tool call in milliseconds.
*/
@@ -97,4 +98,4 @@ reasoningEffort: ReasoningEffort | null,
/**
* Last known status of the target agents, when available.
*/
agentsStates: { [key in string]?: CollabAgentState }, } | { "type": "webSearch", id: string, query: string, action: WebSearchAction | null, } | { "type": "imageView", id: string, path: string, } | { "type": "imageGeneration", id: string, status: string, revisedPrompt: string | null, result: string, savedPath?: string, } | { "type": "enteredReviewMode", id: string, review: string, } | { "type": "exitedReviewMode", id: string, review: string, } | { "type": "contextCompaction", id: string, };
agentsStates: { [key in string]?: CollabAgentState }, } | { "type": "webSearch", id: string, query: string, action: WebSearchAction | null, } | { "type": "imageView", id: string, path: AbsolutePathBuf, } | { "type": "imageGeneration", id: string, status: string, revisedPrompt: string | null, result: string, savedPath?: AbsolutePathBuf, } | { "type": "enteredReviewMode", id: string, review: string, } | { "type": "exitedReviewMode", id: string, review: string, } | { "type": "contextCompaction", id: string, };

View File

@@ -6,4 +6,8 @@
* EXPERIMENTAL - flat transcript delta emitted whenever realtime
* transcript text changes.
*/
export type ThreadRealtimeTranscriptUpdatedNotification = { threadId: string, role: string, text: string, };
export type ThreadRealtimeTranscriptDeltaNotification = { threadId: string, role: string,
/**
* Live transcript delta from the realtime event.
*/
delta: string, };

View File

@@ -0,0 +1,13 @@
// 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.
/**
* EXPERIMENTAL - final transcript text emitted when realtime completes
* a transcript part.
*/
export type ThreadRealtimeTranscriptDoneNotification = { threadId: string, role: string,
/**
* Final complete text for the transcript part.
*/
text: string, };

View File

@@ -1,6 +1,7 @@
// 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 { AbsolutePathBuf } from "../AbsolutePathBuf";
import type { ReasoningEffort } from "../ReasoningEffort";
import type { ServiceTier } from "../ServiceTier";
import type { ApprovalsReviewer } from "./ApprovalsReviewer";
@@ -8,11 +9,11 @@ import type { AskForApproval } from "./AskForApproval";
import type { SandboxPolicy } from "./SandboxPolicy";
import type { Thread } from "./Thread";
export type ThreadResumeResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: ServiceTier | null, cwd: string,
export type ThreadResumeResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: ServiceTier | null, cwd: AbsolutePathBuf,
/**
* Instruction source files currently loaded for this thread.
*/
instructionSources: Array<string>, approvalPolicy: AskForApproval,
instructionSources: Array<AbsolutePathBuf>, approvalPolicy: AskForApproval,
/**
* Reviewer currently used for approval requests on this thread.
*/

View File

@@ -1,6 +1,7 @@
// 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 { AbsolutePathBuf } from "../AbsolutePathBuf";
import type { ReasoningEffort } from "../ReasoningEffort";
import type { ServiceTier } from "../ServiceTier";
import type { ApprovalsReviewer } from "./ApprovalsReviewer";
@@ -8,11 +9,11 @@ import type { AskForApproval } from "./AskForApproval";
import type { SandboxPolicy } from "./SandboxPolicy";
import type { Thread } from "./Thread";
export type ThreadStartResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: ServiceTier | null, cwd: string,
export type ThreadStartResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: ServiceTier | null, cwd: AbsolutePathBuf,
/**
* Instruction source files currently loaded for this thread.
*/
instructionSources: Array<string>, approvalPolicy: AskForApproval,
instructionSources: Array<AbsolutePathBuf>, approvalPolicy: AskForApproval,
/**
* Reviewer currently used for approval requests on this thread.
*/

View File

@@ -149,6 +149,8 @@ export type { ListMcpServerStatusResponse } from "./ListMcpServerStatusResponse"
export type { LoginAccountParams } from "./LoginAccountParams";
export type { LoginAccountResponse } from "./LoginAccountResponse";
export type { LogoutAccountResponse } from "./LogoutAccountResponse";
export type { MarketplaceAddParams } from "./MarketplaceAddParams";
export type { MarketplaceAddResponse } from "./MarketplaceAddResponse";
export type { MarketplaceInterface } from "./MarketplaceInterface";
export type { MarketplaceLoadErrorInfo } from "./MarketplaceLoadErrorInfo";
export type { McpAuthStatus } from "./McpAuthStatus";
@@ -196,6 +198,7 @@ export type { McpToolCallStatus } from "./McpToolCallStatus";
export type { MemoryCitation } from "./MemoryCitation";
export type { MemoryCitationEntry } from "./MemoryCitationEntry";
export type { MergeStrategy } from "./MergeStrategy";
export type { MigrationDetails } from "./MigrationDetails";
export type { Model } from "./Model";
export type { ModelAvailabilityNux } from "./ModelAvailabilityNux";
export type { ModelListParams } from "./ModelListParams";
@@ -234,6 +237,7 @@ export type { PluginSource } from "./PluginSource";
export type { PluginSummary } from "./PluginSummary";
export type { PluginUninstallParams } from "./PluginUninstallParams";
export type { PluginUninstallResponse } from "./PluginUninstallResponse";
export type { PluginsMigration } from "./PluginsMigration";
export type { ProfileV2 } from "./ProfileV2";
export type { RateLimitSnapshot } from "./RateLimitSnapshot";
export type { RateLimitWindow } from "./RateLimitWindow";
@@ -282,6 +286,8 @@ export type { ThreadCompactStartParams } from "./ThreadCompactStartParams";
export type { ThreadCompactStartResponse } from "./ThreadCompactStartResponse";
export type { ThreadForkParams } from "./ThreadForkParams";
export type { ThreadForkResponse } from "./ThreadForkResponse";
export type { ThreadInjectItemsParams } from "./ThreadInjectItemsParams";
export type { ThreadInjectItemsResponse } from "./ThreadInjectItemsResponse";
export type { ThreadItem } from "./ThreadItem";
export type { ThreadListParams } from "./ThreadListParams";
export type { ThreadListResponse } from "./ThreadListResponse";
@@ -301,7 +307,8 @@ export type { ThreadRealtimeOutputAudioDeltaNotification } from "./ThreadRealtim
export type { ThreadRealtimeSdpNotification } from "./ThreadRealtimeSdpNotification";
export type { ThreadRealtimeStartTransport } from "./ThreadRealtimeStartTransport";
export type { ThreadRealtimeStartedNotification } from "./ThreadRealtimeStartedNotification";
export type { ThreadRealtimeTranscriptUpdatedNotification } from "./ThreadRealtimeTranscriptUpdatedNotification";
export type { ThreadRealtimeTranscriptDeltaNotification } from "./ThreadRealtimeTranscriptDeltaNotification";
export type { ThreadRealtimeTranscriptDoneNotification } from "./ThreadRealtimeTranscriptDoneNotification";
export type { ThreadResumeParams } from "./ThreadResumeParams";
export type { ThreadResumeResponse } from "./ThreadResumeResponse";
export type { ThreadRollbackParams } from "./ThreadRollbackParams";

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