diff --git a/codex-rs/otel/src/events/session_telemetry.rs b/codex-rs/otel/src/events/session_telemetry.rs index 8dabf63a8c..0478728e61 100644 --- a/codex-rs/otel/src/events/session_telemetry.rs +++ b/codex-rs/otel/src/events/session_telemetry.rs @@ -363,7 +363,9 @@ impl SessionTelemetry { remember_choice: bool, ) { let justification_to_log = match justification { - Some(justification) if self.metadata.log_user_prompts => justification, + Some(justification) if justification_required || self.metadata.log_user_prompts => { + justification + } Some(_) => "[REDACTED]", None => "", }; diff --git a/codex-rs/otel/tests/suite/otel_export_routing_policy.rs b/codex-rs/otel/tests/suite/otel_export_routing_policy.rs index 93381e778e..19f73b87cd 100644 --- a/codex-rs/otel/tests/suite/otel_export_routing_policy.rs +++ b/codex-rs/otel/tests/suite/otel_export_routing_policy.rs @@ -235,7 +235,7 @@ fn otel_export_routing_policy_routes_full_access_mode_enabled_events() { Some("engineer@example.com".to_string()), Some(TelemetryAuthMode::ApiKey), "codex_exec".to_string(), - true, + false, "tty".to_string(), SessionSource::Cli, ); @@ -297,6 +297,68 @@ fn otel_export_routing_policy_routes_full_access_mode_enabled_events() { ); } +#[test] +fn otel_export_routing_policy_redacts_optional_full_access_justification_when_prompt_logging_disabled() + { + let log_exporter = InMemoryLogExporter::default(); + let logger_provider = SdkLoggerProvider::builder() + .with_simple_exporter(log_exporter.clone()) + .build(); + let span_exporter = InMemorySpanExporter::default(); + let tracer_provider = SdkTracerProvider::builder() + .with_simple_exporter(span_exporter) + .build(); + let tracer = tracer_provider.tracer("sink-split-test"); + + let subscriber = tracing_subscriber::registry() + .with( + opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge::new( + &logger_provider, + ) + .with_filter(filter_fn(OtelProvider::log_export_filter)), + ) + .with( + tracing_opentelemetry::layer() + .with_tracer(tracer) + .with_filter(filter_fn(OtelProvider::trace_export_filter)), + ); + + tracing::subscriber::with_default(subscriber, || { + tracing::callsite::rebuild_interest_cache(); + let manager = SessionTelemetry::new( + ThreadId::new(), + "gpt-5.1", + "gpt-5.1", + Some("account-id".to_string()), + Some("engineer@example.com".to_string()), + Some(TelemetryAuthMode::ApiKey), + "codex_exec".to_string(), + false, + "tty".to_string(), + SessionSource::Cli, + ); + let root_span = tracing::info_span!("root"); + let _root_guard = root_span.enter(); + manager.full_access_mode_enabled( + /*justification_required*/ false, + Some("Need to inspect repo-local secrets handling"), + /*remember_choice*/ false, + ); + }); + + logger_provider.force_flush().expect("flush logs"); + + let logs = log_exporter.get_emitted_logs().expect("log export"); + let full_access_log = find_log_by_event_name(&logs, "codex.full_access_mode_enabled"); + let full_access_log_attrs = log_attributes(&full_access_log.record); + assert_eq!( + full_access_log_attrs + .get("justification") + .map(String::as_str), + Some("[REDACTED]") + ); +} + #[test] fn otel_export_routing_policy_routes_tool_result_log_and_trace_events() { let log_exporter = InMemoryLogExporter::default();