Compare commits

...

2 Commits

Author SHA1 Message Date
kevin zhao
34809c9880 timezones 2025-10-30 18:17:52 -04:00
kevin zhao
325e35388c adding system date to env context 2025-10-30 18:00:32 -04:00
3 changed files with 50 additions and 9 deletions

View File

@@ -116,6 +116,7 @@ use crate::user_instructions::DeveloperInstructions;
use crate::user_instructions::UserInstructions; use crate::user_instructions::UserInstructions;
use crate::user_notification::UserNotification; use crate::user_notification::UserNotification;
use crate::util::backoff; use crate::util::backoff;
use chrono::Local;
use codex_async_utils::OrCancelExt; use codex_async_utils::OrCancelExt;
use codex_otel::otel_event_manager::OtelEventManager; use codex_otel::otel_event_manager::OtelEventManager;
use codex_protocol::config_types::ReasoningEffort as ReasoningEffortConfig; use codex_protocol::config_types::ReasoningEffort as ReasoningEffortConfig;
@@ -267,6 +268,7 @@ pub(crate) struct TurnContext {
/// the model as well as sandbox policies are resolved against this path /// the model as well as sandbox policies are resolved against this path
/// instead of `std::env::current_dir()`. /// instead of `std::env::current_dir()`.
pub(crate) cwd: PathBuf, pub(crate) cwd: PathBuf,
pub(crate) local_date_with_timezone: Option<String>,
pub(crate) developer_instructions: Option<String>, pub(crate) developer_instructions: Option<String>,
pub(crate) base_instructions: Option<String>, pub(crate) base_instructions: Option<String>,
pub(crate) compact_prompt: Option<String>, pub(crate) compact_prompt: Option<String>,
@@ -423,6 +425,7 @@ impl Session {
sub_id, sub_id,
client, client,
cwd: session_configuration.cwd.clone(), cwd: session_configuration.cwd.clone(),
local_date_with_timezone: Some(Local::now().format("%Y-%m-%d %:z").to_string()),
developer_instructions: session_configuration.developer_instructions.clone(), developer_instructions: session_configuration.developer_instructions.clone(),
base_instructions: session_configuration.base_instructions.clone(), base_instructions: session_configuration.base_instructions.clone(),
compact_prompt: session_configuration.compact_prompt.clone(), compact_prompt: session_configuration.compact_prompt.clone(),
@@ -1007,6 +1010,7 @@ impl Session {
} }
items.push(ResponseItem::from(EnvironmentContext::new( items.push(ResponseItem::from(EnvironmentContext::new(
Some(turn_context.cwd.clone()), Some(turn_context.cwd.clone()),
turn_context.local_date_with_timezone.clone(),
Some(turn_context.approval_policy), Some(turn_context.approval_policy),
Some(turn_context.sandbox_policy.clone()), Some(turn_context.sandbox_policy.clone()),
Some(self.user_shell().clone()), Some(self.user_shell().clone()),
@@ -1692,6 +1696,7 @@ async fn spawn_review_thread(
sandbox_policy: parent_turn_context.sandbox_policy.clone(), sandbox_policy: parent_turn_context.sandbox_policy.clone(),
shell_environment_policy: parent_turn_context.shell_environment_policy.clone(), shell_environment_policy: parent_turn_context.shell_environment_policy.clone(),
cwd: parent_turn_context.cwd.clone(), cwd: parent_turn_context.cwd.clone(),
local_date_with_timezone: parent_turn_context.local_date_with_timezone.clone(),
final_output_json_schema: None, final_output_json_schema: None,
codex_linux_sandbox_exe: parent_turn_context.codex_linux_sandbox_exe.clone(), codex_linux_sandbox_exe: parent_turn_context.codex_linux_sandbox_exe.clone(),
tool_call_gate: Arc::new(ReadinessFlag::new()), tool_call_gate: Arc::new(ReadinessFlag::new()),

View File

@@ -24,6 +24,7 @@ pub enum NetworkAccess {
#[serde(rename = "environment_context", rename_all = "snake_case")] #[serde(rename = "environment_context", rename_all = "snake_case")]
pub(crate) struct EnvironmentContext { pub(crate) struct EnvironmentContext {
pub cwd: Option<PathBuf>, pub cwd: Option<PathBuf>,
pub local_date: Option<String>,
pub approval_policy: Option<AskForApproval>, pub approval_policy: Option<AskForApproval>,
pub sandbox_mode: Option<SandboxMode>, pub sandbox_mode: Option<SandboxMode>,
pub network_access: Option<NetworkAccess>, pub network_access: Option<NetworkAccess>,
@@ -34,12 +35,14 @@ pub(crate) struct EnvironmentContext {
impl EnvironmentContext { impl EnvironmentContext {
pub fn new( pub fn new(
cwd: Option<PathBuf>, cwd: Option<PathBuf>,
local_date: Option<String>,
approval_policy: Option<AskForApproval>, approval_policy: Option<AskForApproval>,
sandbox_policy: Option<SandboxPolicy>, sandbox_policy: Option<SandboxPolicy>,
shell: Option<Shell>, shell: Option<Shell>,
) -> Self { ) -> Self {
Self { Self {
cwd, cwd,
local_date,
approval_policy, approval_policy,
sandbox_mode: match sandbox_policy { sandbox_mode: match sandbox_policy {
Some(SandboxPolicy::DangerFullAccess) => Some(SandboxMode::DangerFullAccess), Some(SandboxPolicy::DangerFullAccess) => Some(SandboxMode::DangerFullAccess),
@@ -79,6 +82,7 @@ impl EnvironmentContext {
pub fn equals_except_shell(&self, other: &EnvironmentContext) -> bool { pub fn equals_except_shell(&self, other: &EnvironmentContext) -> bool {
let EnvironmentContext { let EnvironmentContext {
cwd, cwd,
local_date,
approval_policy, approval_policy,
sandbox_mode, sandbox_mode,
network_access, network_access,
@@ -88,6 +92,7 @@ impl EnvironmentContext {
} = other; } = other;
self.cwd == *cwd self.cwd == *cwd
&& self.local_date == *local_date
&& self.approval_policy == *approval_policy && self.approval_policy == *approval_policy
&& self.sandbox_mode == *sandbox_mode && self.sandbox_mode == *sandbox_mode
&& self.network_access == *network_access && self.network_access == *network_access
@@ -100,6 +105,11 @@ impl EnvironmentContext {
} else { } else {
None None
}; };
let local_date = if before.local_date_with_timezone != after.local_date_with_timezone {
after.local_date_with_timezone.clone()
} else {
None
};
let approval_policy = if before.approval_policy != after.approval_policy { let approval_policy = if before.approval_policy != after.approval_policy {
Some(after.approval_policy) Some(after.approval_policy)
} else { } else {
@@ -110,7 +120,7 @@ impl EnvironmentContext {
} else { } else {
None None
}; };
EnvironmentContext::new(cwd, approval_policy, sandbox_policy, None) EnvironmentContext::new(cwd, local_date, approval_policy, sandbox_policy, None)
} }
} }
@@ -118,6 +128,7 @@ impl From<&TurnContext> for EnvironmentContext {
fn from(turn_context: &TurnContext) -> Self { fn from(turn_context: &TurnContext) -> Self {
Self::new( Self::new(
Some(turn_context.cwd.clone()), Some(turn_context.cwd.clone()),
turn_context.local_date_with_timezone.clone(),
Some(turn_context.approval_policy), Some(turn_context.approval_policy),
Some(turn_context.sandbox_policy.clone()), Some(turn_context.sandbox_policy.clone()),
// Shell is not configurable from turn to turn // Shell is not configurable from turn to turn
@@ -134,6 +145,7 @@ impl EnvironmentContext {
/// ```xml /// ```xml
/// <environment_context> /// <environment_context>
/// <cwd>...</cwd> /// <cwd>...</cwd>
/// <local_date>...</local_date>
/// <approval_policy>...</approval_policy> /// <approval_policy>...</approval_policy>
/// <sandbox_mode>...</sandbox_mode> /// <sandbox_mode>...</sandbox_mode>
/// <writable_roots>...</writable_roots> /// <writable_roots>...</writable_roots>
@@ -146,6 +158,9 @@ impl EnvironmentContext {
if let Some(cwd) = self.cwd { if let Some(cwd) = self.cwd {
lines.push(format!(" <cwd>{}</cwd>", cwd.to_string_lossy())); lines.push(format!(" <cwd>{}</cwd>", cwd.to_string_lossy()));
} }
if let Some(local_date) = self.local_date {
lines.push(format!(" <local_date>{local_date}</local_date>"));
}
if let Some(approval_policy) = self.approval_policy { if let Some(approval_policy) = self.approval_policy {
lines.push(format!( lines.push(format!(
" <approval_policy>{approval_policy}</approval_policy>" " <approval_policy>{approval_policy}</approval_policy>"
@@ -212,6 +227,7 @@ mod tests {
fn serialize_workspace_write_environment_context() { fn serialize_workspace_write_environment_context() {
let context = EnvironmentContext::new( let context = EnvironmentContext::new(
Some(PathBuf::from("/repo")), Some(PathBuf::from("/repo")),
Some("2025-01-01 +00:00".to_string()),
Some(AskForApproval::OnRequest), Some(AskForApproval::OnRequest),
Some(workspace_write_policy(vec!["/repo", "/tmp"], false)), Some(workspace_write_policy(vec!["/repo", "/tmp"], false)),
None, None,
@@ -219,6 +235,7 @@ mod tests {
let expected = r#"<environment_context> let expected = r#"<environment_context>
<cwd>/repo</cwd> <cwd>/repo</cwd>
<local_date>2025-01-01 +00:00</local_date>
<approval_policy>on-request</approval_policy> <approval_policy>on-request</approval_policy>
<sandbox_mode>workspace-write</sandbox_mode> <sandbox_mode>workspace-write</sandbox_mode>
<network_access>restricted</network_access> <network_access>restricted</network_access>
@@ -235,12 +252,14 @@ mod tests {
fn serialize_read_only_environment_context() { fn serialize_read_only_environment_context() {
let context = EnvironmentContext::new( let context = EnvironmentContext::new(
None, None,
Some("2025-01-01 +00:00".to_string()),
Some(AskForApproval::Never), Some(AskForApproval::Never),
Some(SandboxPolicy::ReadOnly), Some(SandboxPolicy::ReadOnly),
None, None,
); );
let expected = r#"<environment_context> let expected = r#"<environment_context>
<local_date>2025-01-01 +00:00</local_date>
<approval_policy>never</approval_policy> <approval_policy>never</approval_policy>
<sandbox_mode>read-only</sandbox_mode> <sandbox_mode>read-only</sandbox_mode>
<network_access>restricted</network_access> <network_access>restricted</network_access>
@@ -253,12 +272,14 @@ mod tests {
fn serialize_full_access_environment_context() { fn serialize_full_access_environment_context() {
let context = EnvironmentContext::new( let context = EnvironmentContext::new(
None, None,
Some("2025-01-01 +00:00".to_string()),
Some(AskForApproval::OnFailure), Some(AskForApproval::OnFailure),
Some(SandboxPolicy::DangerFullAccess), Some(SandboxPolicy::DangerFullAccess),
None, None,
); );
let expected = r#"<environment_context> let expected = r#"<environment_context>
<local_date>2025-01-01 +00:00</local_date>
<approval_policy>on-failure</approval_policy> <approval_policy>on-failure</approval_policy>
<sandbox_mode>danger-full-access</sandbox_mode> <sandbox_mode>danger-full-access</sandbox_mode>
<network_access>enabled</network_access> <network_access>enabled</network_access>
@@ -272,12 +293,14 @@ mod tests {
// Approval policy // Approval policy
let context1 = EnvironmentContext::new( let context1 = EnvironmentContext::new(
Some(PathBuf::from("/repo")), Some(PathBuf::from("/repo")),
Some("2025-01-01 +00:00".to_string()),
Some(AskForApproval::OnRequest), Some(AskForApproval::OnRequest),
Some(workspace_write_policy(vec!["/repo"], false)), Some(workspace_write_policy(vec!["/repo"], false)),
None, None,
); );
let context2 = EnvironmentContext::new( let context2 = EnvironmentContext::new(
Some(PathBuf::from("/repo")), Some(PathBuf::from("/repo")),
Some("2025-01-01 +00:00".to_string()),
Some(AskForApproval::Never), Some(AskForApproval::Never),
Some(workspace_write_policy(vec!["/repo"], true)), Some(workspace_write_policy(vec!["/repo"], true)),
None, None,
@@ -289,12 +312,14 @@ mod tests {
fn equals_except_shell_compares_sandbox_policy() { fn equals_except_shell_compares_sandbox_policy() {
let context1 = EnvironmentContext::new( let context1 = EnvironmentContext::new(
Some(PathBuf::from("/repo")), Some(PathBuf::from("/repo")),
Some("2025-01-01 +00:00".to_string()),
Some(AskForApproval::OnRequest), Some(AskForApproval::OnRequest),
Some(SandboxPolicy::new_read_only_policy()), Some(SandboxPolicy::new_read_only_policy()),
None, None,
); );
let context2 = EnvironmentContext::new( let context2 = EnvironmentContext::new(
Some(PathBuf::from("/repo")), Some(PathBuf::from("/repo")),
Some("2025-01-01 +00:00".to_string()),
Some(AskForApproval::OnRequest), Some(AskForApproval::OnRequest),
Some(SandboxPolicy::new_workspace_write_policy()), Some(SandboxPolicy::new_workspace_write_policy()),
None, None,
@@ -307,12 +332,14 @@ mod tests {
fn equals_except_shell_compares_workspace_write_policy() { fn equals_except_shell_compares_workspace_write_policy() {
let context1 = EnvironmentContext::new( let context1 = EnvironmentContext::new(
Some(PathBuf::from("/repo")), Some(PathBuf::from("/repo")),
Some("2025-01-01 +00:00".to_string()),
Some(AskForApproval::OnRequest), Some(AskForApproval::OnRequest),
Some(workspace_write_policy(vec!["/repo", "/tmp", "/var"], false)), Some(workspace_write_policy(vec!["/repo", "/tmp", "/var"], false)),
None, None,
); );
let context2 = EnvironmentContext::new( let context2 = EnvironmentContext::new(
Some(PathBuf::from("/repo")), Some(PathBuf::from("/repo")),
Some("2025-01-01 +00:00".to_string()),
Some(AskForApproval::OnRequest), Some(AskForApproval::OnRequest),
Some(workspace_write_policy(vec!["/repo", "/tmp"], true)), Some(workspace_write_policy(vec!["/repo", "/tmp"], true)),
None, None,
@@ -325,6 +352,7 @@ mod tests {
fn equals_except_shell_ignores_shell() { fn equals_except_shell_ignores_shell() {
let context1 = EnvironmentContext::new( let context1 = EnvironmentContext::new(
Some(PathBuf::from("/repo")), Some(PathBuf::from("/repo")),
Some("2025-01-01 +00:00".to_string()),
Some(AskForApproval::OnRequest), Some(AskForApproval::OnRequest),
Some(workspace_write_policy(vec!["/repo"], false)), Some(workspace_write_policy(vec!["/repo"], false)),
Some(Shell::Bash(BashShell { Some(Shell::Bash(BashShell {
@@ -334,6 +362,7 @@ mod tests {
); );
let context2 = EnvironmentContext::new( let context2 = EnvironmentContext::new(
Some(PathBuf::from("/repo")), Some(PathBuf::from("/repo")),
Some("2025-01-01 +00:00".to_string()),
Some(AskForApproval::OnRequest), Some(AskForApproval::OnRequest),
Some(workspace_write_policy(vec!["/repo"], false)), Some(workspace_write_policy(vec!["/repo"], false)),
Some(Shell::Zsh(ZshShell { Some(Shell::Zsh(ZshShell {

View File

@@ -1,5 +1,6 @@
#![allow(clippy::unwrap_used)] #![allow(clippy::unwrap_used)]
use chrono::Local;
use codex_core::CodexAuth; use codex_core::CodexAuth;
use codex_core::ConversationManager; use codex_core::ConversationManager;
use codex_core::ModelProviderInfo; use codex_core::ModelProviderInfo;
@@ -40,9 +41,11 @@ fn text_user_input(text: String) -> serde_json::Value {
} }
fn default_env_context_str(cwd: &str, shell: &Shell) -> String { fn default_env_context_str(cwd: &str, shell: &Shell) -> String {
let local_date = Local::now().format("%Y-%m-%d %:z").to_string();
format!( format!(
r#"<environment_context> r#"<environment_context>
<cwd>{}</cwd> <cwd>{}</cwd>
<local_date>{local_date}</local_date>
<approval_policy>on-request</approval_policy> <approval_policy>on-request</approval_policy>
<sandbox_mode>read-only</sandbox_mode> <sandbox_mode>read-only</sandbox_mode>
<network_access>restricted</network_access> <network_access>restricted</network_access>
@@ -344,19 +347,23 @@ async fn prefixes_context_and_instructions_once_and_consistently_across_requests
let shell = default_user_shell().await; let shell = default_user_shell().await;
let expected_env_text = format!( let expected_env_text = {
r#"<environment_context> let local_date = Local::now().format("%Y-%m-%d %:z").to_string();
format!(
r#"<environment_context>
<cwd>{}</cwd> <cwd>{}</cwd>
<local_date>{local_date}</local_date>
<approval_policy>on-request</approval_policy> <approval_policy>on-request</approval_policy>
<sandbox_mode>read-only</sandbox_mode> <sandbox_mode>read-only</sandbox_mode>
<network_access>restricted</network_access> <network_access>restricted</network_access>
{}</environment_context>"#, {}</environment_context>"#,
cwd.path().to_string_lossy(), cwd.path().to_string_lossy(),
match shell.name() { match shell.name() {
Some(name) => format!(" <shell>{name}</shell>\n"), Some(name) => format!(" <shell>{name}</shell>\n"),
None => String::new(), None => String::new(),
} }
); )
};
let expected_ui_text = let expected_ui_text =
"<user_instructions>\n\nbe consistent and helpful\n\n</user_instructions>"; "<user_instructions>\n\nbe consistent and helpful\n\n</user_instructions>";