mirror of
https://github.com/openai/codex.git
synced 2026-04-19 04:04:46 +00:00
Compare commits
1 Commits
codex-debu
...
rhan/rollo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71f1c6ed79 |
@@ -85,6 +85,7 @@ pub fn create_fake_rollout_with_source(
|
||||
base_instructions: None,
|
||||
dynamic_tools: None,
|
||||
memory_mode: None,
|
||||
approved_command_prefixes: None,
|
||||
};
|
||||
let payload = serde_json::to_value(SessionMetaLine {
|
||||
meta,
|
||||
@@ -167,6 +168,7 @@ pub fn create_fake_rollout_with_text_elements(
|
||||
base_instructions: None,
|
||||
dynamic_tools: None,
|
||||
memory_mode: None,
|
||||
approved_command_prefixes: None,
|
||||
};
|
||||
let payload = serde_json::to_value(SessionMetaLine {
|
||||
meta,
|
||||
|
||||
@@ -328,6 +328,7 @@ stream_max_retries = 0
|
||||
base_instructions: None,
|
||||
dynamic_tools: None,
|
||||
memory_mode: None,
|
||||
approved_command_prefixes: None,
|
||||
};
|
||||
std::fs::write(
|
||||
&rollout_path,
|
||||
|
||||
@@ -71,6 +71,7 @@ mod tests {
|
||||
request_max_retries: None,
|
||||
stream_max_retries: None,
|
||||
stream_idle_timeout_ms: None,
|
||||
websocket_connect_timeout_ms: None,
|
||||
requires_openai_auth: false,
|
||||
supports_websockets: false,
|
||||
};
|
||||
|
||||
@@ -91,9 +91,11 @@ use codex_protocol::mcp::CallToolResult;
|
||||
use codex_protocol::models::BaseInstructions;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::models::format_allow_prefixes;
|
||||
use codex_protocol::models::summarize_allow_prefixes;
|
||||
use codex_protocol::openai_models::ModelInfo;
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||
use codex_protocol::protocol::ApprovedCommandPrefixesSnapshot;
|
||||
use codex_protocol::protocol::FileChange;
|
||||
use codex_protocol::protocol::HasLegacyEvent;
|
||||
use codex_protocol::protocol::ItemCompletedEvent;
|
||||
@@ -1404,6 +1406,9 @@ impl Session {
|
||||
let (conversation_id, rollout_params) = match &initial_history {
|
||||
InitialHistory::New | InitialHistory::Forked(_) => {
|
||||
let conversation_id = ThreadId::default();
|
||||
let approved_command_prefixes = exec_policy.current().get_allowed_prefixes();
|
||||
let allow_prefix_summary =
|
||||
summarize_allow_prefixes(approved_command_prefixes.clone());
|
||||
(
|
||||
conversation_id,
|
||||
RolloutRecorderParams::new(
|
||||
@@ -1414,6 +1419,12 @@ impl Session {
|
||||
text: session_configuration.base_instructions.clone(),
|
||||
},
|
||||
session_configuration.dynamic_tools.clone(),
|
||||
ApprovedCommandPrefixesSnapshot {
|
||||
prefixes: approved_command_prefixes,
|
||||
prompt_truncated: allow_prefix_summary.prompt_truncated,
|
||||
total_count: allow_prefix_summary.total_count,
|
||||
prompt_visible_count: allow_prefix_summary.prompt_visible_count,
|
||||
},
|
||||
if session_configuration.persist_extended_history {
|
||||
EventPersistenceMode::Extended
|
||||
} else {
|
||||
|
||||
@@ -2008,6 +2008,7 @@ async fn attach_rollout_recorder(session: &Arc<Session>) -> PathBuf {
|
||||
SessionSource::Exec,
|
||||
BaseInstructions::default(),
|
||||
Vec::new(),
|
||||
Default::default(),
|
||||
EventPersistenceMode::Limited,
|
||||
),
|
||||
None,
|
||||
@@ -3902,6 +3903,7 @@ async fn record_context_updates_and_set_reference_context_item_persists_baseline
|
||||
SessionSource::Exec,
|
||||
BaseInstructions::default(),
|
||||
Vec::new(),
|
||||
Default::default(),
|
||||
EventPersistenceMode::Limited,
|
||||
),
|
||||
None,
|
||||
@@ -3999,6 +4001,7 @@ async fn record_context_updates_and_set_reference_context_item_persists_full_rei
|
||||
SessionSource::Exec,
|
||||
BaseInstructions::default(),
|
||||
Vec::new(),
|
||||
Default::default(),
|
||||
EventPersistenceMode::Limited,
|
||||
),
|
||||
None,
|
||||
|
||||
@@ -44,6 +44,7 @@ async fn write_session_with_user_event(codex_home: &Path) -> io::Result<()> {
|
||||
base_instructions: None,
|
||||
dynamic_tools: None,
|
||||
memory_mode: None,
|
||||
approved_command_prefixes: None,
|
||||
},
|
||||
git: None,
|
||||
};
|
||||
|
||||
@@ -44,6 +44,7 @@ async fn extract_metadata_from_rollout_uses_session_meta() {
|
||||
base_instructions: None,
|
||||
dynamic_tools: None,
|
||||
memory_mode: None,
|
||||
approved_command_prefixes: None,
|
||||
};
|
||||
let session_meta_line = SessionMetaLine {
|
||||
meta: session_meta,
|
||||
@@ -94,9 +95,11 @@ async fn extract_metadata_from_rollout_returns_latest_memory_mode() {
|
||||
base_instructions: None,
|
||||
dynamic_tools: None,
|
||||
memory_mode: None,
|
||||
approved_command_prefixes: None,
|
||||
};
|
||||
let polluted_meta = SessionMeta {
|
||||
memory_mode: Some("polluted".to_string()),
|
||||
approved_command_prefixes: None,
|
||||
..session_meta.clone()
|
||||
};
|
||||
let lines = vec![
|
||||
@@ -361,6 +364,7 @@ fn write_rollout_in_sessions_with_cwd(
|
||||
base_instructions: None,
|
||||
dynamic_tools: None,
|
||||
memory_mode: None,
|
||||
approved_command_prefixes: None,
|
||||
};
|
||||
let session_meta_line = SessionMetaLine {
|
||||
meta: session_meta,
|
||||
|
||||
@@ -47,6 +47,7 @@ use crate::state_db;
|
||||
use crate::state_db::StateDbHandle;
|
||||
use crate::truncate::TruncationPolicy;
|
||||
use crate::truncate::truncate_text;
|
||||
use codex_protocol::protocol::ApprovedCommandPrefixesSnapshot;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::protocol::InitialHistory;
|
||||
use codex_protocol::protocol::ResumedHistory;
|
||||
@@ -83,6 +84,7 @@ pub enum RolloutRecorderParams {
|
||||
source: SessionSource,
|
||||
base_instructions: BaseInstructions,
|
||||
dynamic_tools: Vec<DynamicToolSpec>,
|
||||
approved_command_prefixes: ApprovedCommandPrefixesSnapshot,
|
||||
event_persistence_mode: EventPersistenceMode,
|
||||
},
|
||||
Resume {
|
||||
@@ -112,6 +114,7 @@ impl RolloutRecorderParams {
|
||||
source: SessionSource,
|
||||
base_instructions: BaseInstructions,
|
||||
dynamic_tools: Vec<DynamicToolSpec>,
|
||||
approved_command_prefixes: ApprovedCommandPrefixesSnapshot,
|
||||
event_persistence_mode: EventPersistenceMode,
|
||||
) -> Self {
|
||||
Self::Create {
|
||||
@@ -120,6 +123,7 @@ impl RolloutRecorderParams {
|
||||
source,
|
||||
base_instructions,
|
||||
dynamic_tools,
|
||||
approved_command_prefixes,
|
||||
event_persistence_mode,
|
||||
}
|
||||
}
|
||||
@@ -381,6 +385,7 @@ impl RolloutRecorder {
|
||||
source,
|
||||
base_instructions,
|
||||
dynamic_tools,
|
||||
approved_command_prefixes,
|
||||
event_persistence_mode,
|
||||
} => {
|
||||
let log_file_info = precompute_log_file_info(config, conversation_id)?;
|
||||
@@ -415,6 +420,7 @@ impl RolloutRecorder {
|
||||
},
|
||||
memory_mode: (!config.memories.generate_memories)
|
||||
.then_some("disabled".to_string()),
|
||||
approved_command_prefixes: Some(approved_command_prefixes),
|
||||
};
|
||||
|
||||
(
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::features::Feature;
|
||||
use chrono::TimeZone;
|
||||
use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig;
|
||||
use codex_protocol::protocol::AgentMessageEvent;
|
||||
use codex_protocol::protocol::ApprovedCommandPrefixesSnapshot;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::protocol::SandboxPolicy;
|
||||
@@ -59,6 +60,15 @@ async fn recorder_materializes_only_after_explicit_persist() -> std::io::Result<
|
||||
.build()
|
||||
.await?;
|
||||
let thread_id = ThreadId::new();
|
||||
let approved_command_prefixes = ApprovedCommandPrefixesSnapshot {
|
||||
prefixes: vec![
|
||||
vec!["cargo".to_string(), "test".to_string()],
|
||||
vec!["git".to_string(), "fetch".to_string()],
|
||||
],
|
||||
prompt_truncated: true,
|
||||
total_count: 101,
|
||||
prompt_visible_count: 100,
|
||||
};
|
||||
let recorder = RolloutRecorder::new(
|
||||
&config,
|
||||
RolloutRecorderParams::new(
|
||||
@@ -67,6 +77,7 @@ async fn recorder_materializes_only_after_explicit_persist() -> std::io::Result<
|
||||
SessionSource::Exec,
|
||||
BaseInstructions::default(),
|
||||
Vec::new(),
|
||||
approved_command_prefixes.clone(),
|
||||
EventPersistenceMode::Limited,
|
||||
),
|
||||
None,
|
||||
@@ -120,6 +131,10 @@ async fn recorder_materializes_only_after_explicit_persist() -> std::io::Result<
|
||||
text.contains("\"type\":\"session_meta\""),
|
||||
"expected session metadata in rollout"
|
||||
);
|
||||
assert!(
|
||||
text.contains(&serde_json::to_string(&approved_command_prefixes)?),
|
||||
"expected approved command prefixes snapshot in rollout"
|
||||
);
|
||||
let buffered_idx = text
|
||||
.find("buffered-event")
|
||||
.expect("buffered event in rollout");
|
||||
@@ -166,6 +181,7 @@ async fn metadata_irrelevant_events_touch_state_db_updated_at() -> std::io::Resu
|
||||
SessionSource::Cli,
|
||||
BaseInstructions::default(),
|
||||
Vec::new(),
|
||||
Default::default(),
|
||||
EventPersistenceMode::Limited,
|
||||
),
|
||||
Some(state_db.clone()),
|
||||
|
||||
@@ -1107,6 +1107,7 @@ async fn test_updated_at_uses_file_mtime() -> Result<()> {
|
||||
base_instructions: None,
|
||||
dynamic_tools: None,
|
||||
memory_mode: None,
|
||||
approved_command_prefixes: None,
|
||||
},
|
||||
git: None,
|
||||
}),
|
||||
|
||||
@@ -72,6 +72,7 @@ async fn write_rollout_with_user_event(dir: &Path, thread_id: ThreadId) -> io::R
|
||||
base_instructions: None,
|
||||
dynamic_tools: None,
|
||||
memory_mode: None,
|
||||
approved_command_prefixes: None,
|
||||
},
|
||||
git: None,
|
||||
};
|
||||
@@ -116,6 +117,7 @@ async fn write_rollout_with_meta_only(dir: &Path, thread_id: ThreadId) -> io::Re
|
||||
base_instructions: None,
|
||||
dynamic_tools: None,
|
||||
memory_mode: None,
|
||||
approved_command_prefixes: None,
|
||||
},
|
||||
git: None,
|
||||
};
|
||||
|
||||
@@ -172,6 +172,7 @@ async fn find_locates_rollout_file_written_by_recorder() -> std::io::Result<()>
|
||||
SessionSource::Exec,
|
||||
BaseInstructions::default(),
|
||||
Vec::new(),
|
||||
Default::default(),
|
||||
EventPersistenceMode::Limited,
|
||||
),
|
||||
None,
|
||||
|
||||
@@ -147,6 +147,7 @@ async fn backfill_scans_existing_rollouts() -> Result<()> {
|
||||
base_instructions: None,
|
||||
dynamic_tools: Some(dynamic_tools_for_hook),
|
||||
memory_mode: None,
|
||||
approved_command_prefixes: None,
|
||||
},
|
||||
git: None,
|
||||
};
|
||||
|
||||
@@ -778,13 +778,18 @@ const MAX_RENDERED_PREFIXES: usize = 100;
|
||||
const MAX_ALLOW_PREFIX_TEXT_BYTES: usize = 5000;
|
||||
const TRUNCATED_MARKER: &str = "...\n[Some commands were truncated]";
|
||||
|
||||
pub fn format_allow_prefixes(prefixes: Vec<Vec<String>>) -> Option<String> {
|
||||
let mut truncated = false;
|
||||
if prefixes.len() > MAX_RENDERED_PREFIXES {
|
||||
truncated = true;
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AllowPrefixRenderSummary {
|
||||
pub rendered: String,
|
||||
pub prompt_truncated: bool,
|
||||
pub total_count: usize,
|
||||
pub prompt_visible_count: usize,
|
||||
}
|
||||
|
||||
pub fn summarize_allow_prefixes(mut prefixes: Vec<Vec<String>>) -> AllowPrefixRenderSummary {
|
||||
let total_count = prefixes.len();
|
||||
let mut truncated = total_count > MAX_RENDERED_PREFIXES;
|
||||
|
||||
let mut prefixes = prefixes;
|
||||
prefixes.sort_by(|a, b| {
|
||||
a.len()
|
||||
.cmp(&b.len())
|
||||
@@ -792,15 +797,15 @@ pub fn format_allow_prefixes(prefixes: Vec<Vec<String>>) -> Option<String> {
|
||||
.then_with(|| a.cmp(b))
|
||||
});
|
||||
|
||||
let full_text = prefixes
|
||||
let rendered_prefixes = prefixes
|
||||
.into_iter()
|
||||
.take(MAX_RENDERED_PREFIXES)
|
||||
.map(|prefix| format!("- {}", render_command_prefix(&prefix)))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
.collect::<Vec<_>>();
|
||||
let mut output = rendered_prefixes.join("\n");
|
||||
let mut prompt_visible_count = rendered_prefixes.len();
|
||||
|
||||
// truncate to last UTF8 char
|
||||
let mut output = full_text;
|
||||
let byte_idx = output
|
||||
.char_indices()
|
||||
.nth(MAX_ALLOW_PREFIX_TEXT_BYTES)
|
||||
@@ -808,13 +813,27 @@ pub fn format_allow_prefixes(prefixes: Vec<Vec<String>>) -> Option<String> {
|
||||
if let Some(byte_idx) = byte_idx {
|
||||
truncated = true;
|
||||
output = output[..byte_idx].to_string();
|
||||
prompt_visible_count = if output.is_empty() {
|
||||
0
|
||||
} else {
|
||||
output.lines().count()
|
||||
};
|
||||
}
|
||||
|
||||
if truncated {
|
||||
Some(format!("{output}{TRUNCATED_MARKER}"))
|
||||
} else {
|
||||
Some(output)
|
||||
output = format!("{output}{TRUNCATED_MARKER}");
|
||||
}
|
||||
|
||||
AllowPrefixRenderSummary {
|
||||
rendered: output,
|
||||
prompt_truncated: truncated,
|
||||
total_count,
|
||||
prompt_visible_count,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_allow_prefixes(prefixes: Vec<Vec<String>>) -> Option<String> {
|
||||
Some(summarize_allow_prefixes(prefixes).rendered)
|
||||
}
|
||||
|
||||
fn prefix_combined_str_len(prefix: &[String]) -> usize {
|
||||
@@ -2280,10 +2299,11 @@ mod tests {
|
||||
.map(|i| vec![format!("{i:03}")])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let output = format_allow_prefixes(prefixes).expect("rendered list");
|
||||
assert_eq!(output.ends_with(TRUNCATED_MARKER), true);
|
||||
eprintln!("output: {output}");
|
||||
assert_eq!(output.lines().count(), MAX_RENDERED_PREFIXES + 1);
|
||||
let summary = summarize_allow_prefixes(prefixes);
|
||||
assert!(summary.rendered.ends_with(TRUNCATED_MARKER));
|
||||
assert_eq!(summary.total_count, MAX_RENDERED_PREFIXES + 5);
|
||||
assert_eq!(summary.prompt_visible_count, MAX_RENDERED_PREFIXES);
|
||||
assert_eq!(summary.rendered.lines().count(), MAX_RENDERED_PREFIXES + 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2298,12 +2318,26 @@ mod tests {
|
||||
.expect("add rule");
|
||||
}
|
||||
|
||||
let output =
|
||||
format_allow_prefixes(exec_policy.get_allowed_prefixes()).expect("formatted prefixes");
|
||||
let summary = summarize_allow_prefixes(exec_policy.get_allowed_prefixes());
|
||||
assert!(
|
||||
output.len() <= MAX_ALLOW_PREFIX_TEXT_BYTES + TRUNCATED_MARKER.len(),
|
||||
"output length exceeds expected limit: {output}",
|
||||
summary.rendered.len() <= MAX_ALLOW_PREFIX_TEXT_BYTES + TRUNCATED_MARKER.len(),
|
||||
"output length exceeds expected limit: {}",
|
||||
summary.rendered,
|
||||
);
|
||||
assert!(summary.prompt_truncated);
|
||||
assert!(summary.prompt_visible_count < summary.total_count);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn summarize_allow_prefixes_reports_untruncated_counts() {
|
||||
let summary = summarize_allow_prefixes(vec![
|
||||
vec!["git".to_string(), "fetch".to_string()],
|
||||
vec!["cargo".to_string(), "test".to_string()],
|
||||
]);
|
||||
|
||||
assert_eq!(summary.total_count, 2);
|
||||
assert_eq!(summary.prompt_visible_count, 2);
|
||||
assert!(!summary.prompt_truncated);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -2344,6 +2344,14 @@ impl fmt::Display for SubAgentSource {
|
||||
/// NOTE: There used to be an `instructions` field here, which stored user_instructions, but we
|
||||
/// now save that on TurnContext. base_instructions stores the base instructions for the session,
|
||||
/// and should be used when there is no config override.
|
||||
#[derive(Default, Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, TS)]
|
||||
pub struct ApprovedCommandPrefixesSnapshot {
|
||||
pub prefixes: Vec<Vec<String>>,
|
||||
pub prompt_truncated: bool,
|
||||
pub total_count: usize,
|
||||
pub prompt_visible_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema, TS)]
|
||||
pub struct SessionMeta {
|
||||
pub id: ThreadId,
|
||||
@@ -2370,6 +2378,8 @@ pub struct SessionMeta {
|
||||
pub dynamic_tools: Option<Vec<DynamicToolSpec>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub memory_mode: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub approved_command_prefixes: Option<ApprovedCommandPrefixesSnapshot>,
|
||||
}
|
||||
|
||||
impl Default for SessionMeta {
|
||||
@@ -2388,6 +2398,7 @@ impl Default for SessionMeta {
|
||||
base_instructions: None,
|
||||
dynamic_tools: None,
|
||||
memory_mode: None,
|
||||
approved_command_prefixes: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,6 +257,7 @@ mod tests {
|
||||
base_instructions: None,
|
||||
dynamic_tools: None,
|
||||
memory_mode: None,
|
||||
approved_command_prefixes: None,
|
||||
},
|
||||
git: None,
|
||||
}),
|
||||
@@ -383,6 +384,7 @@ mod tests {
|
||||
base_instructions: None,
|
||||
dynamic_tools: None,
|
||||
memory_mode: None,
|
||||
approved_command_prefixes: None,
|
||||
},
|
||||
git: None,
|
||||
}),
|
||||
|
||||
@@ -764,6 +764,7 @@ mod tests {
|
||||
base_instructions: None,
|
||||
dynamic_tools: None,
|
||||
memory_mode: Some("polluted".to_string()),
|
||||
approved_command_prefixes: None,
|
||||
},
|
||||
git: None,
|
||||
})];
|
||||
@@ -818,6 +819,7 @@ mod tests {
|
||||
base_instructions: None,
|
||||
dynamic_tools: None,
|
||||
memory_mode: None,
|
||||
approved_command_prefixes: None,
|
||||
},
|
||||
git: Some(GitInfo {
|
||||
commit_hash: Some("rollout-sha".to_string()),
|
||||
|
||||
Reference in New Issue
Block a user