mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
657841e7f5054b1b12d245b0d3b8c3e2e5f533fd
27
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
657841e7f5 |
Persist initialized js_repl bindings after failed cells (#13482)
## Summary
- Change `js_repl` failed-cell persistence so later cells keep prior
bindings plus only the current-cell bindings whose initialization
definitely completed before the throw.
- Preserve initialized lexical bindings across failed cells via
module-namespace readability, including top-level destructuring that
partially succeeds before a later throw.
- Preserve hoisted `var` and `function` bindings only when execution
clearly reached their declaration site, and preserve direct top-level
pre-declaration `var` writes and updates through explicit write-site
markers.
- Preserve top-level `for...in` / `for...of` `var` bindings when the
loop body executes at least once, using a first-iteration guard to avoid
per-iteration bookkeeping overhead.
- Keep prior module state intact across link-time failures and
evaluation failures before the prelude runs, while still allowing failed
cells that already recreated prior bindings to persist updates to those
existing bindings.
- Hide internal commit hooks from user `js_repl` code after the prelude
aliases them, so snippets cannot spoof committed bindings by calling the
raw `import.meta` hooks directly.
- Add focused regression coverage for the supported failed-cell
behaviors and the intentionally unsupported boundaries.
- Update `js_repl` docs and generated instructions to describe the new,
narrower failed-cell persistence model.
## Motivation
We saw `js_repl` drop bindings that had already been initialized
successfully when a later statement in the same cell threw, for example:
const { context: liveContext, session } =
await initializeGoogleSheetsLiveForTab(tab);
// later statement throws
That was surprising in practice because successful earlier work
disappeared from the next cell.
This change makes failed-cell persistence more useful without trying to
model every possible partially executed JavaScript edge case. The
resulting behavior is narrower and easier to reason about:
- prior bindings are always preserved
- lexical bindings persist when their initialization completed before
the throw
- hoisted `var` / `function` bindings persist only when execution
clearly reached their declaration or a supported top-level `var` write
site
- failed cells that already recreated prior bindings can persist writes
to those existing bindings even if they introduce no new bindings
The detailed edge-case matrix stays in `docs/js_repl.md`. The
model-facing `project_doc` guidance is intentionally shorter and focused
on generation-relevant behavior.
## Supported Failed-Cell Behavior
- Prior bindings remain available after a failed cell.
- Initialized lexical bindings remain available after a failed cell.
- Top-level destructuring like `const { a, b } = ...` preserves names
whose initialization completed before a later throw.
- Hoisted `function` bindings persist when execution reached the
declaration statement before the throw.
- Direct top-level pre-declaration `var` writes and updates persist, for
example:
- `x = 1`
- `x += 1`
- `x++`
- short-circuiting logical assignments only persist when the write
branch actually runs
- Non-empty top-level `for...in` / `for...of` `var` loops persist their
loop bindings.
- Failed cells can persist updates to existing carried bindings after
the prelude has run, even when the cell commits no new bindings.
- Link failures and eval failures before the prelude do not poison
`@prev`.
## Intentionally Unsupported Failed-Cell Cases
- Hoisted function reads before the declaration, such as `foo(); ...;
function foo() {}`
- Aliasing or inference-based recovery from reads before declaration
- Nested writes inside already-instrumented assignment RHS expressions
- Destructuring-assignment recovery for hoisted `var`
- Partial `var` destructuring recovery
- Pre-declaration `undefined` reads for hoisted `var`
- Empty top-level `for...in` / `for...of` loop vars
- Nested or scope-sensitive pre-declaration `var` writes outside direct
top-level expression statements
|
||
|
|
ff0341dc94 |
[js_repl] Support local ESM file imports (#13437)
## Summary
- add `js_repl` support for dynamic imports of relative and absolute
local ESM `.js` / `.mjs` files
- keep bare package imports on the native Node path and resolved from
REPL-global search roots (`CODEX_JS_REPL_NODE_MODULE_DIRS`, then `cwd`),
even when they originate from imported local files
- restrict static imports inside imported local files to other local
relative/absolute `.js` / `.mjs` files, and surface a clear error for
unsupported top-level static imports in the REPL cell
- run imported local files inside the REPL VM context so they can access
`codex.tmpDir`, `codex.tool`, captured `console`, and Node-like
`import.meta` helpers
- reload local files between execs so later `await import("./file.js")`
calls pick up edits and fixed failures, while preserving package/builtin
caching and persistent top-level REPL bindings
- make `import.meta.resolve()` self-consistent by allowing the returned
`file://...` URLs to round-trip through `await import(...)`
- update both public and injected `js_repl` docs to clarify the narrowed
contract, including global bare-import resolution behavior for local
absolute files
## Testing
- `cargo test -p codex-core js_repl_`
- built codex binary and verified behavior
---------
Co-authored-by: Codex <noreply@openai.com>
|
||
|
|
bfff0c729f |
config: enforce enterprise feature requirements (#13388)
## Why Enterprises can already constrain approvals, sandboxing, and web search through `requirements.toml` and MDM, but feature flags were still only configurable as managed defaults. That meant an enterprise could suggest feature values, but it could not actually pin them. This change closes that gap and makes enterprise feature requirements behave like the other constrained settings. The effective feature set now stays consistent with enterprise requirements during config load, when config writes are validated, and when runtime code mutates feature flags later in the session. It also tightens the runtime API for managed features. `ManagedFeatures` now follows the same constraint-oriented shape as `Constrained<T>` instead of exposing panic-prone mutation helpers, and production code can no longer construct it through an unconstrained `From<Features>` path. The PR also hardens the `compact_resume_fork` integration coverage on Windows. After the feature-management changes, `compact_resume_after_second_compaction_preserves_history` was overflowing the libtest/Tokio thread stacks on Windows, so the test now uses an explicit larger-stack harness as a pragmatic mitigation. That may not be the ideal root-cause fix, and it merits a parallel investigation into whether part of the async future chain should be boxed to reduce stack pressure instead. ## What Changed Enterprises can now pin feature values in `requirements.toml` with the requirements-side `features` table: ```toml [features] personality = true unified_exec = false ``` Only canonical feature keys are allowed in the requirements `features` table; omitted keys remain unconstrained. - Added a requirements-side pinned feature map to `ConfigRequirementsToml`, threaded it through source-preserving requirements merge and normalization in `codex-config`, and made the TOML surface use `[features]` (while still accepting legacy `[feature_requirements]` for compatibility). - Exposed `featureRequirements` from `configRequirements/read`, regenerated the JSON/TypeScript schema artifacts, and updated the app-server README. - Wrapped the effective feature set in `ManagedFeatures`, backed by `ConstrainedWithSource<Features>`, and changed its API to mirror `Constrained<T>`: `can_set(...)`, `set(...) -> ConstraintResult<()>`, and result-returning `enable` / `disable` / `set_enabled` helpers. - Removed the legacy-usage and bulk-map passthroughs from `ManagedFeatures`; callers that need those behaviors now mutate a plain `Features` value and reapply it through `set(...)`, so the constrained wrapper remains the enforcement boundary. - Removed the production loophole for constructing unconstrained `ManagedFeatures`. Non-test code now creates it through the configured feature-loading path, and `impl From<Features> for ManagedFeatures` is restricted to `#[cfg(test)]`. - Rejected legacy feature aliases in enterprise feature requirements, and return a load error when a pinned combination cannot survive dependency normalization. - Validated config writes against enterprise feature requirements before persisting changes, including explicit conflicting writes and profile-specific feature states that normalize into invalid combinations. - Updated runtime and TUI feature-toggle paths to use the constrained setter API and to persist or apply the effective post-constraint value rather than the requested value. - Updated the `core_test_support` Bazel target to include the bundled core model-catalog fixtures in its runtime data, so helper code that resolves `core/models.json` through runfiles works in remote Bazel test environments. - Renamed the core config test coverage to emphasize that effective feature values are normalized at runtime, while conflicting persisted config writes are rejected. - Ran `compact_resume_after_second_compaction_preserves_history` inside an explicit 8 MiB test thread and Tokio runtime worker stack, following the existing larger-stack integration-test pattern, to keep the Windows `compact_resume_fork` test slice from aborting while a parallel investigation continues into whether some of the underlying async futures should be boxed. ## Verification - `cargo test -p codex-config` - `cargo test -p codex-core feature_requirements_ -- --nocapture` - `cargo test -p codex-core load_requirements_toml_produces_expected_constraints -- --nocapture` - `cargo test -p codex-core compact_resume_after_second_compaction_preserves_history -- --nocapture` - `cargo test -p codex-core compact_resume_fork -- --nocapture` - Re-ran the built `codex-core` `tests/all` binary with `RUST_MIN_STACK=262144` for `compact_resume_after_second_compaction_preserves_history` to confirm the explicit-stack harness fixes the deterministic low-stack repro. - `cargo test -p codex-core` - This still fails locally in unrelated integration areas that expect the `codex` / `test_stdio_server` binaries or hit existing `search_tool` wiremock mismatches. ## Docs `developers.openai.com/codex` should document the requirements-side `[features]` table for enterprise and MDM-managed configuration, including that it only accepts canonical feature keys and that conflicting config writes are rejected. |
||
|
|
c4cb594e73 |
Make js_repl image output controllable (#13331)
## Summary
Instead of always adding inner function call outputs to the model
context, let js code decide which ones to return.
- Stop auto-hoisting nested tool outputs from `codex.tool(...)` into the
outer `js_repl` function output.
- Keep `codex.tool(...)` return values unchanged as structured JS
objects.
- Add `codex.emitImage(...)` as the explicit path for attaching an image
to the outer `js_repl` function output.
- Support emitting from a direct image URL, a single `input_image` item,
an explicit `{ bytes, mimeType }` object, or a raw tool response object
containing exactly one image.
- Preserve existing `view_image` original-resolution behavior when JS
emits the raw `view_image` tool result.
- Suppress the special `ViewImageToolCall` event for `js_repl`-sourced
`view_image` calls so nested inspection stays side-effect free until JS
explicitly emits.
- Update the `js_repl` docs and generated project instructions with both
recommended patterns:
- `await codex.emitImage(codex.tool("view_image", { path }))`
- `await codex.emitImage({ bytes: await page.screenshot({ type: "jpeg",
quality: 85 }), mimeType: "image/jpeg" })`
#### [git stack](https://github.com/magus/git-stack-cli)
- β
`1` https://github.com/openai/codex/pull/13050
- π `2` https://github.com/openai/codex/pull/13331
- β³ `3` https://github.com/openai/codex/pull/13049
|
||
|
|
b92146d48b |
Add under-development original-resolution view_image support (#13050)
## Summary
Add original-resolution support for `view_image` behind the
under-development `view_image_original_resolution` feature flag.
When the flag is enabled and the target model is `gpt-5.3-codex` or
newer, `view_image` now preserves original PNG/JPEG/WebP bytes and sends
`detail: "original"` to the Responses API instead of using the legacy
resize/compress path.
## What changed
- Added `view_image_original_resolution` as an under-development feature
flag.
- Added `ImageDetail` to the protocol models and support for serializing
`detail: "original"` on tool-returned images.
- Added `PromptImageMode::Original` to `codex-utils-image`.
- Preserves original PNG/JPEG/WebP bytes.
- Keeps legacy behavior for the resize path.
- Updated `view_image` to:
- use the shared `local_image_content_items_with_label_number(...)`
helper in both code paths
- select original-resolution mode only when:
- the feature flag is enabled, and
- the model slug parses as `gpt-5.3-codex` or newer
- Kept local user image attachments on the existing resize path; this
change is specific to `view_image`.
- Updated history/image accounting so only `detail: "original"` images
use the docs-based GPT-5 image cost calculation; legacy images still use
the old fixed estimate.
- Added JS REPL guidance, gated on the same feature flag, to prefer JPEG
at 85% quality unless lossless is required, while still allowing other
formats when explicitly requested.
- Updated tests and helper code that construct
`FunctionCallOutputContentItem::InputImage` to carry the new `detail`
field.
## Behavior
### Feature off
- `view_image` keeps the existing resize/re-encode behavior.
- History estimation keeps the existing fixed-cost heuristic.
### Feature on + `gpt-5.3-codex+`
- `view_image` sends original-resolution images with `detail:
"original"`.
- PNG/JPEG/WebP source bytes are preserved when possible.
- History estimation uses the GPT-5 docs-based image-cost calculation
for those `detail: "original"` images.
#### [git stack](https://github.com/magus/git-stack-cli)
- π `1` https://github.com/openai/codex/pull/13050
- β³ `2` https://github.com/openai/codex/pull/13331
- β³ `3` https://github.com/openai/codex/pull/13049
|
||
|
|
7e980d7db6 |
Support multimodal custom tool outputs (#12948)
## Summary This changes `custom_tool_call_output` to use the same output payload shape as `function_call_output`, so freeform tools can return either plain text or structured content items. The main goal is to let `js_repl` return image content from nested `view_image` calls in its own `custom_tool_call_output`, instead of relying on a separate injected message. ## What changed - Changed `custom_tool_call_output.output` from `string` to `FunctionCallOutputPayload` - Updated freeform tool plumbing to preserve structured output bodies - Updated `js_repl` to aggregate nested tool content items and attach them to the outer `js_repl` result - Removed the old `js_repl` special case that injected `view_image` results as a separate pending user image message - Updated normalization/history/truncation paths to handle multimodal `custom_tool_call_output` - Regenerated app-server protocol schema artifacts ## Behavior Direct `view_image` calls still return a `function_call_output` with image content. When `view_image` is called inside `js_repl`, the outer `js_repl` `custom_tool_call_output` now carries: - an `input_text` item if the JS produced text output - one or more `input_image` items from nested tool results So the nested image result now stays inside the `js_repl` tool output instead of being injected as a separate message. ## Compatibility This is intended to be backward-compatible for resumed conversations. Older histories that stored `custom_tool_call_output.output` as a plain string still deserialize correctly, and older histories that used the previous injected-image-message flow also continue to resume. Added regression coverage for resuming a pre-change rollout containing: - string-valued `custom_tool_call_output` - legacy injected image message history #### [git stack](https://github.com/magus/git-stack-cli) - π `1` https://github.com/openai/codex/pull/12948 |
||
|
|
7fa9d9ae35 |
feat: include sandbox config with escalation request (#12839)
## Why Before this change, an escalation approval could say that a command should be rerun, but it could not carry the sandbox configuration that should still apply when the escalated command is actually spawned. That left an unsafe gap in the `zsh-fork` skill path: skill scripts under `scripts/` that did not declare permissions could be escalated without a sandbox, and scripts that did declare permissions could lose their bounded sandbox on rerun or cached session approval. This PR extends the escalation protocol so approvals can optionally carry sandbox configuration all the way through execution. That lets the shell runtime preserve the intended sandbox instead of silently widening access. We likely want a single permissions type for this codepath eventually, probably centered on `Permissions`. For now, the protocol needs to represent both the existing `PermissionProfile` form and the fuller `Permissions` form, so this introduces a temporary disjoint union, `EscalationPermissions`, to carry either one. Further, this means that today, a skill either: - does not declare any permissions, in which case it is run using the default sandbox for the turn - specifies permissions, in which case the skill is run using that exact sandbox, which might be more restrictive than the default sandbox for the turn We will likely change the skill's permissions to be additive to the existing permissions for the turn. ## What Changed - Added `EscalationPermissions` to `codex-protocol` so escalation requests can carry either a `PermissionProfile` or a full `Permissions` payload. - Added an explicit `EscalationExecution` mode to the shell escalation protocol so reruns distinguish between `Unsandboxed`, `TurnDefault`, and `Permissions(...)` instead of overloading `None`. - Updated `zsh-fork` shell reruns to resolve `TurnDefault` at execution time, which keeps ordinary `UseDefault` commands on the turn sandbox and preserves turn-level macOS seatbelt profile extensions. - Updated the `zsh-fork` skill path so a skill with no declared permissions inherits the conversation's effective sandbox instead of escalating unsandboxed. - Updated the `zsh-fork` skill path so a skill with declared permissions reruns with exactly those permissions, including when a cached session approval is reused. ## Testing - Added unit coverage in `core/src/tools/runtimes/shell/unix_escalation.rs` for the explicit `UseDefault` / `RequireEscalated` / `WithAdditionalPermissions` execution mapping. - Added unit coverage in `core/src/tools/runtimes/shell/unix_escalation.rs` for macOS seatbelt extension preservation in both the `TurnDefault` and explicit-permissions rerun paths. - Added integration coverage in `core/tests/suite/skill_approval.rs` for permissionless skills inheriting the turn sandbox and explicit skill permissions remaining bounded across cached approval reuse. |
||
|
|
eb77db2957 |
Log js_repl nested tool responses in rollout history (#12837)
## Summary - add tracing-based diagnostics for nested `codex.tool(...)` calls made from `js_repl` - emit a bounded, sanitized summary at `info!` - emit the exact raw serialized response object or error string seen by JavaScript at `trace!` - document how to enable these logs and where to find them, especially for `codex app-server` ## Why Nested `codex.tool(...)` calls inside `js_repl` are a debugging boundary: JavaScript sees the tool result, but that result is otherwise hard to inspect from outside the kernel. This change adds explicit tracing for that path using the repoβs normal observability pattern: - `info` for compact summaries - `trace` for exact raw payloads when deep debugging is needed ## What changed - `js_repl` now summarizes nested tool-call results across the response shapes it can receive: - message content - function-call outputs - custom tool outputs - MCP tool results and MCP error results - direct error strings - each nested `codex.tool(...)` completion logs: - `exec_id` - `tool_call_id` - `tool_name` - `ok` - a bounded summary struct describing the payload shape - at `trace`, the same path also logs the exact serialized response object or error string that JavaScript received - docs now include concrete logging examples for `codex app-server` - unit coverage was added for multimodal function output summaries and error summaries ## How to use it ### Summary-only logging Set: ```sh RUST_LOG=codex_core::tools::js_repl=info ``` For `codex app-server`, tracing output is written to the server process `stderr`. Example: ```sh RUST_LOG=codex_core::tools::js_repl=info \ LOG_FORMAT=json \ codex app-server \ 2> /tmp/codex-app-server.log ``` This emits bounded summary lines for nested `codex.tool(...)` calls. ### Full raw debugging Set: ```sh RUST_LOG=codex_core::tools::js_repl=trace ``` Example: ```sh RUST_LOG=codex_core::tools::js_repl=trace \ LOG_FORMAT=json \ codex app-server \ 2> /tmp/codex-app-server.log ``` At `trace`, you get: - the same `info` summary line - a `trace` line with the exact serialized response object seen by JavaScript - or the exact error string if the nested tool call failed ### Where the logs go For `codex app-server`, these logs go to process `stderr`, so redirect or capture `stderr` to inspect them. Example: ```sh RUST_LOG=codex_core::tools::js_repl=trace \ LOG_FORMAT=json \ /Users/fjord/code/codex/codex-rs/target/debug/codex app-server \ 2> /tmp/codex-app-server.log ``` Then inspect: ```sh rg "js_repl nested tool call" /tmp/codex-app-server.log ``` Without an explicit `RUST_LOG` override, these `js_repl` nested tool-call logs are typically not visible. |
||
|
|
40ab71a985 |
Disable js_repl when Node is incompatible at startup (#12824)
## Summary - validate `js_repl` Node compatibility during session startup when the experiment is enabled - if Node is missing or too old, disable `js_repl` and `js_repl_tools_only` for the session before tools and instructions are built - surface that startup disablement to users through the existing startup warning flow instead of only logging it - reuse the same compatibility check in js_repl kernel startup so startup gating and runtime behavior stay aligned - add a regression test that verifies the warning is emitted and that the first advertised tool list omits `js_repl` and `js_repl_reset` when Node is incompatible ## Why Today `js_repl` can be advertised based only on the feature flag, then fail later when the kernel starts. That makes the available tool list inaccurate at the start of a conversation, and users do not get a clear explanation for why the tool is unavailable. This change makes tool availability reflect real startup checks, keeps the advertised tool set stable for the lifetime of the session, and gives users a visible warning when `js_repl` is disabled. ## Testing - `just fmt` - `cargo test -p codex-core --test all js_repl_is_not_advertised_when_startup_node_is_incompatible` |
||
|
|
9501669a24 |
tests(js_repl): remove node-related skip paths from js_repl tests (#12185)
## Summary Remove js_repl/node test-skip paths and make Node setup explicit in CI so js_repl tests always run instead of silently skipping. ## Why We had multiple βexpediencyβ skip paths that let js_repl tests pass without actually exercising Node-backed behavior. This reduced CI signal and hid runtime/environment regressions. ## What changed ### CI - Added Node setup using `codex-rs/node-version.txt` in: - `.github/workflows/rust-ci.yml` - `.github/workflows/bazel.yml` - Added a Unix PATH copy step in Bazel workflow to expose the setup-node binary in common paths. ### js_repl test harness - Added explicit js_repl sandbox test configuration helpers in: - `codex-rs/core/src/tools/js_repl/mod.rs` - `codex-rs/core/src/tools/handlers/js_repl.rs` - Added Linux arg0 dispatch glue for js_repl tests so sandbox subprocess entrypoint behavior is correct under Linux test execution. ### Removed skip behavior - Deleted runtime guard function and early-return skips in js_repl tests (`can_run_js_repl_runtime_tests` and related per-test short-circuits). - Removed view_image integration test skip behavior: - dropped `skip_if_no_network!(Ok(()))` - removed βskip on Node missing/too oldβ branch after js_repl output inspection. ## Impact - js_repl/node tests now consistently execute and fail loudly when the environment is not correctly provisioned. - CI has stronger signal for js_repl regressions instead of false green from conditional skips. ## Testing - `cargo test -p codex-core` (locally) to validate js_repl unit/integration behavior with skips removed. - CI expected to surface any remaining environment/runtime gaps directly (rather than masking them). #### [git stack](https://github.com/magus/git-stack-cli) - β `1` https://github.com/openai/codex/pull/12300 - β `2` https://github.com/openai/codex/pull/12275 - β `3` https://github.com/openai/codex/pull/12205 - β `4` https://github.com/openai/codex/pull/12407 - β `5` https://github.com/openai/codex/pull/12372 - π `6` https://github.com/openai/codex/pull/12185 - β³ `7` https://github.com/openai/codex/pull/10673 |
||
|
|
8f3f2c3c02 |
tests(js_repl): stabilize CI runtime test execution (#12407)
## Summary Stabilize `js_repl` runtime test setup in CI and move tool-facing `js_repl` behavior coverage into integration tests. This is a test/CI change only. No production `js_repl` behavior change is intended. ## Why - Bazel test sandboxes (especially on macOS) could resolve a different `node` than the one installed by `actions/setup-node`, which caused `js_repl` runtime/version failures. - `js_repl` runtime tests depend on platform-specific sandbox/test-harness behavior, so they need explicit gating in a base-stability commit. - Several tests in the `js_repl` unit test module were actually black-box/tool-level behavior tests and fit better in the integration suite. ## Changes - Add `actions/setup-node` to the Bazel and Rust `Tests` workflows, using the exact version pinned in the repoβs Node version file. - In Bazel (non-Windows), pass `CODEX_JS_REPL_NODE_PATH=$(which node)` into test env so `js_repl` uses the `actions/setup-node` runtime inside Bazel tests. - Add a new integration test suite for `js_repl` tool behavior and register it in the core integration test suite module. - Move black-box `js_repl` behavior tests into the integration suite (persistence/TLA, builtin tool invocation, recursive self-call rejection, `process` isolation, blocked builtin imports). - Keep white-box manager/kernel tests in the `js_repl` unit test module. - Gate `js_repl` runtime tests to run only on macOS and only when a usable Node runtime is available (skip on other platforms / missing Node in this commit). ## Impact - Reduces `js_repl` CI failures caused by Node resolution drift in Bazel. - Improves test organization by separating tool-facing behavior tests from white-box manager/kernel tests. - Keeps the base commit stable while expanding `js_repl` runtime coverage. #### [git stack](https://github.com/magus/git-stack-cli) - β `1` https://github.com/openai/codex/pull/12372 - π `2` https://github.com/openai/codex/pull/12407 - β³ `3` https://github.com/openai/codex/pull/12185 - β³ `4` https://github.com/openai/codex/pull/10673 |
||
|
|
125fbec317 |
Fix js_repl view_image attachments in nested tool calls (#12725)
## Summary
- Fix `js_repl` so `await codex.tool("view_image", { path })` actually
attaches the image to the active turn when called from inside the JS
REPL.
- Restore the behavior expected by the existing `js_repl`
image-attachment test.
- This is a follow-up to
[#12553](https://github.com/openai/codex/pull/12553), which changed
`view_image` to return structured image content.
## Root Cause
- [#12553](https://github.com/openai/codex/pull/12553) changed
`view_image` from directly injecting a pending user image message to
returning structured `function_call_output` content items.
- The nested tool-call bridge inside `js_repl` serialized that tool
response back to the JS runtime, but it did not mirror returned image
content into the active turn.
- As a result, `view_image` appeared to succeed inside `js_repl`, but no
`input_image` was actually attached for the outer turn.
## What Changed
- Updated the nested tool-call path in `js_repl` to inspect function
tool responses for structured content items.
- When a nested tool response includes `input_image` content, `js_repl`
now injects a corresponding user `Message` into the active turn before
returning the raw tool result back to the JS runtime.
- Kept the normal JSON result flow intact, so `codex.tool(...)` still
returns the original tool output object to JavaScript.
## Why
- `js_repl` documentation and tests already assume that `view_image` can
be used from inside the REPL to attach generated images to the model.
- Without this fix, the nested call path silently dropped that
attachment behavior.
|
||
|
|
63c2ac96cd |
fix(js_repl): surface uncaught kernel errors and reset cleanly (#12636)
## Summary Improve `js_repl` behavior when the Node kernel hits a process-level failure (for example, an uncaught exception or unhandled Promise rejection). Instead of only surfacing a generic `js_repl kernel exited unexpectedly` after stdout EOF, `js_repl` now returns a clearer exec error for the active request, then resets the kernel cleanly. ## Why Some sandbox-denied operations can trigger Node errors that become process-level failures (for example, an unhandled EventEmitter `'error'` event). In that case: - the kernel process exits, - the host sees stdout EOF, - the user gets a generic kernel-exit error, - and the next request can briefly race with stale kernel state. This change improves that failure mode without monkeypatching Node APIs. ## Changes ### Kernel-side (`js_repl` Node process) - Add process-level handlers for: - `uncaughtException` - `unhandledRejection` - When one of these fires: - best-effort emit a normal `exec_result` error for the active exec - include actionable guidance to catch/handle async errors (including Promise rejections and EventEmitter `'error'` events) - exit intentionally so the host can reset/restart the kernel ### Host-side (`JsReplManager`) - Clear dead kernel state as soon as the stdout reader observes unexpected kernel exit/EOF. - This lets the next `js_repl` exec start a fresh kernel instead of hitting a stale broken-pipe path. ### Tests - Add regression coverage for: - uncaught async exception -> exec error + kernel recovery on next exec - Update forced-kernel-exit test to validate recovery behavior (next exec restarts cleanly) ## Impact - Better user-facing error for kernel crashes caused by uncaught/unhandled async failures. - Cleaner recovery behavior after kernel exit. ## Validation - `cargo test -p codex-core --lib tools::js_repl::tests::js_repl_uncaught_exception_returns_exec_error_and_recovers -- --exact` - `cargo test -p codex-core --lib tools::js_repl::tests::js_repl_forced_kernel_exit_recovers_on_next_exec -- --exact` - `just fmt` |
||
|
|
f6053fdfb3 |
feat(core) Introduce Feature::RequestPermissions (#11871)
## Summary Introduces the initial implementation of Feature::RequestPermissions. RequestPermissions allows the model to request that a command be run inside the sandbox, with additional permissions, like writing to a specific folder. Eventually this will include other rules as well, and the ability to persist these permissions, but this PR is already quite large - let's get the core flow working and go from there! <img width="1279" height="541" alt="Screenshot 2026-02-15 at 2 26 22β―PM" src="https://github.com/user-attachments/assets/0ee3ec0f-02ec-4509-91a2-809ac80be368" /> ## Testing - [x] Added tests - [x] Tested locally - [x] Feature |
||
|
|
66d5d34e6e | core: preserve constrained approval/sandbox policies in TurnContext (#12473) | ||
|
|
097620218d |
js_repl: remove codex.state helper references (#12275)
## Summary This PR removes `codex.state` from the `js_repl` helper surface and removes all corresponding documentation/instruction references. ## Motivation Top-level bindings in `js_repl` now persist across cells, so the extra `codex.state` helper is redundant and adds unnecessary API/docs surface. ## Changes - Removed the long-lived `state` object from the Node kernel helper wiring. - Stopped exposing `codex.state` (and `context.state`) during `js_repl` execution. - Updated user-facing `js_repl` docs to remove `codex.state`. - Updated generated instruction text and related test expectations to list only: - `codex.tmpDir` - `codex.tool(name, args?)` #### [git stack](https://github.com/magus/git-stack-cli) - β `1` https://github.com/openai/codex/pull/12300 - π `2` https://github.com/openai/codex/pull/12275 - β³ `3` https://github.com/openai/codex/pull/12205 - β³ `4` https://github.com/openai/codex/pull/12185 - β³ `5` https://github.com/openai/codex/pull/10673 |
||
|
|
cc248e4681 |
js_repl: canonicalize paths for node_modules boundary checks (#12177)
## Summary Fix `js_repl` package-resolution boundary checks for macOS temp directory path aliasing (`/var` vs `/private/var`). ## Problem `js_repl` verifies that resolved bare-package imports stay inside a configured `node_modules` root. On macOS, temp directories are commonly exposed as `/var/...` but canonicalize to `/private/var/...`. Because the boundary check compared raw paths with `path.relative(...)`, valid resolutions under temp dirs could be misclassified as escaping the allowed base, causing false `Module not found` errors. ## Changes - Add `fs` import in the JS kernel. - Add `canonicalizePath()` using `fs.realpathSync.native(...)` (with safe fallback). - Canonicalize both `base` and `resolvedPath` before running the `node_modules` containment check. ## Impact - Fixes false-negative boundary checks for valid package resolutions in macOS temp-dir scenarios. - Keeps the existing security boundary behavior intact. - Scope is limited to `js_repl` kernel module path validation logic. #### [git stack](https://github.com/magus/git-stack-cli) - π `1` https://github.com/openai/codex/pull/12177 - β³ `2` https://github.com/openai/codex/pull/10673 |
||
|
|
f600453699 |
[js_repl] paths for node module resolution can be specified for js_repl (#11944)
# External (non-OpenAI) Pull Request Requirements
In `js_repl` mode, module resolution currently starts from
`js_repl_kernel.js`, which is written to a per-kernel temp dir. This
effectively means that bare imports will not resolve.
This PR adds a new config option, `js_repl_node_module_dirs`, which is a
list of dirs that are used (in order) to resolve a bare import. If none
of those work, the current working directory of the thread is used.
For example:
```toml
js_repl_node_module_dirs = [
"/path/to/node_modules/",
"/other/path/to/node_modules/",
]
```
|
||
|
|
77f74a5c17 |
fix: race in js repl (#11922)
js_repl_reset previously raced with in-flight/new js_repl executions because reset() could clear exec_tool_calls without synchronizing with execute(). In that window, a running exec could lose its per-exec tool-call context, and subsequent kernel RunTool messages would fail with js_repl exec context not found. The fix serializes reset and execute on the same exec_lock, so reset cannot run concurrently with exec setup/teardown. We also keep the timeout path safe by performing reset steps inline while execute() already holds the lock, avoiding re-entrant lock acquisition. A regression test now verifies that reset waits for the exec lock and does not clear tool-call state early. |
||
|
|
846464e869 |
fix: js_repl reset hang by clearing exec tool calls without waiting (#11932)
Remove the waiting loop in `reset` so it no longer blocks on potentially hanging exec tool calls + add `clear_all_exec_tool_calls_map` to drain the map and notify waiters so `reset` completes immediately |
||
|
|
0d76d029b7 |
Fix js_repl in-flight tool-call waiter race (#11800)
## Summary This PR fixes a race in `js_repl` tool-call draining that could leave an exec waiting indefinitely for in-flight tool calls to finish. The fix is in: - `/Users/fjord/code/codex-jsrepl-seq/codex-rs/core/src/tools/js_repl/mod.rs` ## Problem `js_repl` tracks in-flight tool calls per exec and waits for them to drain on completion/timeout/cancel paths. The previous wait logic used a check-then-wait pattern with `Notify` that could miss a wakeup: 1. Observe `in_flight > 0` 2. Drop lock 3. Register wait (`notified().await`) If `notify_waiters()` happened between (2) and (3), the waiter could sleep until another notification that never comes. ## What changed - Updated all exec-tool-call wait loops to create an owned notification future while holding the lock: - use `Arc<Notify>::notified_owned()` instead of cloning notify and awaiting later. - Applied this consistently to: - `wait_for_exec_tool_calls` - `wait_for_all_exec_tool_calls` - `wait_for_exec_tool_calls_map` This preserves existing behavior while eliminating the lost-wakeup window. ## Test coverage Added a regression test: - `wait_for_exec_tool_calls_map_drains_inflight_calls_without_hanging` The test repeatedly races waiter/finisher tasks and asserts bounded completion to catch hangs. ## Impact - No API changes. - No user-facing behavior changes intended. - Improves reliability of exec lifecycle boundaries when tool calls are still in flight. #### [git stack](https://github.com/magus/git-stack-cli) - β `1` https://github.com/openai/codex/pull/11796 - π `2` https://github.com/openai/codex/pull/11800 - β³ `3` https://github.com/openai/codex/pull/10673 - β³ `4` https://github.com/openai/codex/pull/10670 |
||
|
|
6cbb489e6e |
Fix js_repl view_image test runtime panic (#11796)
## Summary Fixes a flaky/panicking `js_repl` image-path test by running it on a multi-thread Tokio runtime and tightening assertions to focus on real behavior. ## Problem `js_repl_can_attach_image_via_view_image_tool` in `/Users/fjord/code/codex-jsrepl-seq/codex-rs/core/src/tools/js_repl/mod.rs` can panic under single-thread test runtime with: `can call blocking only when running on the multi-threaded runtime` It also asserted a brittle user-facing text string. ## Changes 1. Updated the test runtime to: `#[tokio::test(flavor = "multi_thread", worker_threads = 2)]` 2. Removed the brittle `"attached local image path"` string assertion. 3. Kept the concrete side-effect assertions: - tool call succeeds - image is actually injected into pending input (`InputImage` with `data:image/png;base64,...`) ## Why this is safe This is test-only behavior. No production runtime code paths are changed. ## Validation - Ran: `cargo test -p codex-core tools::js_repl::tests::js_repl_can_attach_image_via_view_image_tool -- --nocapture` - Result: pass #### [git stack](https://github.com/magus/git-stack-cli) - π `1` https://github.com/openai/codex/pull/11796 - β³ `2` https://github.com/openai/codex/pull/11800 - β³ `3` https://github.com/openai/codex/pull/10673 - β³ `4` https://github.com/openai/codex/pull/10670 |
||
|
|
a02342c9e1 |
Add js_repl kernel crash diagnostics (#11666)
## Summary
This PR improves `js_repl` crash diagnostics so kernel failures are
debuggable without weakening timeout/reset guarantees.
## What Changed
- Added bounded kernel stderr capture and truncation logic (line + byte
caps).
- Added structured kernel snapshots (`pid`, exit status, stderr tail)
for failure paths.
- Enriched model-visible kernel-failure errors with a structured
diagnostics payload:
- `js_repl diagnostics: {...}`
- Included only for likely kernel-failure write/EOF cases.
- Improved logging around kernel write failures, unexpected exits, and
kill/wait paths.
- Added/updated unit tests for:
- UTF-8-safe truncation
- stderr tail bounds
- structured diagnostics shape/truncation
- conditional diagnostics emission
- timeout kill behavior
- forced kernel-failure diagnostics
## Why
Before this, failures like broken pipe / unexpected kernel exit often
surfaced as generic errors with little context. This change preserves
existing behavior but adds actionable diagnostics while keeping output
bounded.
## Scope
- Code changes are limited to:
-
`/Users/fjord/code/codex-jsrepl-seq/codex-rs/core/src/tools/js_repl/mod.rs`
## Validation
- `cargo clippy -p codex-core --all-targets -- -D warnings`
- Targeted `codex-core` js_repl unit tests (including new
diagnostics/timeout coverage)
- Tried starting a long running js_repl command (sleep for 10 minutes),
verified error output was as expected after killing the node process.
#### [git stack](https://github.com/magus/git-stack-cli)
- π `1` https://github.com/openai/codex/pull/11666
- β³ `2` https://github.com/openai/codex/pull/10673
- β³ `3` https://github.com/openai/codex/pull/10670
|
||
|
|
38c442ca7f |
core: limit search_tool_bm25 to Apps and clarify discovery guidance (#11669)
## Summary - Limit `search_tool_bm25` indexing to `codex_apps` tools only, so non-Apps MCP servers are no longer discoverable through this search path. - Move search-tool discovery guidance into the `search_tool_bm25` tool description (via template include) instead of injecting it as a separate developer message. - Update Apps discovery guidance wording to clarify when to use `search_tool_bm25` for Apps-backed systems (for example Slack, Google Drive, Jira, Notion) and when to call tools directly. - Remove dead `core` helper code (`filter_codex_apps_mcp_tools` and `codex_apps_connector_id`) that is no longer used after the tool-selection refactor. - Update `core` search-tool tests to assert codex-apps-only behavior and to validate guidance from the tool description. ## Validation - β `just fmt` - β `cargo test -p codex-core search_tool` - β οΈ `cargo test -p codex-core` was attempted, but the run repeatedly stalled on `tools::js_repl::tests::js_repl_can_attach_image_via_view_image_tool`. ## Tickets - None |
||
|
|
0dcfc59171 |
Add js_repl_tools_only model and routing restrictions (#10671)
# 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. #### [git stack](https://github.com/magus/git-stack-cli) - β `1` https://github.com/openai/codex/pull/10674 - β `2` https://github.com/openai/codex/pull/10672 - π `3` https://github.com/openai/codex/pull/10671 - β³ `4` https://github.com/openai/codex/pull/10673 - β³ `5` https://github.com/openai/codex/pull/10670 |
||
|
|
466be55abc |
Add js_repl host helpers and exec end events (#10672)
## Summary
This PR adds host-integrated helper APIs for `js_repl` and updates model
guidance so the agent can use them reliably.
### Whatβs included
- Add `codex.tool(name, args?)` in the JS kernel so `js_repl` can call
normal Codex tools.
- Keep persistent JS state and scratch-path helpers available:
- `codex.state`
- `codex.tmpDir`
- Wire `js_repl` tool calls through the standard tool router path.
- Add/align `js_repl` execution completion/end event behavior with
existing tool logging patterns.
- Update dynamic prompt injection (`project_doc`) to document:
- how to call `codex.tool(...)`
- raw output behavior
- image flow via `view_image` (`codex.tmpDir` +
`codex.tool("view_image", ...)`)
- stdio safety guidance (`console.log` / `codex.tool`, avoid direct
`process.std*`)
## Why
- Standardize JS-side tool usage on `codex.tool(...)`
- Make `js_repl` behavior more consistent with existing tool execution
and event/logging patterns.
- Give the model enough runtime guidance to use `js_repl` safely and
effectively.
## Testing
- Added/updated unit and runtime tests for:
- `codex.tool` calls from `js_repl` (including shell/MCP paths)
- image handoff flow via `view_image`
- prompt-injection text for `js_repl` guidance
- execution/end event behavior and related regression coverage
#### [git stack](https://github.com/magus/git-stack-cli)
- β
`1` https://github.com/openai/codex/pull/10674
- π `2` https://github.com/openai/codex/pull/10672
- β³ `3` https://github.com/openai/codex/pull/10671
- β³ `4` https://github.com/openai/codex/pull/10673
- β³ `5` https://github.com/openai/codex/pull/10670
|
||
|
|
42e22f3bde |
Add feature-gated freeform js_repl core runtime (#10674)
## Summary This PR adds an **experimental, feature-gated `js_repl` core runtime** so models can execute JavaScript in a persistent REPL context across tool calls. The implementation integrates with existing feature gating, tool registration, prompt composition, config/schema docs, and tests. ## What changed - Added new experimental feature flag: `features.js_repl`. - Added freeform `js_repl` tool and companion `js_repl_reset` tool. - Gated tool availability behind `Feature::JsRepl`. - Added conditional prompt-section injection for JS REPL instructions via marker-based prompt processing. - Implemented JS REPL handlers, including freeform parsing and pragma support (timeout/reset controls). - Added runtime resolution order for Node: 1. `CODEX_JS_REPL_NODE_PATH` 2. `js_repl_node_path` in config 3. `PATH` - Added JS runtime assets/version files and updated docs/schema. ## Why This enables richer agent workflows that require incremental JavaScript execution with preserved state, while keeping rollout safe behind an explicit feature flag. ## Testing Coverage includes: - Feature-flag gating behavior for tool exposure. - Freeform parser/pragma handling edge cases. - Runtime behavior (state persistence across calls and top-level `await` support). ## Usage ```toml [features] js_repl = true ``` Optional runtime override: - `CODEX_JS_REPL_NODE_PATH`, or - `js_repl_node_path` in config. #### [git stack](https://github.com/magus/git-stack-cli) - π `1` https://github.com/openai/codex/pull/10674 - β³ `2` https://github.com/openai/codex/pull/10672 - β³ `3` https://github.com/openai/codex/pull/10671 - β³ `4` https://github.com/openai/codex/pull/10673 - β³ `5` https://github.com/openai/codex/pull/10670 |