Promote js_repl to experimental with Node requirement (#12712)

## Summary

- Promote `js_repl` to an experimental feature that users can enable
from `/experimental`.
- Add `js_repl` experimental metadata, including the Node prerequisite
and activation guidance.
- Add regression coverage for the feature metadata and the
`/experimental` popup.

## What Changed

- Changed `Feature::JsRepl` from `Stage::UnderDevelopment` to
`Stage::Experimental`.
- Added experimental metadata for `js_repl` in `core/src/features.rs`:
  - name: `JavaScript REPL`
- description: calls out interactive website debugging, inline
JavaScript execution, and the required Node version (`>= v24.13.1`)
- announcement: tells users to enable it, then start a new chat or
restart Codex
- Added a core unit test that verifies:
  - `js_repl` is experimental
  - `js_repl` is disabled by default
- the hardcoded Node version in the description matches
`node-version.txt`
- Added a TUI test that opens the `/experimental` popup and verifies the
rendered `js_repl` entry includes the Node requirement text.

## Testing

- `just fmt`
- `cargo test -p codex-tui`
- `cargo test -p codex-core` (unit-test phase passed; stopped during the
long `tests/all.rs` integration suite)
This commit is contained in:
Curtis 'Fjord' Hawthorne
2026-02-25 09:44:52 -08:00
committed by GitHub
Unverified
parent 9a393c9b6f
commit 0543d0a022
2 changed files with 47 additions and 1 deletions
+22 -1
View File
@@ -460,7 +460,11 @@ pub const FEATURES: &[FeatureSpec] = &[
FeatureSpec {
id: Feature::JsRepl,
key: "js_repl",
stage: Stage::UnderDevelopment,
stage: Stage::Experimental {
name: "JavaScript REPL",
menu_description: "Enable a persistent Node-backed JavaScript REPL for interactive website debugging and other inline JavaScript execution capabilities. Requires Node >= v24.13.1 installed.",
announcement: "NEW: JavaScript REPL is now available in /experimental. Enable it, then start a new chat or restart Codex to use it.",
},
default_enabled: false,
},
FeatureSpec {
@@ -795,6 +799,23 @@ mod tests {
assert_eq!(Feature::UseLinuxSandboxBwrap.default_enabled(), false);
}
#[test]
fn js_repl_is_experimental_and_user_toggleable() {
let spec = Feature::JsRepl.info();
let stage = spec.stage;
let expected_node_version = include_str!("../../node-version.txt").trim_end();
assert!(matches!(stage, Stage::Experimental { .. }));
assert_eq!(stage.experimental_menu_name(), Some("JavaScript REPL"));
assert_eq!(
stage.experimental_menu_description().map(str::to_owned),
Some(format!(
"Enable a persistent Node-backed JavaScript REPL for interactive website debugging and other inline JavaScript execution capabilities. Requires Node >= v{expected_node_version} installed."
))
);
assert_eq!(Feature::JsRepl.default_enabled(), false);
}
#[test]
fn collab_is_legacy_alias_for_multi_agent() {
assert_eq!(feature_for_key("multi_agent"), Some(Feature::Collab));
+25
View File
@@ -23,6 +23,7 @@ use codex_core::config::ConstraintError;
#[cfg(target_os = "windows")]
use codex_core::config::types::WindowsSandboxModeToml;
use codex_core::config_loader::RequirementSource;
use codex_core::features::FEATURES;
use codex_core::features::Feature;
use codex_core::models_manager::manager::ModelsManager;
use codex_core::skills::model::SkillMetadata;
@@ -5945,6 +5946,30 @@ async fn experimental_features_toggle_saves_on_exit() {
assert_eq!(updates, vec![(expected_feature, true)]);
}
#[tokio::test]
async fn experimental_popup_shows_js_repl_node_requirement() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None).await;
let js_repl_description = FEATURES
.iter()
.find(|spec| spec.id == Feature::JsRepl)
.and_then(|spec| spec.stage.experimental_menu_description())
.expect("expected js_repl experimental description");
let node_requirement = js_repl_description
.split(". ")
.find(|sentence| sentence.starts_with("Requires Node >= v"))
.map(|sentence| sentence.trim_end_matches(" installed."))
.expect("expected js_repl description to mention the Node requirement");
chat.open_experimental_popup();
let popup = render_bottom_popup(&chat, 120);
assert!(
popup.contains(node_requirement),
"expected js_repl feature description to mention the required Node version, got:\n{popup}"
);
}
#[tokio::test]
async fn model_selection_popup_snapshot() {
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(Some("gpt-5-codex")).await;