Skip to content

feat(codex-auth): add default_mode_request_user_input feature toggle - #911

Merged
Wibias merged 6 commits into
lidge-jun:devfrom
Wibias:codex/default-mode-request-user-input
Aug 3, 2026
Merged

feat(codex-auth): add default_mode_request_user_input feature toggle#911
Wibias merged 6 commits into
lidge-jun:devfrom
Wibias:codex/default-mode-request-user-input

Conversation

@Wibias

@Wibias Wibias commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Adds a Codex Auth page toggle for Codex's own default_mode_request_user_input feature flag. Enabling it adds [features] default_mode_request_user_input = true to $CODEX_HOME/config.toml via the official codex features enable|disable CLI (format-preserving, removed again when disabled), which lets Codex pause a Default-mode session and ask the user questions with the request_user_input tool.

What changed

  • src/codex/features.ts — new isDefaultModeRequestUserInputEnabled() reader for the [features] boolean form plus a shared DEFAULT_MODE_REQUEST_USER_INPUT_FEATURE_KEY constant. Missing file/key reads as false, matching the upstream default.
  • src/cli/v2.tscodexFeaturesInvocation now accepts any feature key (multi_agent_v2 remains the default), so the same win-exec/.cmd shim handling and runtime resolution serve both flags.
  • src/server/management/agent-settings-routes.ts — new GET/PUT /api/codex-auth/features/default-mode-request-user-input. PUT flips the flag through codex features (deps-injectable for tests) and returns 502 with the CLI's stderr when the installed Codex build does not know the flag yet, instead of silently writing an unknown key.
  • src/server/management/context.tstoggleDefaultModeRequestUserInput deps slot.
  • GUI — new DefaultModeRequestUserInputSetting card on the Codex Auth page showing the exact config.toml line it manages, with optimistic toggle, rollback on failure, load/retry state, and 30s polling. Copy is localized in en/de/ja/ko/zh/ru; new CSS follows the existing auto-switch card pattern.
  • Docs — feature-flag note added to the agents configuration reference.

Why

default_mode_request_user_input is an under-development upstream codex-rs feature (default off) that is not exposed anywhere in opencodex. The Codex Auth page is the natural surface: one switch, and the flag line is added to or removed from config.toml by the official CLI so the TOML edit stays upstream-owned and format-preserving.

Testing

  • New server tests: config reader forms, API GET/PUT round-trip through an injected toggle, non-boolean 400, and 502 postcondition failure (old/unknown flag).
  • New GUI tests: hydration gating, PUT round-trip with confirmation, failed-PUT rollback with error copy.
  • bun run typecheck, bun run lint:gui, bun run lint:i18n, bun run privacy:scan, bun run build:gui all green; GUI suite green.
  • Live smoke-tested against a real Codex 0.144.6 CLI: enable writes [features] default_mode_request_user_input = true, disable removes it.

Notes

  • The flag applies to new sessions; the toggle reports that and suggests restarting the Codex app.
  • If the installed Codex build predates the flag, the toggle fails loudly (502 with the CLI error) instead of pretending to work.

Summary by CodeRabbit

  • New Features

    • Added a Codex Auth setting to enable or disable asking for input in Default mode.
    • Changes synchronize with Codex configuration and apply to new sessions.
    • Added localized support in English, German, Japanese, Korean, Russian, and Chinese.
    • Added status feedback, retry handling, and automatic rollback when updates fail.
  • Documentation

    • Documented configuration behavior, session scope, and unsupported-version handling.
  • Bug Fixes

    • Improved Codex feature selection for greater command compatibility.

Adds a Codex Auth page toggle for Codex's own default_mode_request_user_input feature flag. Enabling it adds [features] default_mode_request_user_input = true to \/config.toml via the official codex features CLI (format-preserving, removed when disabled), letting Codex pause a Default-mode session and ask the user questions with request_user_input.

- features.ts: reads the flag from config.toml ([features] boolean form)
- cli/v2.ts: codexFeaturesInvocation now takes any feature key
- management API: GET/PUT /api/codex-auth/features/default-mode-request-user-input with deps injection and fail-loud 502 when the installed Codex build does not know the flag
- GUI: DefaultModeRequestUserInputSetting card on the Codex Auth page, i18n in en/de/ja/ko/zh/ru
- tests: config reader, API round-trip/validation/postcondition, GUI toggle tests
- docs: reference configuration note
@github-actions github-actions Bot added the enhancement New feature or request label Aug 2, 2026
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Wibias, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 18 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1de50e33-9b95-4eaf-8c99-e0a7e31444a3

📥 Commits

Reviewing files that changed from the base of the PR and between d9c34d0 and 3a32423.

📒 Files selected for processing (12)
  • gui/src/components/DefaultModeRequestUserInputSetting.tsx
  • gui/src/i18n/de.ts
  • gui/src/i18n/en.ts
  • gui/src/i18n/ja.ts
  • gui/src/i18n/ko.ts
  • gui/src/i18n/ru.ts
  • gui/src/i18n/zh.ts
  • gui/tests/codex-auth-request-user-input.test.tsx
  • src/cli/v2.ts
  • src/codex/features.ts
  • src/server/management/agent-settings-routes.ts
  • tests/codex-v2-gate.test.ts
📝 Walkthrough

Walkthrough

Added end-to-end support for Codex’s default_mode_request_user_input feature. The change includes config detection, CLI toggling, management GET/PUT routes, Codex Auth UI controls, localization, documentation, and tests.

Changes

Codex default-mode request-user-input toggle

Layer / File(s) Summary
Feature key, config reader, and CLI support
src/codex/features.ts, src/cli/v2.ts, src/server/management/agent-settings-routes.ts, tests/codex-v2-gate.test.ts
Adds the feature key and config reader. codexFeaturesInvocation accepts an optional feature name and retains multi_agent_v2 as the default. Shared CLI execution uses standard timeout and stdio settings. Tests cover configuration parsing and POSIX and Windows invocation paths.
Management API toggle flow
src/server/management/context.ts, src/server/management/agent-settings-routes.ts, tests/codex-v2-gate.test.ts, docs-site/src/content/docs/reference/configuration/agents.md
Adds GET/PUT handlers with boolean validation, injected or CLI-based toggling, state verification, HTTP 502 failures, new-session warnings, and API documentation. Tests cover persistence, validation, and ineffective toggles.
Codex Auth setting and localized feedback
gui/src/components/DefaultModeRequestUserInputSetting.tsx, gui/src/pages/CodexAuth.tsx, gui/src/styles.css, gui/src/i18n/*.ts, gui/tests/codex-auth-request-user-input.test.tsx
Adds periodic state loading, optimistic updates, rollback on failed updates, localized feedback, page integration, responsive styling, and interaction tests.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CodexAuthSetting
  participant AgentSettingsRoutes
  participant CodexFeatures
  participant CodexCLI
  CodexAuthSetting->>AgentSettingsRoutes: GET feature state
  AgentSettingsRoutes->>CodexFeatures: Read config.toml state
  CodexFeatures-->>AgentSettingsRoutes: Return enabled state and feature key
  CodexAuthSetting->>AgentSettingsRoutes: PUT enabled boolean
  AgentSettingsRoutes->>CodexCLI: Enable or disable feature
  CodexCLI-->>AgentSettingsRoutes: Return command result
  AgentSettingsRoutes->>CodexFeatures: Verify persisted state
  AgentSettingsRoutes-->>CodexAuthSetting: Return state or HTTP 502
Loading

Possibly related PRs

Suggested reviewers: ingwannu, lidge-jun

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the added Codex Auth feature toggle for default_mode_request_user_input.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 03f4b557db

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}
if (url.pathname === "/api/codex-auth/features/default-mode-request-user-input" && req.method === "PUT") {
let body: { enabled?: unknown };
try { body = await req.json(); } catch { return jsonResponse({ error: "invalid JSON body" }, 400); }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bound the new management request body

When this endpoint receives a chunked request without Content-Length, req.json() buffers the entire body and bypasses the management API's 4 MiB decompressed-body limit, allowing an oversized request to exhaust memory. Parse it with readManagementJsonBody(req) and rethrow DecompressedBodyTooLargeError, as the other management routes do, so the dispatcher can return 413.

AGENTS.md reference: src/AGENTS.md:L20-L20

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[shipping-github] Fixed in 3a324231c: the PUT now parses through readManagementJsonBody (4 MiB bound, size sentinel rethrown so the dispatcher returns 413) and rejects null/array/non-object bodies with 400 before touching enabled. Regression tests added for null, array, and oversized chunked bodies.

Comment on lines +323 to +325
const inv = codexFeaturesInvocation(enabled ? "enable" : "disable", DEFAULT_MODE_REQUEST_USER_INPUT_FEATURE_KEY);
execFileSync(inv.file, inv.args,
{ stdio: ["ignore", "pipe", "pipe"], timeout: 15_000, windowsHide: true, ...inv.options });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Run Codex against the same resolved home

When opencodex is running in WSL without an explicit CODEX_HOME and auto-detects the sole Windows-mounted .codex directory, the reader checks that Windows config, but this child inherits no CODEX_HOME, so Codex uses the Linux ~/.codex instead. The toggle can therefore mutate the wrong config and then return 502 because its postcondition checks the unchanged Windows file; pass the resolved home in the child environment.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[shipping-github] Fixed in 3a324231c: runCodexFeaturesCommand now passes the reader's resolved home (dirname(activeCodexConfigPath())) as CODEX_HOME in the child environment, so the CLI toggles the same config the postcondition re-reads — including the WSL Windows-home detection path. No-op when the reader and CLI already agree.

Comment on lines +28 to +29
enabledRef.current = payload.enabled === true;
setEnabled(enabledRef.current);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Ignore poll results that predate a toggle

If the 30-second GET starts just before the user toggles and its stale response resolves during or after the PUT, these assignments overwrite enabledRef and the rendered state with the pre-toggle value. The next click can then submit the opposite of the persisted state; track a request revision or defer/ignore reads that overlap a save, following the existing auto-switch controller's reconciliation pattern.

AGENTS.md reference: gui/AGENTS.md:L9-L10

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[shipping-github] Fixed in 3a324231c: load() now tracks a generation ref; GETs that were in flight when a save started are ignored after await, so a stale poll can no longer revert the optimistic state. New GUI test covers the overlap window (poll resolves mid-PUT).

Comment thread gui/src/i18n/en.ts Outdated
"codexAuth.autoSwitchUpdateFailed": "The usage-based switching update could not be confirmed. The last confirmed value is shown.",
"codexAuth.requestUserInput": "Ask for input in Default mode",
"codexAuth.requestUserInputDesc": "Lets Codex pause a Default-mode session and ask you questions with the request_user_input tool.",
"codexAuth.requestUserInputConfig": "config.toml: [features] default_mode_request_user_input = true",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Render the TOML snippet as literal multiline code

This value presents [features] default_mode_request_user_input = true as one copyable line, but TOML requires the table header and assignment on separate lines, so pasting the displayed snippet makes config.toml invalid. Render a literal <pre>/<code> snippet containing [features]\ndefault_mode_request_user_input = true instead of duplicating this machine text across locale files.

AGENTS.md reference: gui/AGENTS.md:L22-L25

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[shipping-github] Fixed in 3a324231c: the card renders a literal two-line <code> snippet ([features]\ndefault_mode_request_user_input = true), and the duplicated machine text was removed from all six locale files per gui/AGENTS.md (technical/machine text does not belong in locale files).

if (url.pathname === "/api/codex-auth/features/default-mode-request-user-input" && req.method === "PUT") {
let body: { enabled?: unknown };
try { body = await req.json(); } catch { return jsonResponse({ error: "invalid JSON body" }, 400); }
if (typeof body.enabled !== "boolean") return jsonResponse({ error: "body.enabled must be a boolean" }, 400);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject a null JSON payload before dereferencing it

When a client sends the valid JSON body null, parsing succeeds and body.enabled then throws a TypeError; the management dispatcher only converts its specific bounded-body and busy errors, so this malformed request escapes as an internal server failure instead of a 400. Validate that the parsed value is a non-null plain object before reading enabled.

AGENTS.md reference: src/AGENTS.md:L17-L17

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[shipping-github] Fixed in 3a324231c: covered by the same bounded-parser change as the body-size finding — a JSON null body now returns 400 before any property dereference. Regression test added.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@gui/src/components/DefaultModeRequestUserInputSetting.tsx`:
- Around line 23-44: Guard the successful response handling in load so it does
not overwrite the optimistic toggle state while savingRef.current is true. In
the load callback, only update enabledRef.current and call setEnabled when no
save is in progress, while preserving hydration and existing error behavior.
- Around line 56-66: Update the PUT response handling in
DefaultModeRequestUserInputSetting to parse an optional warnings string array,
preserve the localized success message, and render any non-empty warnings
returned by the server. Extend the relevant component test to cover a response
containing at least one warning and verify it is displayed.

In `@src/server/management/agent-settings-routes.ts`:
- Around line 312-315: Update the PUT handler for
/api/codex-auth/features/default-mode-request-user-input to parse the request
through readManagementJsonBody() as unknown, preserving and rethrowing its size
sentinel. Validate that the parsed value is a non-null, non-array object before
accessing enabled, returning 400 for invalid bodies, and retain the boolean
validation for body.enabled. Add regression coverage for null and oversized
chunked bodies.

In `@tests/codex-v2-gate.test.ts`:
- Around line 893-898: Remove the duplicate deps object from both
handleManagementAPI calls in tests/codex-v2-gate.test.ts at lines 893-898 and
906-911, leaving each call with exactly the request, URL, config, and single
deps arguments.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 606bac8a-c953-4737-a8df-45c092ddc90e

📥 Commits

Reviewing files that changed from the base of the PR and between 4a0d038 and 03f4b55.

📒 Files selected for processing (16)
  • docs-site/src/content/docs/reference/configuration/agents.md
  • gui/src/components/DefaultModeRequestUserInputSetting.tsx
  • gui/src/i18n/de.ts
  • gui/src/i18n/en.ts
  • gui/src/i18n/ja.ts
  • gui/src/i18n/ko.ts
  • gui/src/i18n/ru.ts
  • gui/src/i18n/zh.ts
  • gui/src/pages/CodexAuth.tsx
  • gui/src/styles.css
  • gui/tests/codex-auth-request-user-input.test.tsx
  • src/cli/v2.ts
  • src/codex/features.ts
  • src/server/management/agent-settings-routes.ts
  • src/server/management/context.ts
  • tests/codex-v2-gate.test.ts

Comment thread gui/src/components/DefaultModeRequestUserInputSetting.tsx
Comment thread gui/src/components/DefaultModeRequestUserInputSetting.tsx Outdated
Comment thread src/server/management/agent-settings-routes.ts
Comment thread tests/codex-v2-gate.test.ts
The PUT response body was parsed before res.ok was checked, which react-doctor flags as no-fetch-response-used-without-status-check. Use the repo's readJsonOrThrow helper so non-OK responses are handled before the body is consumed (error copy still surfaces from the server payload).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@gui/src/components/DefaultModeRequestUserInputSetting.tsx`:
- Around line 62-63: Update the request-user-input update catch block around
readJsonOrThrow to preserve the caught error’s message, displaying it when
available and falling back to the localized requestUserInputUpdateFailed string
otherwise. Apply the same handling to both affected error paths while keeping
the existing failure flow intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4087a4de-8fae-4793-ab63-9c6cae3786bb

📥 Commits

Reviewing files that changed from the base of the PR and between 03f4b55 and 8d155e3.

📒 Files selected for processing (1)
  • gui/src/components/DefaultModeRequestUserInputSetting.tsx

Comment thread gui/src/components/DefaultModeRequestUserInputSetting.tsx Outdated
…UI poll

Extract runCodexFeaturesCommand so the /api/v2 and default_mode_request_user_input PUT handlers share one execFileSync fallback (same invocation builder, timeout, and stdio options). The GUI load poll now skips while a PUT is in flight so the 30s refresh cannot revert the optimistic toggle. Adds a POSIX invocation assertion for the new feature key.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
gui/src/components/DefaultModeRequestUserInputSetting.tsx (1)

71-74: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve the server error detail in failure feedback.

readJsonOrThrow at Line 65 can contain the management API's 502 reason, but the empty catch discards it. An unsupported Codex build therefore produces only the generic localized message.

Use the caught error message when available and fall back to codexAuth.requestUserInputUpdateFailed when it is not. Add a test for the 502 response path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gui/src/components/DefaultModeRequestUserInputSetting.tsx` around lines 71 -
74, The catch handling around readJsonOrThrow in
DefaultModeRequestUserInputSetting must preserve server error details in the
feedback message. Capture the caught error, use its message when available, and
otherwise fall back to t("codexAuth.requestUserInputUpdateFailed"); retain the
rollback behavior for enabledRef and setEnabled, and add coverage for the 502
response path.
src/server/management/agent-settings-routes.ts (1)

319-325: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve Codex CLI stderr on toggle failures.

runCodexFeaturesCommand does not set an encoding, so Bun 1.3.14 exposes piped error.stderr as a Buffer. Decode Buffer/Uint8Array values or set encoding: "utf8", then add a regression test that asserts the 502 response includes the CLI diagnostic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/management/agent-settings-routes.ts` around lines 319 - 325,
Update the error handling around runCodexFeaturesCommand in the toggle flow to
preserve CLI diagnostics when error.stderr is a Buffer or Uint8Array by decoding
it as UTF-8, or configure the command with encoding: "utf8". Keep the existing
trimmed stderr/message fallback behavior, and add a regression test asserting
the 502 response contains the CLI diagnostic.
♻️ Duplicate comments (3)
gui/src/components/DefaultModeRequestUserInputSetting.tsx (2)

65-70: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Show the new-session warning through localized UI state.

src/server/management/agent-settings-routes.ts, Lines 333-337, returns changed and a non-empty warnings array when the setting changes. These lines omit both fields and always show only the success message. The user therefore receives no notice that the setting applies only to new sessions.

Use payload.changed or a stable warning code to select a new localized message. Do not render raw server warning text. Add a component test for the changed response.

As per path instructions, GUI state changes must stay consistent with management API responses, and user-visible strings must go through the i18n locale files.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gui/src/components/DefaultModeRequestUserInputSetting.tsx` around lines 65 -
70, Update the response handling in DefaultModeRequestUserInputSetting to read
the management API’s changed or stable warning indicator and select a localized
new-session warning when the setting changed, while retaining the existing
success message otherwise. Do not render raw warning text; add the corresponding
translation to the locale files and cover the changed-response behavior with a
component test.

Source: Path instructions


24-39: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Invalidate in-flight loads when a save starts.

Line 27 checks savingRef.current only before fetch(). A GET that started before the click can resolve after the click and execute Lines 32-33 with stale server data. If it resolves after the PUT, it can overwrite the final state until the next poll.

Track a load/save generation or abort the request. Check the generation after await res.json() and before updating either state or load-error values.

Proposed guard
+  const loadGenerationRef = useRef(0);
+
   const load = useCallback(async () => {
     if (savingRef.current) return;
+    const generation = ++loadGenerationRef.current;
     try {
       const res = await fetch(`${apiBase}${FEATURE_ENDPOINT}`);
       if (!res.ok) throw new Error("load");
       const payload = await res.json() as { enabled?: unknown };
+      if (savingRef.current || generation !== loadGenerationRef.current) return;
       enabledRef.current = payload.enabled === true;
       setEnabled(enabledRef.current);
       setHydrated(true);
       setLoadError(false);
     } catch {
-      if (!savingRef.current) setLoadError(true);
+      if (!savingRef.current && generation === loadGenerationRef.current) setLoadError(true);
     }
   }, [apiBase]);

   const toggle = useCallback(async () => {
     if (savingRef.current || !hydrated || loadError) return;
+    loadGenerationRef.current++;

As per path instructions, GUI state changes must stay consistent with management API responses.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gui/src/components/DefaultModeRequestUserInputSetting.tsx` around lines 24 -
39, Invalidate in-flight loads when saving begins in the load callback and save
flow of DefaultModeRequestUserInputSetting. Track a load/save generation or
abort the GET, then verify it after await res.json() and before updating
enabledRef, enabled, hydrated, or load-error state; preserve updates only for
the current generation and keep state changes aligned with the management API
response.

Source: Path instructions

src/server/management/agent-settings-routes.ts (1)

307-310: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Use the bounded management parser for this PUT body.

Line 309 calls req.json() directly. Chunked requests can therefore bypass the management body-size limit. A JSON null body also makes Line 310 throw instead of returning 400.

Parse as unknown with readManagementJsonBody(), rethrow its size sentinel, reject null, arrays, and non-objects, then validate enabled. Add regression tests for null and oversized chunked bodies.

Proposed fix
-    let body: { enabled?: unknown };
-    try { body = await req.json(); } catch { return jsonResponse({ error: "invalid JSON body" }, 400); }
+    let parsedBody: unknown;
+    try {
+      parsedBody = await readManagementJsonBody(req);
+    } catch (error) {
+      rethrowManagementBodyTooLarge(error);
+      return jsonResponse({ error: "invalid JSON body" }, 400);
+    }
+    if (!parsedBody || typeof parsedBody !== "object" || Array.isArray(parsedBody)) {
+      return jsonResponse({ error: "body must be a JSON object" }, 400);
+    }
+    const body = parsedBody as { enabled?: unknown };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/server/management/agent-settings-routes.ts` around lines 307 - 310,
Update the PUT handler for
/api/codex-auth/features/default-mode-request-user-input to parse the request
through readManagementJsonBody() as unknown, preserving and rethrowing its
body-size sentinel. Reject null, arrays, and non-object bodies with a 400
response before validating body.enabled as a boolean, and add regression
coverage for null and oversized chunked payloads.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/cli/v2.ts`:
- Around line 57-69: Update runCodexFeaturesCommand to execute the Codex process
asynchronously without blocking the Bun event loop, preserving the existing
invocation options and 15-second timeout. Propagate async/await through
transitionMultiAgentV2 and its callers, and perform feature-state verification
only after the bounded process completes.

---

Outside diff comments:
In `@gui/src/components/DefaultModeRequestUserInputSetting.tsx`:
- Around line 71-74: The catch handling around readJsonOrThrow in
DefaultModeRequestUserInputSetting must preserve server error details in the
feedback message. Capture the caught error, use its message when available, and
otherwise fall back to t("codexAuth.requestUserInputUpdateFailed"); retain the
rollback behavior for enabledRef and setEnabled, and add coverage for the 502
response path.

In `@src/server/management/agent-settings-routes.ts`:
- Around line 319-325: Update the error handling around runCodexFeaturesCommand
in the toggle flow to preserve CLI diagnostics when error.stderr is a Buffer or
Uint8Array by decoding it as UTF-8, or configure the command with encoding:
"utf8". Keep the existing trimmed stderr/message fallback behavior, and add a
regression test asserting the 502 response contains the CLI diagnostic.

---

Duplicate comments:
In `@gui/src/components/DefaultModeRequestUserInputSetting.tsx`:
- Around line 65-70: Update the response handling in
DefaultModeRequestUserInputSetting to read the management API’s changed or
stable warning indicator and select a localized new-session warning when the
setting changed, while retaining the existing success message otherwise. Do not
render raw warning text; add the corresponding translation to the locale files
and cover the changed-response behavior with a component test.
- Around line 24-39: Invalidate in-flight loads when saving begins in the load
callback and save flow of DefaultModeRequestUserInputSetting. Track a load/save
generation or abort the GET, then verify it after await res.json() and before
updating enabledRef, enabled, hydrated, or load-error state; preserve updates
only for the current generation and keep state changes aligned with the
management API response.

In `@src/server/management/agent-settings-routes.ts`:
- Around line 307-310: Update the PUT handler for
/api/codex-auth/features/default-mode-request-user-input to parse the request
through readManagementJsonBody() as unknown, preserving and rethrowing its
body-size sentinel. Reject null, arrays, and non-object bodies with a 400
response before validating body.enabled as a boolean, and add regression
coverage for null and oversized chunked payloads.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e3b06fb5-3148-4a72-a252-5a7147685ba0

📥 Commits

Reviewing files that changed from the base of the PR and between 8d155e3 and d9c34d0.

📒 Files selected for processing (4)
  • gui/src/components/DefaultModeRequestUserInputSetting.tsx
  • src/cli/v2.ts
  • src/server/management/agent-settings-routes.ts
  • tests/codex-v2-gate.test.ts

Comment thread src/cli/v2.ts Outdated
Comment on lines +57 to +69
/**
* Run `codex features <action> <feature>` synchronously - the management API
* fallback when no deps toggle is injected. Shares the invocation builder and
* the bounded timeout/stdio options so every production toggle path behaves
* identically.
*/
export function runCodexFeaturesCommand(
action: "enable" | "disable",
feature: string = "multi_agent_v2",
): void {
const inv = codexFeaturesInvocation(action, feature);
execFileSync(inv.file, inv.args,
{ stdio: ["ignore", "pipe", "pipe"], timeout: 15_000, windowsHide: true, ...inv.options });

@coderabbitai coderabbitai Bot Aug 2, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- file inventory ---'
git ls-files 'src/cli/v2.ts' 'src/**' | sed -n '1,120p'
printf '%s\n' '--- outline ---'
ast-grep outline src/cli/v2.ts --view compact || true
printf '%s\n' '--- target source ---'
sed -n '1,180p' src/cli/v2.ts
printf '%s\n' '--- helper and call sites ---'
rg -n -C 4 'runCodexFeaturesCommand|codexFeaturesInvocation|execFileSync|features' src

Repository: lidge-jun/opencodex

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- management route ---'
sed -n '205,305p' src/server/management/agent-settings-routes.ts
printf '%s\n' '--- feature transition implementation ---'
rg -n -A 100 -B 15 'export function transitionMultiAgentV2|function transitionMultiAgentV2' src/codex/features.ts
printf '%s\n' '--- route registration and handler shape ---'
rg -n -A 8 -B 8 'agent-settings-routes|handle.*Request|serve\\(|fetch\\(|agentSettings' src/server src
printf '%s\n' '--- relevant tests ---'
rg -n -C 5 'runCodexFeaturesCommand|toggleCodexMultiAgentV2|transitionMultiAgentV2|/api/v2' test tests src --glob '*test*' --glob '*spec*' || true
printf '%s\n' '--- read-only call-chain verifier ---'
python3 - <<'PY'
from pathlib import Path
import re

v2 = Path("src/cli/v2.ts").read_text()
route = Path("src/server/management/agent-settings-routes.ts").read_text()
features = Path("src/codex/features.ts").read_text()

checks = {
    "v2 imports execFileSync": r'import\s+\{\s*execFileSync\s*\}\s+from\s+"node:child_process"',
    "management fallback imports runCodexFeaturesCommand": r'const\s+\{\s*runCodexFeaturesCommand\s*\}\s*=\s*await import\("\.\./\.\./cli/v2"\)',
    "fallback invokes synchronously": r'toggle\s*=\s*\(enabled:\s*boolean\)\s*=>\s*runCodexFeaturesCommand',
    "runCodexFeaturesCommand invokes execFileSync": r'export function runCodexFeaturesCommand[\s\S]{0,500}?execFileSync\(',
    "sync timeout is 15 seconds": r'runCodexFeaturesCommand[\s\S]{0,600}?timeout:\s*15_000',
    "transition accepts synchronous toggle": r'export function transitionMultiAgentV2[\s\S]{0,500}?toggle',
}
for name, pattern in checks.items():
    source = route if "management" in name or "fallback" in name else v2 if "v2" in name or "runCodex" in name or "timeout" in name else features
    print(f"{name}: {'YES' if re.search(pattern, source) else 'NO'}")

m = re.search(r'if \(requestedFlag !== undefined \|\| wantsThreads\) \{([\s\S]{0,2500})', route)
if m:
    body = m.group(1)
    print("route transition block contains await before transition:",
          "YES" if re.search(r'\bawait\b', body[:body.find("transitionMultiAgentV2")] if "transitionMultiAgentV2" in body else "") else "NO")
PY

Repository: lidge-jun/opencodex

Length of output: 12261


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- route function and request dispatch ---'
sed -n '1,90p' src/server/management/agent-settings-routes.ts
sed -n '300,345p' src/server/management/agent-settings-routes.ts
rg -n -F -C 6 'agent-settings-routes' src/server src
rg -n -F -C 6 'handleManagement' src/server src
printf '%s\n' '--- exact source line numbers ---'
nl -ba src/cli/v2.ts | sed -n '50,90p'
nl -ba src/server/management/agent-settings-routes.ts | sed -n '224,290p'
nl -ba src/codex/features.ts | sed -n '911,987p'
printf '%s\n' '--- corrected read-only call-chain verifier ---'
python3 - <<'PY'
from pathlib import Path
import re

v2 = Path("src/cli/v2.ts").read_text()
route = Path("src/server/management/agent-settings-routes.ts").read_text()
features = Path("src/codex/features.ts").read_text()

checks = [
    ("v2 imports execFileSync", v2, r'import\s+\{\s*execFileSync\s*\}\s+from\s+"node:child_process"'),
    ("management fallback imports runCodexFeaturesCommand", route, r'runCodexFeaturesCommand\s*\}\s*=\s*await import\("\.\./\.\./cli/v2"\)'),
    ("management fallback invokes runCodexFeaturesCommand", route, r'toggle\s*=\s*\(enabled:\s*boolean\)\s*=>\s*runCodexFeaturesCommand'),
    ("runCodexFeaturesCommand invokes execFileSync", v2, r'export function runCodexFeaturesCommand[\s\S]{0,600}?execFileSync\('),
    ("runCodexFeaturesCommand has 15 second timeout", v2, r'export function runCodexFeaturesCommand[\s\S]{0,700}?timeout:\s*15_000'),
    ("transition toggle parameter is synchronous", features, r'toggleFeature:\s*\(enabled:\s*boolean\)\s*=>\s*void'),
    ("transition calls toggle before postcondition", features, r'toggleFeature\(true\)[\s\S]{0,160}?isMultiAgentV2Enabled\(path\)'),
]
for name, source, pattern in checks:
    print(f"{name}: {'YES' if re.search(pattern, source) else 'NO'}")

transition_start = features.index("export function transitionMultiAgentV2")
transition = features[transition_start:]
print("transition contains await:", "YES" if re.search(r"\bawait\b", transition[:transition.find("\n}") if "\n}" in transition else len(transition)]) else "NO")
PY

Repository: lidge-jun/opencodex

Length of output: 22265


Move Codex feature execution off the Bun event loop.

src/server/management/agent-settings-routes.ts:235-240 calls runCodexFeaturesCommand through synchronous transitionMultiAgentV2. That helper calls execFileSync with a 15-second timeout, blocking unrelated requests while Codex runs. Make the process execution asynchronous, or move it to a worker. Propagate await through transitionMultiAgentV2 and perform state verification only after the bounded process result is available.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli/v2.ts` around lines 57 - 69, Update runCodexFeaturesCommand to
execute the Codex process asynchronously without blocking the Bun event loop,
preserving the existing invocation options and 15-second timeout. Propagate
async/await through transitionMultiAgentV2 and its callers, and perform
feature-state verification only after the bounded process completes.

Source: Path instructions

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[shipping-github] Declined with rationale: the synchronous execFileSync (15s bound) is the pre-existing contract shared with the /api/v2 toggle through transitionMultiAgentV2; making it async would ripple through the CLI command path and the existing route. This PR's new endpoint deliberately matches the established subsystem pattern. Tracked as a follow-up recommendation in the full review rather than an in-PR refactor.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Wibias Understood. The new endpoint follows the existing synchronous feature-toggle contract, including the 15-second bound.

The event-loop blocking concern remains a valid follow-up item, but it is outside the scope of this PR. No change is required here.

🐇

You are interacting with an AI system.

Wibias commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator Author

[shipping-github] Verdict: approve-comment

PR: #911 — feat(codex-auth): add default_mode_request_user_input feature toggle
Head: 3a324231c on dev (mergeStateStatus: CLEAN)

Semantic propagation

  • Concepts audited: default_mode_request_user_input feature flag; the codex features invocation family (multi_agent_v2 default + new key); management GET/PUT surface; GUI toggle state.
  • Authoritative sources: upstream codex-rs FeatureSpec (boolean form, default off, smoke-tested on 0.144.6); src/codex/features.ts reader + DEFAULT_MODE_REQUEST_USER_INPUT_FEATURE_KEY; src/cli/v2.ts invocation builder.
  • Producers and consumers checked: runCodexFeaturesCommand, injected route toggle, GET/PUT routes, GUI card, 6 locales, docs agents.md; all in-repo codexFeaturesInvocation callers updated; the export is internal-only (package exports exposes only the root entry).
  • Public/derived representations checked: config.toml line; GET {enabled,key}; PUT {ok,enabled,changed,warnings} plus 400/413/502 errors; GUI card copy; i18n parity; docs.
  • Material variant partitions checked: both feature keys across POSIX and win32 .cmd invocation paths; TOML boolean form; enabled/disabled/no-change toggle paths; all 6 locales.
  • Positive and negative assertions checked: boolean parse true/false/missing/other-key; non-boolean 400 before toggle; null/array/non-object 400; oversized chunked 413; 502 postcondition; 502 CLI diagnostic; stale-poll race; restart hint changed/unchanged.
  • Unmapped surfaces: none.
  • Unproven equivalence assumptions: table/inline TOML forms of the new flag are not read (upstream ships the boolean form) - residual, not blocking.
  • Representation mismatches: none.
  • Variant coverage gaps: concurrent PUTs from multiple clients (pre-existing pattern); synchronous CLI toggle blocks the event loop up to 15s (pre-existing /api/v2 contract) - residual, follow-up recommendation.
  • Axis verdict: pass

Linked: none

Usefulness

Real value: exposes an otherwise unmanageable upstream Codex flag through the Codex Auth page, keeps the TOML edit upstream-owned via codex features, and ships with reader/toggle tests, GUI coverage, i18n, and docs. Useful.

Bugs / correctness

  • Method: bug-review.md - Bugbot n/a (Codex host); complementary done (silent_failures, resource_leaks, edge_cases, lock/error-mapping probes).
  • Fixed this session (43f65d18, f0e22138, merged in 3a324231c): bounded PUT body with 400 for null/array/non-object and 413 for oversized chunked; CLI stderr preserved (Buffer decode + encoding: "utf8"); child CODEX_HOME aligned with the reader (WSL case); GUI generation guard for in-flight GETs; server 502 reason surfaced in the GUI; localized restart hint on change; features.ts scope doc updated; machine TOML text removed from all locale files.
  • Residual: event-loop-blocking execFileSync (pre-existing shared contract, async refactor recommended as follow-up); concurrent PUT race (pre-existing).

Security

  • Scope reviewed: management auth boundary (new endpoints sit behind requireManagementAuth and the origin gate), body-size handling, CLI arg construction (feature keys are compile-time constants), config-file trust, stderr echo to the authenticated management client.
  • Findings: none Critical/High confirmed. The Medium body-size/DoS vector and the lost CLI diagnostics were fixed in this round.

Spec / standards

  • Spec source: PR body (no linked issue).
  • Gaps: none - the PR's "fails loudly with the CLI's stderr" and "suggests restarting the app" claims now match the implementation; gui/AGENTS.md machine-text rule restored.

Reviews

  • Owners/maintainers: none open. Requested reviewers @Ingwannu and @lidge-jun still pending - MAINTAINERS.md requires one maintainer approval before merge and authors cannot self-approve.
  • Bots: 10 threads handled (9 fixed, 1 declined with rationale); CodeRabbit confirmed the fixes on-thread.

Base / CI

  • Behind/conflicts: clean - branch merged current dev (84efd2460) into 3a324231c.
  • Required checks: green on 3a324231c (ubuntu, macos, windows, npm-global x3, label, enforce-target, react-doctor).
  • Local tip compile/tests: bun run typecheck green; focused server tests 83/84 (1 pre-existing machine-environment failure: a real global codex.cmd on the local box; CI passes); GUI suite 529/530 (1 pre-existing timer flake that passes in isolation); lint, lint:i18n, privacy:scan, GUI build green.

Gate

none - not draft/WIP. Merge still awaits one maintainer approval per repository policy.

Bottom line

Useful, well-tested, and now hardened: all confirmed findings fixed and verified, CI green on the current head, base synchronized. Ready for the required maintainer review.

@Wibias
Wibias merged commit c72acb3 into lidge-jun:dev Aug 3, 2026
11 checks passed
@Wibias
Wibias deleted the codex/default-mode-request-user-input branch August 3, 2026 02:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant