Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions codex-rs/core/src/codex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ use crate::protocol::ExecCommandBeginEvent;
use crate::protocol::ExecCommandEndEvent;
use crate::protocol::FileChange;
use crate::protocol::InputItem;
use crate::protocol::InputMessageKind;
use crate::protocol::ListCustomPromptsResponseEvent;
use crate::protocol::Op;
use crate::protocol::PatchApplyBeginEvent;
Expand All @@ -102,6 +103,7 @@ use crate::protocol::Submission;
use crate::protocol::TaskCompleteEvent;
use crate::protocol::TokenUsageInfo;
use crate::protocol::TurnDiffEvent;
use crate::protocol::UserMessageEvent;
use crate::protocol::WebSearchBeginEvent;
use crate::rollout::RolloutRecorder;
use crate::rollout::RolloutRecorderParams;
Expand Down Expand Up @@ -1083,7 +1085,8 @@ async fn submission_loop(
let mut turn_context = Arc::new(turn_context);
// To break out of this loop, send Op::Shutdown.
while let Ok(sub) = rx_sub.recv().await {
debug!(?sub, "Submission");
// Avoid logging full submission payloads to prevent leaking prompt or template text.
debug!("Submission received: id={}", sub.id);
match sub.op {
Op::Interrupt => {
sess.interrupt_task();
Expand Down Expand Up @@ -1262,12 +1265,22 @@ async fn submission_loop(
Op::AddToHistory { text } => {
let id = sess.conversation_id;
let config = config.clone();
let text_for_history = text.clone();
tokio::spawn(async move {
if let Err(e) = crate::message_history::append_entry(&text, &id, &config).await
if let Err(e) =
crate::message_history::append_entry(&text_for_history, &id, &config).await
{
warn!("failed to append to message history: {e}");
}
});

// Persist a transcript-only user message in rollout so resume displays
// exactly what the user saw in the transcript. Do not send to UI to avoid duplicates.
let rollout_item = RolloutItem::EventMsg(EventMsg::UserMessage(UserMessageEvent {
message: text,
kind: Some(InputMessageKind::Plain),
}));
sess.persist_rollout_items(&[rollout_item]).await;
}

Op::GetHistoryEntryRequest { offset, log_id } => {
Expand Down
38 changes: 38 additions & 0 deletions codex-rs/core/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ pub struct Config {
/// All characters are inserted as they are received, and no buffering
/// or placeholder replacement will occur for fast keypress bursts.
pub disable_paste_burst: bool,
/// When true (default), redact saved prompt bodies in transcript and show
/// only the typed command (e.g., "/saved-prompt"). When false, show full body.
pub redact_saved_prompt_body: bool,
}

impl Config {
Expand Down Expand Up @@ -485,6 +488,10 @@ pub struct ConfigToml {
/// All characters are inserted as they are received, and no buffering
/// or placeholder replacement will occur for fast keypress bursts.
pub disable_paste_burst: Option<bool>,

/// When true, the UI transcript will redact the body of saved prompts and
/// display only the typed command (e.g., "/mdc"). Defaults to true.
pub redact_saved_prompt_body: Option<bool>,
}

impl From<ConfigToml> for UserSavedConfig {
Expand Down Expand Up @@ -624,6 +631,7 @@ pub struct ConfigOverrides {
pub include_view_image_tool: Option<bool>,
pub show_raw_agent_reasoning: Option<bool>,
pub tools_web_search_request: Option<bool>,
pub redact_saved_prompt_body: Option<bool>,
}

impl Config {
Expand Down Expand Up @@ -651,6 +659,7 @@ impl Config {
include_view_image_tool,
show_raw_agent_reasoning,
tools_web_search_request: override_tools_web_search_request,
redact_saved_prompt_body: _,
} = overrides;

let config_profile = match config_profile_key.as_ref().or(cfg.profile.as_ref()) {
Expand Down Expand Up @@ -720,6 +729,11 @@ impl Config {
.or(cfg.tools.as_ref().and_then(|t| t.view_image))
.unwrap_or(true);

let redact_saved_prompt_body = overrides
.redact_saved_prompt_body
.or(cfg.redact_saved_prompt_body)
.unwrap_or(true);

let model = model
.or(config_profile.model)
.or(cfg.model)
Expand Down Expand Up @@ -820,6 +834,7 @@ impl Config {
.unwrap_or(false),
include_view_image_tool,
disable_paste_burst: cfg.disable_paste_burst.unwrap_or(false),
redact_saved_prompt_body,
};
Ok(config)
}
Expand Down Expand Up @@ -1194,6 +1209,7 @@ model_verbosity = "high"
use_experimental_streamable_shell_tool: false,
include_view_image_tool: true,
disable_paste_burst: false,
redact_saved_prompt_body: true,
},
o3_profile_config
);
Expand Down Expand Up @@ -1250,6 +1266,7 @@ model_verbosity = "high"
use_experimental_streamable_shell_tool: false,
include_view_image_tool: true,
disable_paste_burst: false,
redact_saved_prompt_body: true,
};

assert_eq!(expected_gpt3_profile_config, gpt3_profile_config);
Expand Down Expand Up @@ -1321,6 +1338,7 @@ model_verbosity = "high"
use_experimental_streamable_shell_tool: false,
include_view_image_tool: true,
disable_paste_burst: false,
redact_saved_prompt_body: true,
};

assert_eq!(expected_zdr_profile_config, zdr_profile_config);
Expand Down Expand Up @@ -1378,13 +1396,33 @@ model_verbosity = "high"
use_experimental_streamable_shell_tool: false,
include_view_image_tool: true,
disable_paste_burst: false,
redact_saved_prompt_body: true,
};

assert_eq!(expected_gpt5_profile_config, gpt5_profile_config);

Ok(())
}

#[test]
fn config_toml_can_disable_saved_prompt_redaction() -> std::io::Result<()> {
let mut fixture = create_test_fixture()?;
// Set redact_saved_prompt_body = false in the base config
fixture.cfg.redact_saved_prompt_body = Some(false);

let overrides = ConfigOverrides {
cwd: Some(fixture.cwd()),
..Default::default()
};
let cfg: Config = Config::load_from_base_config_with_overrides(
fixture.cfg.clone(),
overrides,
fixture.codex_home(),
)?;
assert_eq!(cfg.redact_saved_prompt_body, false);
Ok(())
}

#[test]
fn test_set_project_trusted_writes_explicit_tables() -> anyhow::Result<()> {
let codex_home = TempDir::new().unwrap();
Expand Down
1 change: 1 addition & 0 deletions codex-rs/exec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>) -> any
include_view_image_tool: None,
show_raw_agent_reasoning: oss.then_some(true),
tools_web_search_request: None,
redact_saved_prompt_body: None,
};
// Parse `-c` overrides.
let cli_kv_overrides = match config_overrides.parse_overrides() {
Expand Down
1 change: 1 addition & 0 deletions codex-rs/mcp-server/src/codex_message_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1157,6 +1157,7 @@ fn derive_config_from_params(
include_view_image_tool: None,
show_raw_agent_reasoning: None,
tools_web_search_request: None,
redact_saved_prompt_body: None,
};

let cli_overrides = cli_overrides
Expand Down
1 change: 1 addition & 0 deletions codex-rs/mcp-server/src/codex_tool_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ impl CodexToolCallParam {
include_view_image_tool: None,
show_raw_agent_reasoning: None,
tools_web_search_request: None,
redact_saved_prompt_body: None,
};

let cli_overrides = cli_overrides
Expand Down
Loading
Loading