mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[codex] Start the guardian child session when parent session is started (#27982)
## Why The first auto-review currently creates its Guardian child session on demand, adding avoidable latency before the review can begin. Creating the ordinary Guardian child during parent-session initialization lets that child use the existing session startup WebSocket prewarm before the first escalation. This does not introduce a Guardian-specific prewarm mechanism. ## What changed - initialize the existing Guardian review-session manager owned by `Session` when a thread starts with auto-review enabled and an approval policy that routes to Guardian - use the standard Guardian child-session construction and the existing session startup WebSocket prewarm - preserve the existing reuse-key invalidation and lazy creation fallback when startup initialization fails or the effective review configuration changes - add an integration test that verifies normal root-session startup emits a Guardian `generate=false` prewarm request ## Benchmark I compared release builds against main. Each prompt first ran a non-escalated `sleep 3`, then requested an escalated marker command. | binary | count | avg Guardian duration | median Guardian duration | avg Guardian TTFT | |---|---:|---:|---:|---:| | origin-main | 10 | 4008.7 ms | 3949.5 ms | 3746.5 ms | | session-fix | 10 | 2865.0 ms | 2594.0 ms | 2492.7 ms | Guardian duration fell by 28.5% and Guardian TTFT fell by 33.5%. These measurements cover Guardian review latency; they do not measure parent thread-start latency.
This commit is contained in:
@@ -17,6 +17,7 @@ use core_test_support::responses::ev_response_created;
|
||||
use core_test_support::responses::mount_sse_sequence;
|
||||
use core_test_support::responses::sse;
|
||||
use core_test_support::responses::start_mock_server;
|
||||
use core_test_support::responses::start_websocket_server;
|
||||
use core_test_support::skip_if_no_network;
|
||||
use core_test_support::skip_if_sandbox;
|
||||
use core_test_support::test_codex::local_selections;
|
||||
@@ -30,6 +31,85 @@ use std::os::unix::fs::PermissionsExt;
|
||||
use std::time::Duration;
|
||||
use tempfile::TempDir;
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn guardian_session_prewarms_and_is_reused_for_first_review() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
let tool_args = json!({
|
||||
"cmd": "true",
|
||||
"sandbox_permissions": SandboxPermissions::RequireEscalated,
|
||||
"justification": "Exercise Guardian approval routing.",
|
||||
})
|
||||
.to_string();
|
||||
let server = start_websocket_server(vec![
|
||||
vec![vec![ev_response_created("warm-1"), ev_completed("warm-1")]],
|
||||
vec![vec![ev_response_created("warm-2"), ev_completed("warm-2")]],
|
||||
vec![vec![
|
||||
ev_response_created("approval-request"),
|
||||
ev_function_call("approval-call", "exec_command", &tool_args),
|
||||
ev_completed("approval-request"),
|
||||
]],
|
||||
vec![vec![
|
||||
ev_response_created("guardian-review"),
|
||||
ev_completed("guardian-review"),
|
||||
]],
|
||||
])
|
||||
.await;
|
||||
let mut builder = test_codex().with_config(|config| {
|
||||
config.permissions.approval_policy = Constrained::allow_any(AskForApproval::OnRequest);
|
||||
config.approvals_reviewer = ApprovalsReviewer::AutoReview;
|
||||
});
|
||||
|
||||
let test = builder.build_with_websocket_server(&server).await?;
|
||||
let (first, second) = tokio::time::timeout(Duration::from_secs(5), async {
|
||||
tokio::join!(
|
||||
server.wait_for_request(/*connection_index*/ 0, /*request_index*/ 0),
|
||||
server.wait_for_request(/*connection_index*/ 1, /*request_index*/ 0)
|
||||
)
|
||||
})
|
||||
.await?;
|
||||
let prewarm_requests = [first.body_json(), second.body_json()];
|
||||
let guardian_prewarm = prewarm_requests
|
||||
.iter()
|
||||
.find(|request| {
|
||||
request["client_metadata"]["x-openai-subagent"].as_str() == Some("guardian")
|
||||
})
|
||||
.expect("guardian startup prewarm request");
|
||||
assert_eq!(guardian_prewarm["generate"].as_bool(), Some(false));
|
||||
let guardian_thread_id = guardian_prewarm["client_metadata"]["thread_id"]
|
||||
.as_str()
|
||||
.expect("guardian thread id");
|
||||
|
||||
test.codex
|
||||
.submit(
|
||||
vec![UserInput::Text {
|
||||
text: "run a command that requires Guardian review".into(),
|
||||
text_elements: Vec::new(),
|
||||
}]
|
||||
.into(),
|
||||
)
|
||||
.await?;
|
||||
let guardian_review = tokio::time::timeout(
|
||||
Duration::from_secs(5),
|
||||
server.wait_for_request(/*connection_index*/ 3, /*request_index*/ 0),
|
||||
)
|
||||
.await?
|
||||
.body_json();
|
||||
assert_eq!(
|
||||
guardian_review["client_metadata"]["x-openai-subagent"].as_str(),
|
||||
Some("guardian")
|
||||
);
|
||||
assert_eq!(
|
||||
guardian_review["client_metadata"]["thread_id"].as_str(),
|
||||
Some(guardian_thread_id)
|
||||
);
|
||||
assert_eq!(guardian_review.get("generate"), None);
|
||||
|
||||
test.codex.shutdown_and_wait().await?;
|
||||
server.shutdown().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn guardian_review_session_does_not_inherit_legacy_notify() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
Reference in New Issue
Block a user