Skip to content

Commit 89bfad9

Browse files
committed
Add feature-gated js_repl polling flow
git-stack-id: fjord/js_repl_seq---4hn_deh_pjc2v_ git-stack-title: Add feature-gated js_repl polling flow
1 parent 6fbde92 commit 89bfad9

File tree

15 files changed

+861
-62
lines changed

15 files changed

+861
-62
lines changed

codex-rs/core/config.schema.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@
193193
"js_repl": {
194194
"type": "boolean"
195195
},
196+
"js_repl_polling": {
197+
"type": "boolean"
198+
},
196199
"js_repl_tools_only": {
197200
"type": "boolean"
198201
},
@@ -1223,6 +1226,9 @@
12231226
"js_repl": {
12241227
"type": "boolean"
12251228
},
1229+
"js_repl_polling": {
1230+
"type": "boolean"
1231+
},
12261232
"js_repl_tools_only": {
12271233
"type": "boolean"
12281234
},

codex-rs/core/gpt-5.2-codex_prompt.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ You are Codex, based on GPT-5. You are running as a coding agent in the Codex CL
1212
- `codex.sh` requires a string command and resolves to `{ stdout, stderr, exitCode }`; `codex.tool` executes a normal tool call and resolves to the raw tool output object.
1313
- Top-level bindings persist across cells. If you hit `SyntaxError: Identifier 'x' has already been declared`, reuse the binding, pick a new name, wrap in `{ ... }` for block scope, or reset the kernel.
1414
- Top-level static import declarations (for example `import x from "pkg"`) are currently unsupported in `js_repl`; use dynamic imports with `await import("pkg")` instead.
15+
<!-- js_repl_polling:start -->
16+
- Polling mode is two-step: (1) call `js_repl` with first-line pragma `// codex-js-repl: poll=true` to get an `exec_id`; (2) call `js_repl_poll` with that `exec_id` until `status` is `completed` or `error`.
17+
- `js_repl_poll` must not be called before a successful `js_repl` submission returns an `exec_id`.
18+
- If `js_repl` rejects your payload format, resend raw JS with the pragma; do not retry with JSON, quoted strings, or markdown fences.
19+
<!-- js_repl_polling:end -->
1520
<!-- js_repl_tools_only:start -->
1621
- Do not call tools directly; use `js_repl` + `codex.tool(...)`, and use `codex.sh(...)` for shell commands instead of direct shell tool calls.
1722
- Tools available via `codex.tool(...)`: {{JS_REPL_TOOL_LIST}}. MCP tools (if any) can also be called by name.

codex-rs/core/gpt_5_codex_prompt.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ You are Codex, based on GPT-5. You are running as a coding agent in the Codex CL
1212
- `codex.sh` requires a string command and resolves to `{ stdout, stderr, exitCode }`; `codex.tool` executes a normal tool call and resolves to the raw tool output object.
1313
- Top-level bindings persist across cells. If you hit `SyntaxError: Identifier 'x' has already been declared`, reuse the binding, pick a new name, wrap in `{ ... }` for block scope, or reset the kernel.
1414
- Top-level static import declarations (for example `import x from "pkg"`) are currently unsupported in `js_repl`; use dynamic imports with `await import("pkg")` instead.
15+
<!-- js_repl_polling:start -->
16+
- Polling mode is two-step: (1) call `js_repl` with first-line pragma `// codex-js-repl: poll=true` to get an `exec_id`; (2) call `js_repl_poll` with that `exec_id` until `status` is `completed` or `error`.
17+
- `js_repl_poll` must not be called before a successful `js_repl` submission returns an `exec_id`.
18+
- If `js_repl` rejects your payload format, resend raw JS with the pragma; do not retry with JSON, quoted strings, or markdown fences.
19+
<!-- js_repl_polling:end -->
1520
<!-- js_repl_tools_only:start -->
1621
- Do not call tools directly; use `js_repl` + `codex.tool(...)`, and use `codex.sh(...)` for shell commands instead of direct shell tool calls.
1722
- Tools available via `codex.tool(...)`: {{JS_REPL_TOOL_LIST}}. MCP tools (if any) can also be called by name.

codex-rs/core/prompt.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ Within this context, Codex refers to the open-source agentic coding interface (n
1616
- `codex.sh` requires a string command and resolves to `{ stdout, stderr, exitCode }`; `codex.tool` executes a normal tool call and resolves to the raw tool output object.
1717
- Top-level bindings persist across cells. If you hit `SyntaxError: Identifier 'x' has already been declared`, reuse the binding, pick a new name, wrap in `{ ... }` for block scope, or reset the kernel.
1818
- Top-level static import declarations (for example `import x from "pkg"`) are currently unsupported in `js_repl`; use dynamic imports with `await import("pkg")` instead.
19+
<!-- js_repl_polling:start -->
20+
- Polling mode is two-step: (1) call `js_repl` with first-line pragma `// codex-js-repl: poll=true` to get an `exec_id`; (2) call `js_repl_poll` with that `exec_id` until `status` is `completed` or `error`.
21+
- `js_repl_poll` must not be called before a successful `js_repl` submission returns an `exec_id`.
22+
- If `js_repl` rejects your payload format, resend raw JS with the pragma; do not retry with JSON, quoted strings, or markdown fences.
23+
<!-- js_repl_polling:end -->
1924
<!-- js_repl_tools_only:start -->
2025
- Do not call tools directly; use `js_repl` + `codex.tool(...)`, and use `codex.sh(...)` for shell commands instead of direct shell tool calls.
2126
- Tools available via `codex.tool(...)`: {{JS_REPL_TOOL_LIST}}. MCP tools (if any) can also be called by name.

codex-rs/core/prompt_with_apply_patch_instructions.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ Within this context, Codex refers to the open-source agentic coding interface (n
1616
- `codex.sh` requires a string command and resolves to `{ stdout, stderr, exitCode }`; `codex.tool` executes a normal tool call and resolves to the raw tool output object.
1717
- Top-level bindings persist across cells. If you hit `SyntaxError: Identifier 'x' has already been declared`, reuse the binding, pick a new name, wrap in `{ ... }` for block scope, or reset the kernel.
1818
- Top-level static import declarations (for example `import x from "pkg"`) are currently unsupported in `js_repl`; use dynamic imports with `await import("pkg")` instead.
19+
<!-- js_repl_polling:start -->
20+
- Polling mode is two-step: (1) call `js_repl` with first-line pragma `// codex-js-repl: poll=true` to get an `exec_id`; (2) call `js_repl_poll` with that `exec_id` until `status` is `completed` or `error`.
21+
- `js_repl_poll` must not be called before a successful `js_repl` submission returns an `exec_id`.
22+
- If `js_repl` rejects your payload format, resend raw JS with the pragma; do not retry with JSON, quoted strings, or markdown fences.
23+
<!-- js_repl_polling:end -->
1924
<!-- js_repl_tools_only:start -->
2025
- Do not call tools directly; use `js_repl` + `codex.tool(...)`, and use `codex.sh(...)` for shell commands instead of direct shell tool calls.
2126
- Tools available via `codex.tool(...)`: {{JS_REPL_TOOL_LIST}}. MCP tools (if any) can also be called by name.

codex-rs/core/src/codex.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4709,7 +4709,11 @@ mod tests {
47094709
use crate::config::ConfigBuilder;
47104710
use crate::config::test_config;
47114711
use crate::exec::ExecToolCallOutput;
4712+
use crate::features::Feature;
47124713
use crate::function_tool::FunctionCallError;
4714+
use crate::models_manager::model_info::apply_js_repl_polling_section;
4715+
use crate::models_manager::model_info::apply_js_repl_section;
4716+
use crate::models_manager::model_info::apply_js_repl_tools_only_section;
47134717
use crate::shell::default_user_shell;
47144718
use crate::tools::format_exec_output_str;
47154719

@@ -4826,9 +4830,17 @@ mod tests {
48264830
];
48274831

48284832
let (session, _turn_context) = make_session_and_context().await;
4833+
let config = test_config();
4834+
let js_repl_enabled = config.features.enabled(Feature::JsRepl);
4835+
let prompt_with_apply_patch_instructions = apply_js_repl_tools_only_section(
4836+
&apply_js_repl_polling_section(
4837+
&apply_js_repl_section(prompt_with_apply_patch_instructions, js_repl_enabled),
4838+
js_repl_enabled && config.features.enabled(Feature::JsReplPolling),
4839+
),
4840+
js_repl_enabled && config.features.enabled(Feature::JsReplToolsOnly),
4841+
);
48294842

48304843
for test_case in test_cases {
4831-
let config = test_config();
48324844
let model_info = ModelsManager::construct_model_info_offline(test_case.slug, &config);
48334845
if test_case.expects_apply_patch_instructions {
48344846
assert_eq!(

codex-rs/core/src/features.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ pub enum Feature {
8080
// Experimental
8181
/// Enable JavaScript REPL tools backed by a persistent Node kernel.
8282
JsRepl,
83+
/// Enable js_repl polling helpers and tool.
84+
JsReplPolling,
8385
/// Only expose js_repl tools directly to the model.
8486
JsReplToolsOnly,
8587
/// Use the single unified PTY-backed exec tool.
@@ -326,6 +328,10 @@ impl Features {
326328
tracing::warn!("js_repl_tools_only requires js_repl; disabling js_repl_tools_only");
327329
features.disable(Feature::JsReplToolsOnly);
328330
}
331+
if features.enabled(Feature::JsReplPolling) && !features.enabled(Feature::JsRepl) {
332+
tracing::warn!("js_repl_polling requires js_repl; disabling js_repl_polling");
333+
features.disable(Feature::JsReplPolling);
334+
}
329335

330336
features
331337
}
@@ -432,6 +438,16 @@ pub const FEATURES: &[FeatureSpec] = &[
432438
},
433439
default_enabled: false,
434440
},
441+
FeatureSpec {
442+
id: Feature::JsReplPolling,
443+
key: "js_repl_polling",
444+
stage: Stage::Experimental {
445+
name: "JS REPL polling",
446+
menu_description: "Allow js_repl to return an exec_id and poll for completion.",
447+
announcement: "NEW! Try polling mode for JavaScript REPL long-running cells.",
448+
},
449+
default_enabled: false,
450+
},
435451
FeatureSpec {
436452
id: Feature::JsReplToolsOnly,
437453
key: "js_repl_tools_only",

codex-rs/core/src/models_manager/model_info.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ const GPT_5_2_CODEX_PERSONALITY_PRAGMATIC: &str =
3535
include_str!("../../templates/personalities/gpt-5.2-codex_pragmatic.md");
3636
const JS_REPL_SECTION_START: &str = "<!-- js_repl:start -->";
3737
const JS_REPL_SECTION_END: &str = "<!-- js_repl:end -->";
38+
const JS_REPL_POLLING_SECTION_START: &str = "<!-- js_repl_polling:start -->";
39+
const JS_REPL_POLLING_SECTION_END: &str = "<!-- js_repl_polling:end -->";
3840
const JS_REPL_TOOLS_ONLY_SECTION_START: &str = "<!-- js_repl_tools_only:start -->";
3941
const JS_REPL_TOOLS_ONLY_SECTION_END: &str = "<!-- js_repl_tools_only:end -->";
4042

@@ -115,6 +117,10 @@ pub(crate) fn with_config_overrides(mut model: ModelInfo, config: &Config) -> Mo
115117
&model.base_instructions,
116118
config.features.enabled(Feature::JsRepl),
117119
);
120+
model.base_instructions = apply_js_repl_polling_section(
121+
&model.base_instructions,
122+
config.features.enabled(Feature::JsRepl) && config.features.enabled(Feature::JsReplPolling),
123+
);
118124
model.base_instructions = apply_js_repl_tools_only_section(
119125
&model.base_instructions,
120126
config.features.enabled(Feature::JsRepl)
@@ -128,6 +134,15 @@ pub(crate) fn apply_js_repl_section(input: &str, enabled: bool) -> String {
128134
apply_section(input, JS_REPL_SECTION_START, JS_REPL_SECTION_END, enabled)
129135
}
130136

137+
pub(crate) fn apply_js_repl_polling_section(input: &str, enabled: bool) -> String {
138+
apply_section(
139+
input,
140+
JS_REPL_POLLING_SECTION_START,
141+
JS_REPL_POLLING_SECTION_END,
142+
enabled,
143+
)
144+
}
145+
131146
pub(crate) fn apply_js_repl_tools_only_section(input: &str, enabled: bool) -> String {
132147
apply_section(
133148
input,

0 commit comments

Comments
 (0)