mirror of
https://github.com/openai/codex.git
synced 2026-04-29 00:55:38 +00:00
Code mode on v8 (#15276)
Moves Code Mode to a new crate with no dependencies on codex. This
create encodes the code mode semantics that we want for lifetime,
mounting, tool calling.
The model-facing surface is mostly unchanged. `exec` still runs raw
JavaScript, `wait` still resumes or terminates a `cell_id`, nested tools
are still available through `tools.*`, and helpers like `text`, `image`,
`store`, `load`, `notify`, `yield_control`, and `exit` still exist.
The major change is underneath that surface:
- Old code mode was an external Node runtime.
- New code mode is an in-process V8 runtime embedded directly in Rust.
- Old code mode managed cells inside a long-lived Node runner process.
- New code mode manages cells in Rust, with one V8 runtime thread per
active `exec`.
- Old code mode used JSON protocol messages over child stdin/stdout plus
Node worker-thread messages.
- New code mode uses Rust channels and direct V8 callbacks/events.
This PR also fixes the two migration regressions that fell out of that
substrate change:
- `wait { terminate: true }` now waits for the V8 runtime to actually
stop before reporting termination.
- synchronous top-level `exit()` now succeeds again instead of surfacing
as a script error.
---
- `core/src/tools/code_mode/*` is now mostly an adapter layer for the
public `exec` / `wait` tools.
- `code-mode/src/service.rs` owns cell sessions and async control flow
in Rust.
- `code-mode/src/runtime/*.rs` owns the embedded V8 isolate and
JavaScript execution.
- each `exec` spawns a dedicated runtime thread plus a Rust
session-control task.
- helper globals are installed directly into the V8 context instead of
being injected through a source prelude.
- helper modules like `tools.js` and `@openai/code_mode` are synthesized
through V8 module resolution callbacks in Rust.
---
Also added a benchmark for showing the speed of init and use of a code
mode env:
```
$ cargo bench -p codex-code-mode --bench exec_overhead -- --samples 30 --warm-iterations 25 --tool-counts 0,32,128
Finished [`bench` profile [optimized]](https://doc.rust-lang.org/cargo/reference/profiles.html#default-profiles) target(s) in 0.18s
Running benches/exec_overhead.rs (target/release/deps/exec_overhead-008c440d800545ae)
exec_overhead: samples=30, warm_iterations=25, tool_counts=[0, 32, 128]
scenario tools samples warmups iters mean/exec p95/exec rssΔ p50 rssΔ max
cold_exec 0 30 0 1 1.13ms 1.20ms 8.05MiB 8.06MiB
warm_exec 0 30 1 25 473.43us 512.49us 912.00KiB 1.33MiB
cold_exec 32 30 0 1 1.03ms 1.15ms 8.08MiB 8.11MiB
warm_exec 32 30 1 25 509.73us 545.76us 960.00KiB 1.30MiB
cold_exec 128 30 0 1 1.14ms 1.19ms 8.30MiB 8.34MiB
warm_exec 128 30 1 25 575.08us 591.03us 736.00KiB 864.00KiB
memory uses a fresh-process max RSS delta for each scenario
```
---------
Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -1672,8 +1672,6 @@ async fn code_mode_exit_stops_script_immediately() -> Result<()> {
|
||||
&server,
|
||||
"use exec to stop script early with exit helper",
|
||||
r#"
|
||||
import { exit, text } from "@openai/code_mode";
|
||||
|
||||
text("before");
|
||||
exit();
|
||||
text("after");
|
||||
@@ -2129,6 +2127,7 @@ text(JSON.stringify(Object.getOwnPropertyNames(globalThis).sort()));
|
||||
"SuppressedError",
|
||||
"Symbol",
|
||||
"SyntaxError",
|
||||
"Temporal",
|
||||
"TypeError",
|
||||
"URIError",
|
||||
"Uint16Array",
|
||||
@@ -2141,7 +2140,6 @@ text(JSON.stringify(Object.getOwnPropertyNames(globalThis).sort()));
|
||||
"WebAssembly",
|
||||
"__codexContentItems",
|
||||
"add_content",
|
||||
"console",
|
||||
"decodeURI",
|
||||
"decodeURIComponent",
|
||||
"encodeURI",
|
||||
@@ -2282,10 +2280,8 @@ async fn code_mode_can_call_hidden_dynamic_tools() -> Result<()> {
|
||||
test.session_configured = new_thread.session_configured;
|
||||
|
||||
let code = r#"
|
||||
import { ALL_TOOLS, hidden_dynamic_tool } from "tools.js";
|
||||
|
||||
const tool = ALL_TOOLS.find(({ name }) => name === "hidden_dynamic_tool");
|
||||
const out = await hidden_dynamic_tool({ city: "Paris" });
|
||||
const out = await tools.hidden_dynamic_tool({ city: "Paris" });
|
||||
text(
|
||||
JSON.stringify({
|
||||
name: tool?.name ?? null,
|
||||
|
||||
@@ -159,7 +159,9 @@ async fn unified_exec_intercepts_apply_patch_exec_command() -> Result<()> {
|
||||
let call_id = "uexec-apply-patch";
|
||||
let args = json!({
|
||||
"cmd": command,
|
||||
"yield_time_ms": 250,
|
||||
// The intercepted apply_patch path spawns a helper process, which can
|
||||
// take longer than a tiny unified-exec yield deadline on CI.
|
||||
"yield_time_ms": 5_000,
|
||||
});
|
||||
|
||||
let responses = vec![
|
||||
|
||||
Reference in New Issue
Block a user