From bccce0d75f836750c32ce4fc3dde6504fa2f591c Mon Sep 17 00:00:00 2001 From: mcgrew-oai <146999853+mcgrew-oai@users.noreply.github.com> Date: Wed, 25 Feb 2026 09:54:45 -0500 Subject: [PATCH 1/3] otel: add host.name resource attribute to logs/traces via gethostname (#12352) **PR Summary** This PR adds the OpenTelemetry `host.name` resource attribute to Codex OTEL exports so every OTEL log (and trace, via the shared resource) carries the machine hostname. **What changed** - Added `host.name` to the shared OTEL `Resource` in `/Users/michael.mcgrew/code/codex/codex-rs/otel/src/otel_provider.rs` - This applies to both: - OTEL logs (`SdkLoggerProvider`) - OTEL traces (`SdkTracerProvider`) - Hostname is now resolved via `gethostname::gethostname()` (best-effort) - Value is trimmed - Empty values are omitted (non-fatal) - Added focused unit tests for: - including `host.name` when present - omitting `host.name` when missing/empty **Why** - `host.name` is host/process metadata and belongs on the OTEL `resource`, not per-event attributes. - Attaching it in the shared resource is the smallest change that guarantees coverage across all exported OTEL logs/traces. **Scope / Non-goals** - No public API changes - No changes to metrics behavior (this PR only updates log/trace resource metadata) **Dependency updates** - Added `gethostname` as a workspace dependency and `codex-otel` dependency - `Cargo.lock` updated accordingly - `MODULE.bazel.lock` unchanged after refresh/check **Validation** - `just fmt` - `cargo test -p codex-otel` - `just bazel-lock-update` - `just bazel-lock-check` --- codex-rs/Cargo.lock | 1 + codex-rs/Cargo.toml | 1 + codex-rs/otel/Cargo.toml | 1 + codex-rs/otel/src/otel_provider.rs | 81 +++++++++++++++++++++++++++--- 4 files changed, 77 insertions(+), 7 deletions(-) diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 45402ed66a..05e0ea297f 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -2095,6 +2095,7 @@ dependencies = [ "codex-utils-absolute-path", "codex-utils-string", "eventsource-stream", + "gethostname", "http 1.4.0", "opentelemetry", "opentelemetry-appender-tracing", diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index e648572ec4..3bb436ef07 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -178,6 +178,7 @@ env_logger = "0.11.9" eventsource-stream = "0.2.3" futures = { version = "0.3", default-features = false } globset = "0.4" +gethostname = "1.1.0" http = "1.3.1" icu_decimal = "2.1" icu_locale_core = "2.1" diff --git a/codex-rs/otel/Cargo.toml b/codex-rs/otel/Cargo.toml index 6e6321d2e5..0fa14ff541 100644 --- a/codex-rs/otel/Cargo.toml +++ b/codex-rs/otel/Cargo.toml @@ -26,6 +26,7 @@ codex-utils-string = { workspace = true } codex-api = { workspace = true } codex-protocol = { workspace = true } eventsource-stream = { workspace = true } +gethostname = { workspace = true } opentelemetry = { workspace = true, features = ["logs", "metrics", "trace"] } opentelemetry-appender-tracing = { workspace = true } opentelemetry-otlp = { workspace = true, features = [ diff --git a/codex-rs/otel/src/otel_provider.rs b/codex-rs/otel/src/otel_provider.rs index b1ea099fa2..6f2dc5a09d 100644 --- a/codex-rs/otel/src/otel_provider.rs +++ b/codex-rs/otel/src/otel_provider.rs @@ -3,6 +3,7 @@ use crate::config::OtelHttpProtocol; use crate::config::OtelSettings; use crate::metrics::MetricsClient; use crate::metrics::MetricsConfig; +use gethostname::gethostname; use opentelemetry::Context; use opentelemetry::KeyValue; use opentelemetry::context::ContextGuard; @@ -40,6 +41,7 @@ use tracing_subscriber::Layer; use tracing_subscriber::registry::LookupSpan; const ENV_ATTRIBUTE: &str = "env"; +const HOST_NAME_ATTRIBUTE: &str = "host.name"; const TRACEPARENT_ENV_VAR: &str = "TRACEPARENT"; const TRACESTATE_ENV_VAR: &str = "TRACESTATE"; static TRACEPARENT_CONTEXT: OnceLock> = OnceLock::new(); @@ -223,16 +225,37 @@ fn extract_traceparent_context(traceparent: String, tracestate: Option) fn make_resource(settings: &OtelSettings) -> Resource { Resource::builder() .with_service_name(settings.service_name.clone()) - .with_attributes(vec![ - KeyValue::new( - semconv::attribute::SERVICE_VERSION, - settings.service_version.clone(), - ), - KeyValue::new(ENV_ATTRIBUTE, settings.environment.clone()), - ]) + .with_attributes(resource_attributes( + settings, + detected_host_name().as_deref(), + )) .build() } +fn resource_attributes(settings: &OtelSettings, host_name: Option<&str>) -> Vec { + let mut attributes = vec![ + KeyValue::new( + semconv::attribute::SERVICE_VERSION, + settings.service_version.clone(), + ), + KeyValue::new(ENV_ATTRIBUTE, settings.environment.clone()), + ]; + if let Some(host_name) = host_name.and_then(normalize_host_name) { + attributes.push(KeyValue::new(HOST_NAME_ATTRIBUTE, host_name)); + } + attributes +} + +fn detected_host_name() -> Option { + let host_name = gethostname(); + normalize_host_name(host_name.to_string_lossy().as_ref()) +} + +fn normalize_host_name(host_name: &str) -> Option { + let host_name = host_name.trim(); + (!host_name.is_empty()).then(|| host_name.to_owned()) +} + fn build_logger( resource: &Resource, exporter: &OtelExporter, @@ -377,6 +400,8 @@ mod tests { use opentelemetry::trace::SpanId; use opentelemetry::trace::TraceContextExt; use opentelemetry::trace::TraceId; + use pretty_assertions::assert_eq; + use std::path::PathBuf; #[test] fn parses_valid_traceparent() { @@ -398,4 +423,46 @@ mod tests { fn invalid_traceparent_returns_none() { assert!(extract_traceparent_context("not-a-traceparent".to_string(), None).is_none()); } + + #[test] + fn resource_attributes_include_host_name_when_present() { + let attrs = resource_attributes(&test_otel_settings(), Some("opentelemetry-test")); + + let host_name = attrs + .iter() + .find(|kv| kv.key.as_str() == HOST_NAME_ATTRIBUTE) + .map(|kv| kv.value.as_str().to_string()); + + assert_eq!(host_name, Some("opentelemetry-test".to_string())); + } + + #[test] + fn resource_attributes_omit_host_name_when_missing_or_empty() { + let missing = resource_attributes(&test_otel_settings(), None); + let empty = resource_attributes(&test_otel_settings(), Some(" ")); + + assert!( + !missing + .iter() + .any(|kv| kv.key.as_str() == HOST_NAME_ATTRIBUTE) + ); + assert!( + !empty + .iter() + .any(|kv| kv.key.as_str() == HOST_NAME_ATTRIBUTE) + ); + } + + fn test_otel_settings() -> OtelSettings { + OtelSettings { + environment: "test".to_string(), + service_name: "codex-test".to_string(), + service_version: "0.0.0".to_string(), + codex_home: PathBuf::from("."), + exporter: OtelExporter::None, + trace_exporter: OtelExporter::None, + metrics_exporter: OtelExporter::None, + runtime_metrics: false, + } + } } From 01f25a7b9646bf71672cb3363132ff8f97556c27 Mon Sep 17 00:00:00 2001 From: jif-oai Date: Wed, 25 Feb 2026 15:20:24 +0000 Subject: [PATCH 2/3] chore: unify max depth parameter (#12770) Users were confused --- codex-rs/core/config.schema.json | 6 --- codex-rs/core/src/agent/guards.rs | 5 --- codex-rs/core/src/agent/mod.rs | 1 - codex-rs/core/src/config/mod.rs | 31 ---------------- .../core/src/tools/handlers/agent_jobs.rs | 5 +-- .../core/src/tools/handlers/multi_agents.rs | 37 +++++++------------ 6 files changed, 15 insertions(+), 70 deletions(-) diff --git a/codex-rs/core/config.schema.json b/codex-rs/core/config.schema.json index ca7fb51579..c81c4e174e 100644 --- a/codex-rs/core/config.schema.json +++ b/codex-rs/core/config.schema.json @@ -41,12 +41,6 @@ "minimum": 1.0, "type": "integer" }, - "max_spawn_depth": { - "description": "Maximum depth for thread-spawned subagents.", - "format": "uint", - "minimum": 1.0, - "type": "integer" - }, "max_threads": { "description": "Maximum number of agent threads that can be open concurrently. When unset, no limit is enforced.", "format": "uint", diff --git a/codex-rs/core/src/agent/guards.rs b/codex-rs/core/src/agent/guards.rs index b8db6e397d..4213cd0ed0 100644 --- a/codex-rs/core/src/agent/guards.rs +++ b/codex-rs/core/src/agent/guards.rs @@ -1,4 +1,3 @@ -use crate::config::DEFAULT_AGENT_MAX_SPAWN_DEPTH; use crate::error::CodexErr; use crate::error::Result; use codex_protocol::ThreadId; @@ -43,10 +42,6 @@ pub(crate) fn next_thread_spawn_depth(session_source: &SessionSource) -> i32 { session_depth(session_source).saturating_add(1) } -pub(crate) fn max_thread_spawn_depth(max_depth: Option) -> i32 { - let max_depth = max_depth.or(DEFAULT_AGENT_MAX_SPAWN_DEPTH).unwrap_or(1); - i32::try_from(max_depth).unwrap_or(i32::MAX) -} pub(crate) fn exceeds_thread_spawn_depth_limit(depth: i32, max_depth: i32) -> bool { depth > max_depth } diff --git a/codex-rs/core/src/agent/mod.rs b/codex-rs/core/src/agent/mod.rs index 6ae7d96159..15be909c3d 100644 --- a/codex-rs/core/src/agent/mod.rs +++ b/codex-rs/core/src/agent/mod.rs @@ -6,6 +6,5 @@ pub(crate) mod status; pub(crate) use codex_protocol::protocol::AgentStatus; pub(crate) use control::AgentControl; pub(crate) use guards::exceeds_thread_spawn_depth_limit; -pub(crate) use guards::max_thread_spawn_depth; pub(crate) use guards::next_thread_spawn_depth; pub(crate) use status::agent_status_from_event; diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index caa6a523f2..6b0c05fef3 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -115,7 +115,6 @@ pub use codex_git::GhostSnapshotConfig; /// the context window. pub(crate) const PROJECT_DOC_MAX_BYTES: usize = 32 * 1024; // 32 KiB pub(crate) const DEFAULT_AGENT_MAX_THREADS: Option = Some(6); -pub(crate) const DEFAULT_AGENT_MAX_SPAWN_DEPTH: Option = Some(2); pub(crate) const DEFAULT_AGENT_MAX_DEPTH: i32 = 1; pub(crate) const DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS: Option = None; @@ -357,8 +356,6 @@ pub struct Config { /// Maximum number of agent threads that can be open concurrently. pub agent_max_threads: Option, - /// Maximum depth for thread-spawned subagents. - pub agent_max_spawn_depth: Option, /// Maximum runtime in seconds for agent job workers before they are failed. pub agent_job_max_runtime_seconds: Option, @@ -1340,9 +1337,6 @@ pub struct AgentsToml { /// When unset, no limit is enforced. #[schemars(range(min = 1))] pub max_threads: Option, - /// Maximum depth for thread-spawned subagents. - #[schemars(range(min = 1))] - pub max_spawn_depth: Option, /// Maximum nesting depth allowed for spawned agent threads. /// Root sessions start at depth 0. #[schemars(range(min = 1))] @@ -1865,25 +1859,6 @@ impl Config { }) .transpose()? .unwrap_or_default(); - let agent_max_spawn_depth = cfg - .agents - .as_ref() - .and_then(|agents| agents.max_spawn_depth) - .or(DEFAULT_AGENT_MAX_SPAWN_DEPTH); - if agent_max_spawn_depth == Some(0) { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "agents.max_spawn_depth must be at least 1", - )); - } - if let Some(max_spawn_depth) = agent_max_spawn_depth - && max_spawn_depth > i32::MAX as usize - { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "agents.max_spawn_depth must fit within a 32-bit signed integer", - )); - } let agent_job_max_runtime_seconds = cfg .agents .as_ref() @@ -2149,7 +2124,6 @@ impl Config { agent_max_depth, agent_roles, memories: cfg.memories.unwrap_or_default().into(), - agent_max_spawn_depth, agent_job_max_runtime_seconds, codex_home, sqlite_home, @@ -4487,7 +4461,6 @@ model = "gpt-5.1-codex" let cfg = ConfigToml { agents: Some(AgentsToml { max_threads: None, - max_spawn_depth: None, max_depth: None, job_max_runtime_seconds: None, roles: BTreeMap::from([( @@ -4763,7 +4736,6 @@ model_verbosity = "high" agent_max_depth: DEFAULT_AGENT_MAX_DEPTH, agent_roles: BTreeMap::new(), memories: MemoriesConfig::default(), - agent_max_spawn_depth: DEFAULT_AGENT_MAX_SPAWN_DEPTH, agent_job_max_runtime_seconds: DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS, codex_home: fixture.codex_home(), sqlite_home: fixture.codex_home(), @@ -4890,7 +4862,6 @@ model_verbosity = "high" agent_max_depth: DEFAULT_AGENT_MAX_DEPTH, agent_roles: BTreeMap::new(), memories: MemoriesConfig::default(), - agent_max_spawn_depth: DEFAULT_AGENT_MAX_SPAWN_DEPTH, agent_job_max_runtime_seconds: DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS, codex_home: fixture.codex_home(), sqlite_home: fixture.codex_home(), @@ -5015,7 +4986,6 @@ model_verbosity = "high" agent_max_depth: DEFAULT_AGENT_MAX_DEPTH, agent_roles: BTreeMap::new(), memories: MemoriesConfig::default(), - agent_max_spawn_depth: DEFAULT_AGENT_MAX_SPAWN_DEPTH, agent_job_max_runtime_seconds: DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS, codex_home: fixture.codex_home(), sqlite_home: fixture.codex_home(), @@ -5126,7 +5096,6 @@ model_verbosity = "high" agent_max_depth: DEFAULT_AGENT_MAX_DEPTH, agent_roles: BTreeMap::new(), memories: MemoriesConfig::default(), - agent_max_spawn_depth: DEFAULT_AGENT_MAX_SPAWN_DEPTH, agent_job_max_runtime_seconds: DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS, codex_home: fixture.codex_home(), sqlite_home: fixture.codex_home(), diff --git a/codex-rs/core/src/tools/handlers/agent_jobs.rs b/codex-rs/core/src/tools/handlers/agent_jobs.rs index 6b36be05dd..c4891a6b3a 100644 --- a/codex-rs/core/src/tools/handlers/agent_jobs.rs +++ b/codex-rs/core/src/tools/handlers/agent_jobs.rs @@ -1,5 +1,4 @@ use crate::agent::exceeds_thread_spawn_depth_limit; -use crate::agent::max_thread_spawn_depth; use crate::agent::next_thread_spawn_depth; use crate::agent::status::is_final; use crate::codex::Session; @@ -531,7 +530,7 @@ async fn build_runner_options( ) -> Result { let session_source = turn.session_source.clone(); let child_depth = next_thread_spawn_depth(&session_source); - let max_depth = max_thread_spawn_depth(turn.config.agent_max_spawn_depth); + let max_depth = turn.config.agent_max_depth; if exceeds_thread_spawn_depth_limit(child_depth, max_depth) { return Err(FunctionCallError::RespondToModel( "agent depth limit reached; this session cannot spawn more subagents".to_string(), @@ -540,7 +539,7 @@ async fn build_runner_options( let max_concurrency = normalize_concurrency(requested_concurrency, turn.config.agent_max_threads); let base_instructions = session.get_base_instructions().await; - let spawn_config = build_agent_spawn_config(&base_instructions, turn.as_ref(), child_depth)?; + let spawn_config = build_agent_spawn_config(&base_instructions, turn.as_ref())?; Ok(JobRunnerOptions { max_concurrency, spawn_config, diff --git a/codex-rs/core/src/tools/handlers/multi_agents.rs b/codex-rs/core/src/tools/handlers/multi_agents.rs index 9000e6d232..6e62fd04fd 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents.rs @@ -1,6 +1,5 @@ use crate::agent::AgentStatus; use crate::agent::exceeds_thread_spawn_depth_limit; -use crate::agent::max_thread_spawn_depth; use crate::codex::Session; use crate::codex::TurnContext; use crate::config::Config; @@ -96,7 +95,6 @@ mod spawn { use crate::agent::role::apply_role_to_config; use crate::agent::exceeds_thread_spawn_depth_limit; - use crate::agent::max_thread_spawn_depth; use crate::agent::next_thread_spawn_depth; use std::sync::Arc; @@ -129,7 +127,7 @@ mod spawn { let prompt = input_preview(&input_items); let session_source = turn.session_source.clone(); let child_depth = next_thread_spawn_depth(&session_source); - let max_depth = max_thread_spawn_depth(turn.config.agent_max_spawn_depth); + let max_depth = turn.config.agent_max_depth; if exceeds_thread_spawn_depth_limit(child_depth, max_depth) { return Err(FunctionCallError::RespondToModel( "Agent depth limit reached. Solve the task yourself.".to_string(), @@ -146,11 +144,8 @@ mod spawn { .into(), ) .await; - let mut config = build_agent_spawn_config( - &session.get_base_instructions().await, - turn.as_ref(), - child_depth, - )?; + let mut config = + build_agent_spawn_config(&session.get_base_instructions().await, turn.as_ref())?; apply_role_to_config(&mut config, role_name) .await .map_err(FunctionCallError::RespondToModel)?; @@ -346,7 +341,7 @@ mod resume_agent { .await .unwrap_or((None, None)); let child_depth = next_thread_spawn_depth(&turn.session_source); - let max_depth = max_thread_spawn_depth(turn.config.agent_max_spawn_depth); + let max_depth = turn.config.agent_max_depth; if exceeds_thread_spawn_depth_limit(child_depth, max_depth) { return Err(FunctionCallError::RespondToModel( "Agent depth limit reached. Solve the task yourself.".to_string(), @@ -896,9 +891,8 @@ fn input_preview(items: &[UserInput]) -> String { pub(crate) fn build_agent_spawn_config( base_instructions: &BaseInstructions, turn: &TurnContext, - child_depth: i32, ) -> Result { - let mut config = build_agent_shared_config(turn, child_depth)?; + let mut config = build_agent_shared_config(turn)?; config.base_instructions = Some(base_instructions.text.clone()); Ok(config) } @@ -907,16 +901,14 @@ fn build_agent_resume_config( turn: &TurnContext, child_depth: i32, ) -> Result { - let mut config = build_agent_shared_config(turn, child_depth)?; + let mut config = build_agent_shared_config(turn)?; + apply_spawn_agent_overrides(&mut config, child_depth); // For resume, keep base instructions sourced from rollout/session metadata. config.base_instructions = None; Ok(config) } -fn build_agent_shared_config( - turn: &TurnContext, - child_depth: i32, -) -> Result { +fn build_agent_shared_config(turn: &TurnContext) -> Result { let base_config = turn.config.clone(); let mut config = (*base_config).clone(); config.model = Some(turn.model_info.slug.clone()); @@ -926,7 +918,6 @@ fn build_agent_shared_config( config.developer_instructions = turn.developer_instructions.clone(); config.compact_prompt = turn.compact_prompt.clone(); apply_spawn_agent_runtime_overrides(&mut config, turn)?; - apply_spawn_agent_overrides(&mut config, child_depth); Ok(config) } @@ -956,8 +947,7 @@ fn apply_spawn_agent_runtime_overrides( } fn apply_spawn_agent_overrides(config: &mut Config, child_depth: i32) { - let max_depth = max_thread_spawn_depth(config.agent_max_spawn_depth); - if exceeds_thread_spawn_depth_limit(child_depth + 1, max_depth) { + if child_depth >= config.agent_max_depth { config.features.disable(Feature::Collab); } } @@ -968,7 +958,6 @@ mod tests { use crate::AuthManager; use crate::CodexAuth; use crate::ThreadManager; - use crate::agent::max_thread_spawn_depth; use crate::built_in_model_providers; use crate::codex::make_session_and_context; use crate::config::DEFAULT_AGENT_MAX_DEPTH; @@ -1273,7 +1262,7 @@ mod tests { let manager = thread_manager(); session.services.agent_control = manager.agent_control(); - let max_depth = max_thread_spawn_depth(turn.config.agent_max_spawn_depth); + let max_depth = turn.config.agent_max_depth; turn.session_source = SessionSource::SubAgent(SubAgentSource::ThreadSpawn { parent_thread_id: session.conversation_id, depth: max_depth, @@ -1704,7 +1693,7 @@ mod tests { let manager = thread_manager(); session.services.agent_control = manager.agent_control(); - let max_depth = max_thread_spawn_depth(turn.config.agent_max_spawn_depth); + let max_depth = turn.config.agent_max_depth; turn.session_source = SessionSource::SubAgent(SubAgentSource::ThreadSpawn { parent_thread_id: session.conversation_id, depth: max_depth, @@ -2051,7 +2040,7 @@ mod tests { .set(AskForApproval::OnRequest) .expect("approval policy set"); - let config = build_agent_spawn_config(&base_instructions, &turn, 0).expect("spawn config"); + let config = build_agent_spawn_config(&base_instructions, &turn).expect("spawn config"); let mut expected = (*turn.config).clone(); expected.base_instructions = Some(base_instructions.text); expected.model = Some(turn.model_info.slug.clone()); @@ -2087,7 +2076,7 @@ mod tests { text: "base".to_string(), }; - let config = build_agent_spawn_config(&base_instructions, &turn, 0).expect("spawn config"); + let config = build_agent_spawn_config(&base_instructions, &turn).expect("spawn config"); assert_eq!(config.user_instructions, base_config.user_instructions); } From 8362b79cb478401c2027296f0ca3aed62b884122 Mon Sep 17 00:00:00 2001 From: jif-oai Date: Wed, 25 Feb 2026 15:52:55 +0000 Subject: [PATCH 3/3] feat: fix sqlite home (#12787) --- codex-rs/core/config.schema.json | 2 +- codex-rs/core/src/config/mod.rs | 32 +++++++++++++++++++------------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/codex-rs/core/config.schema.json b/codex-rs/core/config.schema.json index c81c4e174e..c5e85ad682 100644 --- a/codex-rs/core/config.schema.json +++ b/codex-rs/core/config.schema.json @@ -2052,7 +2052,7 @@ "$ref": "#/definitions/AbsolutePathBuf" } ], - "description": "Directory where Codex stores the SQLite state DB. Defaults to `$CODEX_SQLITE_HOME` when set. Otherwise uses a temp dir under WorkspaceWrite sandboxing and `$CODEX_HOME` for other modes." + "description": "Directory where Codex stores the SQLite state DB. Defaults to `$CODEX_SQLITE_HOME` when set. Otherwise uses `$CODEX_HOME`." }, "suppress_unstable_features_warning": { "description": "Suppress warnings about unstable (under development) features.", diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index 6b0c05fef3..37fd5740a6 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -120,16 +120,6 @@ pub(crate) const DEFAULT_AGENT_JOB_MAX_RUNTIME_SECONDS: Option = None; pub const CONFIG_TOML_FILE: &str = "config.toml"; -fn default_sqlite_home(sandbox_policy: &SandboxPolicy, codex_home: &Path) -> PathBuf { - if matches!(sandbox_policy, SandboxPolicy::WorkspaceWrite { .. }) { - let mut path = std::env::temp_dir(); - path.push("codex-sqlite"); - path - } else { - codex_home.to_path_buf() - } -} - fn resolve_sqlite_home_env(resolved_cwd: &Path) -> Option { let raw = std::env::var(codex_state::SQLITE_HOME_ENV).ok()?; let trimmed = raw.trim(); @@ -1146,8 +1136,7 @@ pub struct ConfigToml { pub history: Option, /// Directory where Codex stores the SQLite state DB. - /// Defaults to `$CODEX_SQLITE_HOME` when set. Otherwise uses a temp dir - /// under WorkspaceWrite sandboxing and `$CODEX_HOME` for other modes. + /// Defaults to `$CODEX_SQLITE_HOME` when set. Otherwise uses `$CODEX_HOME`. pub sqlite_home: Option, /// Directory where Codex writes log files, for example `codex-tui.log`. @@ -2007,7 +1996,7 @@ impl Config { .as_ref() .map(AbsolutePathBuf::to_path_buf) .or_else(|| resolve_sqlite_home_env(&resolved_cwd)) - .unwrap_or_else(|| default_sqlite_home(&sandbox_policy, &codex_home)); + .unwrap_or_else(|| codex_home.to_path_buf()); // Ensure that every field of ConfigRequirements is applied to the final // Config. @@ -2957,6 +2946,23 @@ trust_level = "trusted" Ok(()) } + #[test] + fn sqlite_home_defaults_to_codex_home_for_workspace_write() -> std::io::Result<()> { + let codex_home = TempDir::new()?; + let config = Config::load_from_base_config_with_overrides( + ConfigToml::default(), + ConfigOverrides { + sandbox_mode: Some(SandboxMode::WorkspaceWrite), + ..Default::default() + }, + codex_home.path().to_path_buf(), + )?; + + assert_eq!(config.sqlite_home, codex_home.path().to_path_buf()); + + Ok(()) + } + #[test] fn config_defaults_to_file_cli_auth_store_mode() -> std::io::Result<()> { let codex_home = TempDir::new()?;