docs/js_repl.md
js_repl)js_repl runs JavaScript in a persistent Node-backed kernel with top-level await.
js_repl is disabled by default and only appears when:
[features]
js_repl = true
js_repl_tools_only can be enabled to force direct model tool calls through js_repl:
[features]
js_repl = true
js_repl_tools_only = true
When enabled, direct model tool calls are restricted to js_repl and js_repl_reset; other tools remain available via await codex.tool(...) inside js_repl.
js_repl requires a Node version that meets or exceeds codex-rs/node-version.txt.
Runtime resolution order:
CODEX_JS_REPL_NODE_PATH environment variablejs_repl_node_path in config/profilenode discovered on PATHYou can configure an explicit runtime path:
js_repl_node_path = "/absolute/path/to/node"
js_repl resolves bare specifiers (for example await import("pkg")) using an ordered
search path. Local file imports are also supported for relative paths, absolute paths, and
file:// URLs that point to ESM .js / .mjs files.
Module resolution proceeds in the following order:
CODEX_JS_REPL_NODE_MODULE_DIRS (PATH-delimited list)js_repl_node_module_dirs in config/profile (array of absolute paths)For CODEX_JS_REPL_NODE_MODULE_DIRS and js_repl_node_module_dirs, module resolution is attempted in the order provided with earlier entries taking precedence.
Bare package imports always use this REPL-wide search path, even when they originate from an imported local file. They are not resolved relative to the imported file's location.
js_repl is a freeform tool: send raw JavaScript source text.// codex-js-repl: timeout_ms=15000var / function bindings persist only when execution clearly reached their declaration or a supported write site.var failed-cell cases are direct top-level identifier writes and updates before the declaration (for example x = 1, x += 1, x++, x &&= 1) and non-empty top-level for...in / for...of loops.var, partial var destructuring recovery, pre-declaration undefined reads, and empty top-level for...in / for...of loop vars.import x from "pkg") are currently unsupported; use dynamic imports with await import("pkg")..js / .mjs files and run in the same REPL VM context as the calling cell..js / .mjs files via relative paths, absolute paths, or file:// URLs. Bare package and builtin imports from local files must stay dynamic via await import(...).import.meta.resolve() returns importable strings such as file://..., bare package names, and node:fs; the returned value can be passed back to await import(...).await import("./file.js") picks up edits and fixed failures. Top-level bindings you already created still persist until js_repl_reset.js_repl_reset to clear the kernel state.js_repl exposes these globals:
codex.cwd: REPL working directory path.codex.homeDir: effective home directory path from the kernel environment.codex.tmpDir: per-session scratch directory path.codex.tool(name, args?): executes a normal Codex tool call from inside js_repl (including shell tools like shell / shell_command when available).codex.emitImage(imageLike): explicitly adds one image to the outer js_repl function output each time you call it.codex.tool(...) and codex.emitImage(...) keep stable helper identities across cells. Saved references and persisted objects can reuse them in later cells, but async callbacks that fire after a cell finishes still fail because no exec is active.codex.*, the captured console, and Node-like import.meta helpers.codex.tool(...) call emits a bounded summary at info level from the codex_core::tools::js_repl logger. At trace level, the same path also logs the exact raw response object or error string seen by JavaScript.codex.tool(...) outputs stay inside JavaScript unless you emit them explicitly.codex.emitImage(...) accepts a data URL, a single input_image item, an object like { bytes, mimeType }, or a raw tool response object that contains exactly one image and no text. Call it multiple times if you want to emit multiple images.codex.emitImage(...) rejects mixed text-and-image content.detail: "original" only when the view_image tool schema includes a detail argument. The same availability applies to codex.emitImage(...): if view_image.detail is present, you may also pass detail: "original" there. Use this when high-fidelity image perception or precise localization is needed, especially for CUA agents.await codex.emitImage({ bytes: await page.screenshot({ type: "jpeg", quality: 85 }), mimeType: "image/jpeg", detail: "original" }).await codex.emitImage(codex.tool("view_image", { path: "/absolute/path", detail: "original" })).codex.emitImage(...) or view_image, prefer JPEG at about 85 quality when lossy compression is acceptable; use PNG when transparency or lossless detail matters. Smaller uploads are faster and less likely to hit size limits.Avoid writing directly to process.stdout / process.stderr / process.stdin; the kernel uses a JSON-line transport over stdio.
Nested codex.tool(...) diagnostics are emitted through normal tracing output instead of rollout history.
info level logs a bounded summary.trace level also logs the exact serialized response object or error string seen by JavaScript.For codex app-server, these logs are written to the server process stderr.
Examples:
RUST_LOG=codex_core::tools::js_repl=info \
LOG_FORMAT=json \
codex app-server \
2> /tmp/codex-app-server.log
RUST_LOG=codex_core::tools::js_repl=trace \
LOG_FORMAT=json \
codex app-server \
2> /tmp/codex-app-server.log
In both cases, inspect /tmp/codex-app-server.log or whatever sink captures the process stderr.
meriyah.umd.min.js)The kernel embeds a vendored Meriyah bundle at:
codex-rs/core/src/tools/js_repl/meriyah.umd.min.jsCurrent source is [email protected] from npm (dist/meriyah.umd.min.js).
Licensing is tracked in:
third_party/meriyah/LICENSENOTICEFrom a clean temp directory:
tmp="$(mktemp -d)"
cd "$tmp"
npm pack [email protected]
tar -xzf meriyah-7.0.0.tgz
cp package/dist/meriyah.umd.min.js /path/to/repo/codex-rs/core/src/tools/js_repl/meriyah.umd.min.js
cp package/LICENSE.md /path/to/repo/third_party/meriyah/LICENSE
7.0.0 in the commands above with the target version.dist/meriyah.umd.min.js into codex-rs/core/src/tools/js_repl/meriyah.umd.min.js.third_party/meriyah/LICENSE.meriyah.umd.min.js.NOTICE if the upstream copyright notice changed.js_repl tests.