mirror of
https://github.com/openai/codex.git
synced 2026-11-05 20:10:33 +00:00
Record saved multi-agent opt-outs in session telemetry (#59478)
## Why Saved `features.multi_agent = false` settings can be overridden by a higher-precedence session setting. Session telemetry should retain visibility into those saved opt-outs. ## What changed Add `config.legacy_multi_agent_disabled_sources` to `codex.conversation_starts`, reporting opt-outs from user configuration, project configuration, and managed requirements. Emit comma-separated source labels, or `none` when no opt-outs are found. ## Testing Add a regression test confirming that a user opt-out is still reported when a CLI override enables multi-agent support for the session. GitOrigin-RevId: 488bd317d67e9ee87c5903ae51f466e0a63c05eb
This commit is contained in:
@@ -10133,6 +10133,27 @@ job_max_runtime_seconds = 900
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn legacy_multi_agent_opt_out_survives_a_session_override() -> std::io::Result<()> {
|
||||
let home = TempDir::new()?;
|
||||
std::fs::write(
|
||||
home.path().join(CONFIG_TOML_FILE),
|
||||
"[features]\nmulti_agent = false\n",
|
||||
)?;
|
||||
let config = ConfigBuilder::without_managed_config_for_tests()
|
||||
.codex_home(home.path().to_path_buf())
|
||||
.fallback_cwd(Some(home.path().to_path_buf()))
|
||||
.cli_overrides(vec![("features.multi_agent".to_string(), true.into())])
|
||||
.build()
|
||||
.await?;
|
||||
assert!(config.features.enabled(Feature::Collab));
|
||||
assert_eq!(
|
||||
legacy_multi_agent_disabled_sources(&config.config_layer_stack),
|
||||
["user"]
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn load_config_resolves_agent_controls() -> std::io::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
//! Metrics derived from loaded configuration at session start.
|
||||
|
||||
use super::Config;
|
||||
use codex_config::ConfigLayerSource;
|
||||
use codex_config::ConfigLayerStack;
|
||||
use codex_features::FEATURES;
|
||||
use codex_features::Stage;
|
||||
use codex_otel::SessionTelemetry;
|
||||
@@ -24,3 +26,38 @@ pub(crate) fn emit_session_start_metrics(config: &Config, telemetry: &SessionTel
|
||||
#[cfg(windows)]
|
||||
crate::windows_system_config::emit_namespace_squatting_probe();
|
||||
}
|
||||
|
||||
/// Report saved opt-outs even when a higher-precedence session setting overrides them.
|
||||
pub(crate) fn legacy_multi_agent_disabled_sources(layers: &ConfigLayerStack) -> Vec<&'static str> {
|
||||
let mut user = None;
|
||||
let mut project = None;
|
||||
for layer in layers.layers_low_to_high() {
|
||||
let setting = match layer.name {
|
||||
ConfigLayerSource::User { .. } => &mut user,
|
||||
ConfigLayerSource::Project { .. } => &mut project,
|
||||
_ => continue,
|
||||
};
|
||||
if let Some(value) = layer
|
||||
.config
|
||||
.get("features")
|
||||
.and_then(|features| features.get("multi_agent"))
|
||||
.and_then(toml::Value::as_bool)
|
||||
{
|
||||
*setting = Some(value);
|
||||
}
|
||||
}
|
||||
let managed = layers
|
||||
.requirements()
|
||||
.feature_requirements
|
||||
.as_ref()
|
||||
.and_then(|requirements| requirements.value.entries.get("multi_agent"))
|
||||
.copied();
|
||||
[
|
||||
("user", user),
|
||||
("project", project),
|
||||
("managed_requirement", managed),
|
||||
]
|
||||
.into_iter()
|
||||
.filter_map(|(source, value)| (value == Some(false)).then_some(source))
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -201,6 +201,7 @@ use codex_sandboxing::compatibility_sandbox_policy_for_permission_profile;
|
||||
pub use codex_sandboxing::system_bwrap_warning;
|
||||
pub use managed_features::ManagedFeatures;
|
||||
pub(crate) use metrics::emit_session_start_metrics;
|
||||
pub(crate) use metrics::legacy_multi_agent_disabled_sources;
|
||||
pub use network_config::EnvironmentNetworkConfigError;
|
||||
pub use network_config::NetworkConfigInputs;
|
||||
pub use network_config::PreparedNetworkConfig;
|
||||
|
||||
@@ -1512,6 +1512,7 @@ impl Session {
|
||||
.permissions
|
||||
.legacy_sandbox_policy(session_configuration.cwd().as_path()),
|
||||
mcp_server_names.iter().map(String::as_str).collect(),
|
||||
&crate::config::legacy_multi_agent_disabled_sources(&config.config_layer_stack),
|
||||
);
|
||||
|
||||
let default_shell = session_configuration
|
||||
|
||||
@@ -696,11 +696,17 @@ impl SessionTelemetry {
|
||||
approval_policy: AskForApproval,
|
||||
sandbox_policy: SandboxPolicy,
|
||||
mcp_servers: Vec<&str>,
|
||||
legacy_multi_agent_disabled_sources: &[&str],
|
||||
) {
|
||||
log_and_trace_event!(
|
||||
self,
|
||||
common: {
|
||||
event.name = "codex.conversation_starts",
|
||||
config.legacy_multi_agent_disabled_sources = if legacy_multi_agent_disabled_sources.is_empty() {
|
||||
"none".to_string()
|
||||
} else {
|
||||
legacy_multi_agent_disabled_sources.join(",")
|
||||
},
|
||||
provider_name = %provider_name,
|
||||
auth.env_openai_api_key_present = self.metadata.auth_env.openai_api_key_env_present,
|
||||
auth.env_codex_api_key_present = self.metadata.auth_env.codex_api_key_env_present,
|
||||
|
||||
@@ -883,6 +883,7 @@ fn otel_export_routing_policy_routes_api_request_auth_observability() {
|
||||
AskForApproval::Never,
|
||||
SandboxPolicy::DangerFullAccess,
|
||||
Vec::new(),
|
||||
/*legacy_multi_agent_disabled_sources*/ &[],
|
||||
);
|
||||
let agent_identity_telemetry = AgentIdentityTelemetry {
|
||||
agent_id: "agent-runtime-otel".to_string(),
|
||||
|
||||
Reference in New Issue
Block a user