mirror of
https://github.com/openai/codex.git
synced 2026-09-06 15:29:32 +00:00
## What changed - Add an extension API for spawning host-owned internal sessions and a `ThreadReadyInput` lifecycle callback that runs after thread registration. - Add Guardian reviewer session scaffolding that records the parent thread and effective model, plus the under-development `guardian_ext` feature flag. - Start internal sessions with fresh history while preserving parent lineage, shared session controls, and internal-thread visibility rules. - Scope internal-session prompt cache keys to their source and parent thread, and expose `guardian` as an internal session source. ## Testing - Cover internal-session spawning, parent metadata, history isolation, prompt cache keys, and extension spawner argument forwarding. GitOrigin-RevId: 682dae80397d62bb36247796b447042e760ca364
86 lines
2.8 KiB
Rust
86 lines
2.8 KiB
Rust
use std::sync::Arc;
|
|
use std::sync::Mutex;
|
|
|
|
use codex_extension_api::AgentSpawnFuture;
|
|
use codex_extension_api::AgentSpawner;
|
|
use codex_extension_api::InternalSessionSpawnFuture;
|
|
use codex_extension_api::InternalSessionSpawner;
|
|
use codex_extension_api::NoopResponseItemInjector;
|
|
use codex_extension_api::ResponseItemInjector;
|
|
use codex_protocol::ThreadId;
|
|
use codex_protocol::models::ContentItem;
|
|
use codex_protocol::models::ResponseInputItem;
|
|
use pretty_assertions::assert_eq;
|
|
|
|
#[tokio::test]
|
|
async fn noop_response_item_injector_returns_original_items() {
|
|
let items = vec![ResponseInputItem::Message {
|
|
role: "user".to_string(),
|
|
content: vec![ContentItem::InputText {
|
|
text: "keep this input".to_string(),
|
|
}],
|
|
phase: None,
|
|
}];
|
|
|
|
let returned_items = NoopResponseItemInjector
|
|
.inject_response_items(items.clone())
|
|
.await
|
|
.expect_err("noop injector should reject same-turn injection");
|
|
|
|
assert_eq!(returned_items, items);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn closure_agent_spawner_forwards_arguments_and_result() {
|
|
let calls = Arc::new(Mutex::new(Vec::new()));
|
|
let recorded_calls = Arc::clone(&calls);
|
|
let spawner = move |thread_id: ThreadId,
|
|
request: String|
|
|
-> AgentSpawnFuture<'static, usize, &'static str> {
|
|
recorded_calls
|
|
.lock()
|
|
.expect("agent spawn calls lock")
|
|
.push((thread_id, request.clone()));
|
|
Box::pin(async move { Ok(request.len()) })
|
|
};
|
|
let thread_id =
|
|
ThreadId::from_string("11111111-1111-4111-8111-111111111111").expect("valid thread id");
|
|
|
|
let spawned = spawner
|
|
.spawn_subagent(thread_id, "delegate this".to_string())
|
|
.await;
|
|
|
|
assert_eq!(spawned, Ok(13));
|
|
assert_eq!(
|
|
calls.lock().expect("agent spawn calls lock").as_slice(),
|
|
[(thread_id, "delegate this".to_string())]
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn closure_internal_session_spawner_forwards_arguments_and_result() {
|
|
let calls = Arc::new(Mutex::new(Vec::new()));
|
|
let recorded_calls = Arc::clone(&calls);
|
|
let spawner = move |thread_id: ThreadId,
|
|
request: String|
|
|
-> InternalSessionSpawnFuture<'static, usize, &'static str> {
|
|
recorded_calls
|
|
.lock()
|
|
.expect("agent spawn calls lock")
|
|
.push((thread_id, request.clone()));
|
|
Box::pin(async move { Ok(request.len()) })
|
|
};
|
|
let thread_id =
|
|
ThreadId::from_string("11111111-1111-4111-8111-111111111111").expect("valid thread id");
|
|
|
|
let spawned = spawner
|
|
.spawn_internal_session(thread_id, "delegate this".to_string())
|
|
.await;
|
|
|
|
assert_eq!(spawned, Ok(13));
|
|
assert_eq!(
|
|
calls.lock().expect("agent spawn calls lock").as_slice(),
|
|
[(thread_id, "delegate this".to_string())]
|
|
);
|
|
}
|