Skip to content

Commit 5b5a5b9

Browse files
authored
Add config to disable /feedback (#8909)
Some enterprises do not want their users to be able to `/feedback`. <img width="395" height="325" alt="image" src="https://github.com/user-attachments/assets/2dae9c0b-20c3-4a15-bcd3-0187857ebbd8" /> Adds to `config.toml`: ```toml [feedback] enabled = false ``` I've deliberately decided to: 1. leave other references to `/feedback` (e.g. in the interrupt message, tips of the day) unchanged. I think we should continue to promote the feature even if it is not usable currently. 2. leave the `/feedback` menu item selectable and display an error saying it's disabled, rather than remove the menu item (which I believe would raise more questions). but happy to discuss these. This will be followed by a change to requirements.toml that admins can use to force the value of feedback.enabled.
1 parent ea56186 commit 5b5a5b9

File tree

9 files changed

+97
-0
lines changed

9 files changed

+97
-0
lines changed

codex-rs/app-server/src/codex_message_processor.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3678,6 +3678,16 @@ impl CodexMessageProcessor {
36783678
}
36793679

36803680
async fn upload_feedback(&self, request_id: RequestId, params: FeedbackUploadParams) {
3681+
if !self.config.feedback_enabled {
3682+
let error = JSONRPCErrorError {
3683+
code: INVALID_REQUEST_ERROR_CODE,
3684+
message: "sending feedback is disabled by configuration".to_string(),
3685+
data: None,
3686+
};
3687+
self.outgoing.send_error(request_id, error).await;
3688+
return;
3689+
}
3690+
36813691
let FeedbackUploadParams {
36823692
classification,
36833693
reason,

codex-rs/core/src/config/mod.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,10 @@ pub struct Config {
356356
/// Defaults to `true`.
357357
pub analytics: bool,
358358

359+
/// When `false`, disables feedback collection across Codex product surfaces.
360+
/// Defaults to `true`.
361+
pub feedback_enabled: bool,
362+
359363
/// OTEL configuration (exporter type, endpoint, headers, etc.).
360364
pub otel: crate::config::types::OtelConfig,
361365
}
@@ -820,6 +824,10 @@ pub struct ConfigToml {
820824
/// Defaults to `true`.
821825
pub analytics: Option<crate::config::types::AnalyticsConfigToml>,
822826

827+
/// When `false`, disables feedback collection across Codex product surfaces.
828+
/// Defaults to `true`.
829+
pub feedback: Option<crate::config::types::FeedbackConfigToml>,
830+
823831
/// OTEL configuration.
824832
pub otel: Option<crate::config::types::OtelConfigToml>,
825833

@@ -1403,6 +1411,11 @@ impl Config {
14031411
.and_then(|a| a.enabled)
14041412
.or(cfg.analytics.as_ref().and_then(|a| a.enabled))
14051413
.unwrap_or(true),
1414+
feedback_enabled: cfg
1415+
.feedback
1416+
.as_ref()
1417+
.and_then(|feedback| feedback.enabled)
1418+
.unwrap_or(true),
14061419
tui_notifications: cfg
14071420
.tui
14081421
.as_ref()
@@ -1559,6 +1572,7 @@ mod tests {
15591572
use crate::config::edit::ConfigEdit;
15601573
use crate::config::edit::ConfigEditsBuilder;
15611574
use crate::config::edit::apply_blocking;
1575+
use crate::config::types::FeedbackConfigToml;
15621576
use crate::config::types::HistoryPersistence;
15631577
use crate::config::types::McpServerTransportConfig;
15641578
use crate::config::types::Notifications;
@@ -1885,6 +1899,25 @@ trust_level = "trusted"
18851899
Ok(())
18861900
}
18871901

1902+
#[test]
1903+
fn feedback_enabled_defaults_to_true() -> std::io::Result<()> {
1904+
let codex_home = TempDir::new()?;
1905+
let cfg = ConfigToml {
1906+
feedback: Some(FeedbackConfigToml::default()),
1907+
..Default::default()
1908+
};
1909+
1910+
let config = Config::load_from_base_config_with_overrides(
1911+
cfg,
1912+
ConfigOverrides::default(),
1913+
codex_home.path().to_path_buf(),
1914+
)?;
1915+
1916+
assert_eq!(config.feedback_enabled, true);
1917+
1918+
Ok(())
1919+
}
1920+
18881921
#[test]
18891922
fn profile_legacy_toggles_override_base() -> std::io::Result<()> {
18901923
let codex_home = TempDir::new()?;
@@ -3234,6 +3267,7 @@ model_verbosity = "high"
32343267
animations: true,
32353268
show_tooltips: true,
32363269
analytics: true,
3270+
feedback_enabled: true,
32373271
tui_scroll_events_per_tick: None,
32383272
tui_scroll_wheel_lines: None,
32393273
tui_scroll_trackpad_lines: None,
@@ -3318,6 +3352,7 @@ model_verbosity = "high"
33183352
animations: true,
33193353
show_tooltips: true,
33203354
analytics: true,
3355+
feedback_enabled: true,
33213356
tui_scroll_events_per_tick: None,
33223357
tui_scroll_wheel_lines: None,
33233358
tui_scroll_trackpad_lines: None,
@@ -3417,6 +3452,7 @@ model_verbosity = "high"
34173452
animations: true,
34183453
show_tooltips: true,
34193454
analytics: false,
3455+
feedback_enabled: true,
34203456
tui_scroll_events_per_tick: None,
34213457
tui_scroll_wheel_lines: None,
34223458
tui_scroll_trackpad_lines: None,
@@ -3502,6 +3538,7 @@ model_verbosity = "high"
35023538
animations: true,
35033539
show_tooltips: true,
35043540
analytics: true,
3541+
feedback_enabled: true,
35053542
tui_scroll_events_per_tick: None,
35063543
tui_scroll_wheel_lines: None,
35073544
tui_scroll_trackpad_lines: None,

codex-rs/core/src/config/types.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,12 @@ pub struct AnalyticsConfigToml {
282282
pub enabled: Option<bool>,
283283
}
284284

285+
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
286+
pub struct FeedbackConfigToml {
287+
/// When `false`, disables the feedback flow across Codex product surfaces.
288+
pub enabled: Option<bool>,
289+
}
290+
285291
// ===== OTEL configuration =====
286292

287293
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]

codex-rs/tui/src/bottom_pane/feedback_view.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,21 @@ pub(crate) fn feedback_selection_params(
380380
}
381381
}
382382

383+
/// Build the selection popup params shown when feedback is disabled.
384+
pub(crate) fn feedback_disabled_params() -> super::SelectionViewParams {
385+
super::SelectionViewParams {
386+
title: Some("Sending feedback is disabled".to_string()),
387+
subtitle: Some("This action is disabled by configuration.".to_string()),
388+
footer_hint: Some(standard_popup_hint_line()),
389+
items: vec![super::SelectionItem {
390+
name: "Close".to_string(),
391+
dismiss_on_select: true,
392+
..Default::default()
393+
}],
394+
..Default::default()
395+
}
396+
}
397+
383398
fn make_feedback_item(
384399
app_event_tx: AppEventSender,
385400
name: &str,

codex-rs/tui/src/bottom_pane/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ mod prompt_args;
3434
mod skill_popup;
3535
pub(crate) use list_selection_view::SelectionViewParams;
3636
mod feedback_view;
37+
pub(crate) use feedback_view::feedback_disabled_params;
3738
pub(crate) use feedback_view::feedback_selection_params;
3839
pub(crate) use feedback_view::feedback_upload_consent_params;
3940
mod paste_burst;

codex-rs/tui/src/chatwidget.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1703,6 +1703,12 @@ impl ChatWidget {
17031703
}
17041704
match cmd {
17051705
SlashCommand::Feedback => {
1706+
if !self.config.feedback_enabled {
1707+
let params = crate::bottom_pane::feedback_disabled_params();
1708+
self.bottom_pane.show_selection_view(params);
1709+
self.request_redraw();
1710+
return;
1711+
}
17061712
// Step 1: pick a category (UI built in feedback_view)
17071713
let params =
17081714
crate::bottom_pane::feedback_selection_params(self.app_event_tx.clone());

codex-rs/tui2/src/bottom_pane/feedback_view.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,21 @@ pub(crate) fn feedback_selection_params(
380380
}
381381
}
382382

383+
/// Build the selection popup params shown when feedback is disabled.
384+
pub(crate) fn feedback_disabled_params() -> super::SelectionViewParams {
385+
super::SelectionViewParams {
386+
title: Some("Sending feedback is disabled".to_string()),
387+
subtitle: Some("This action is disabled by configuration.".to_string()),
388+
footer_hint: Some(standard_popup_hint_line()),
389+
items: vec![super::SelectionItem {
390+
name: "Close".to_string(),
391+
dismiss_on_select: true,
392+
..Default::default()
393+
}],
394+
..Default::default()
395+
}
396+
}
397+
383398
fn make_feedback_item(
384399
app_event_tx: AppEventSender,
385400
name: &str,

codex-rs/tui2/src/bottom_pane/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ mod prompt_args;
3232
mod skill_popup;
3333
pub(crate) use list_selection_view::SelectionViewParams;
3434
mod feedback_view;
35+
pub(crate) use feedback_view::feedback_disabled_params;
3536
pub(crate) use feedback_view::feedback_selection_params;
3637
pub(crate) use feedback_view::feedback_upload_consent_params;
3738
mod paste_burst;

codex-rs/tui2/src/chatwidget.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1537,6 +1537,12 @@ impl ChatWidget {
15371537
}
15381538
match cmd {
15391539
SlashCommand::Feedback => {
1540+
if !self.config.feedback_enabled {
1541+
let params = crate::bottom_pane::feedback_disabled_params();
1542+
self.bottom_pane.show_selection_view(params);
1543+
self.request_redraw();
1544+
return;
1545+
}
15401546
// Step 1: pick a category (UI built in feedback_view)
15411547
let params =
15421548
crate::bottom_pane::feedback_selection_params(self.app_event_tx.clone());

0 commit comments

Comments
 (0)