From 0543d0a022376f0440c2ea5b9aab8c2fd0813758 Mon Sep 17 00:00:00 2001 From: Curtis 'Fjord' Hawthorne Date: Wed, 25 Feb 2026 09:44:52 -0800 Subject: [PATCH] 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) --- codex-rs/core/src/features.rs | 23 ++++++++++++++++++++++- codex-rs/tui/src/chatwidget/tests.rs | 25 +++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/codex-rs/core/src/features.rs b/codex-rs/core/src/features.rs index d3bace851..546652ae6 100644 --- a/codex-rs/core/src/features.rs +++ b/codex-rs/core/src/features.rs @@ -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)); diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index 358a9615c..22ba19af6 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -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;