mirror of
https://github.com/openai/codex.git
synced 2026-11-10 21:00:47 +00:00
[codex] wire native integrity state surfaces [ci changed_files]
This commit is contained in:
@@ -26,6 +26,7 @@ chrono = { workspace = true, features = ["serde"] }
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
codex-analytics = { workspace = true }
|
||||
codex-api = { workspace = true }
|
||||
codex-client = { workspace = true }
|
||||
codex-app-server-protocol = { workspace = true }
|
||||
codex-apply-patch = { workspace = true }
|
||||
codex-async-utils = { workspace = true }
|
||||
|
||||
@@ -307,6 +307,21 @@ impl AgentControl {
|
||||
{
|
||||
let client_metadata = match state.get_thread(*parent_thread_id).await {
|
||||
Ok(parent_thread) => {
|
||||
if let Some(surface) = parent_thread
|
||||
.codex
|
||||
.session
|
||||
.services
|
||||
.model_client
|
||||
.native_integrity_surface()
|
||||
{
|
||||
new_thread
|
||||
.thread
|
||||
.codex
|
||||
.session
|
||||
.services
|
||||
.model_client
|
||||
.set_native_integrity_surface(surface);
|
||||
}
|
||||
parent_thread
|
||||
.codex
|
||||
.session
|
||||
|
||||
@@ -10,6 +10,7 @@ use crate::context::ContextualUserFragment;
|
||||
use crate::context::SubagentNotification;
|
||||
use crate::init_state_db;
|
||||
use assert_matches::assert_matches;
|
||||
use codex_client::NativeIntegritySurface;
|
||||
use codex_features::Feature;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_protocol::AgentPath;
|
||||
@@ -1584,6 +1585,51 @@ async fn spawn_child_completion_notifies_parent_history() {
|
||||
assert_eq!(wait_for_subagent_notification(&parent_thread).await, true);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn spawn_thread_subagent_inherits_parent_native_integrity_surface() {
|
||||
let harness = AgentControlHarness::new().await;
|
||||
let (parent_thread_id, parent_thread) = harness.start_thread().await;
|
||||
parent_thread
|
||||
.set_app_server_client_info(
|
||||
Some("codex_desktop".to_string()),
|
||||
/*app_server_client_version*/ None,
|
||||
/*mcp_elicitations_auto_deny*/ false,
|
||||
)
|
||||
.await
|
||||
.expect("parent client info should update");
|
||||
|
||||
let child_thread_id = harness
|
||||
.control
|
||||
.spawn_agent(
|
||||
harness.config.clone(),
|
||||
text_input("hello child"),
|
||||
Some(SessionSource::SubAgent(SubAgentSource::ThreadSpawn {
|
||||
parent_thread_id,
|
||||
depth: 1,
|
||||
agent_path: None,
|
||||
agent_nickname: None,
|
||||
agent_role: Some("explorer".to_string()),
|
||||
})),
|
||||
)
|
||||
.await
|
||||
.expect("child spawn should succeed");
|
||||
let child_thread = harness
|
||||
.manager
|
||||
.get_thread(child_thread_id)
|
||||
.await
|
||||
.expect("child thread should exist");
|
||||
|
||||
assert_eq!(
|
||||
child_thread
|
||||
.codex
|
||||
.session
|
||||
.services
|
||||
.model_client
|
||||
.native_integrity_surface(),
|
||||
Some(NativeIntegritySurface::CodexDesktop)
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn multi_agent_v2_completion_ignores_dead_direct_parent() {
|
||||
let harness = AgentControlHarness::new().await;
|
||||
|
||||
@@ -62,6 +62,8 @@ use codex_api::build_session_headers;
|
||||
use codex_api::create_text_param_for_request;
|
||||
use codex_api::response_create_client_metadata;
|
||||
use codex_app_server_protocol::AuthMode;
|
||||
use codex_client::NativeIntegrityStateContext;
|
||||
use codex_client::NativeIntegritySurface;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::RefreshTokenError;
|
||||
@@ -120,6 +122,7 @@ use codex_login::auth_env_telemetry::AuthEnvTelemetry;
|
||||
use codex_login::auth_env_telemetry::collect_auth_env_telemetry;
|
||||
use codex_model_provider::SharedModelProvider;
|
||||
use codex_model_provider::create_model_provider;
|
||||
use codex_model_provider::with_native_integrity_state;
|
||||
#[cfg(test)]
|
||||
use codex_model_provider_info::DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS;
|
||||
use codex_model_provider_info::ModelProviderInfo;
|
||||
@@ -180,6 +183,7 @@ struct ModelClientState {
|
||||
beta_features_header: Option<String>,
|
||||
include_attestation: bool,
|
||||
attestation_provider: Option<Arc<dyn AttestationProvider>>,
|
||||
native_integrity_state: Option<NativeIntegrityStateContext>,
|
||||
disable_websockets: AtomicBool,
|
||||
cached_websocket_session: StdMutex<WebsocketSession>,
|
||||
}
|
||||
@@ -329,6 +333,12 @@ impl ModelClient {
|
||||
beta_features_header: Option<String>,
|
||||
attestation_provider: Option<Arc<dyn AttestationProvider>>,
|
||||
) -> Self {
|
||||
let native_integrity_state = auth_manager.as_ref().map(|auth_manager| {
|
||||
NativeIntegrityStateContext::new(
|
||||
auth_manager.codex_home().to_path_buf(),
|
||||
native_integrity_surface_for_session_source(&session_source),
|
||||
)
|
||||
});
|
||||
let model_provider = create_model_provider(provider_info, auth_manager);
|
||||
let codex_api_key_env_enabled = model_provider
|
||||
.auth_manager()
|
||||
@@ -353,6 +363,7 @@ impl ModelClient {
|
||||
beta_features_header,
|
||||
include_attestation,
|
||||
attestation_provider,
|
||||
native_integrity_state,
|
||||
disable_websockets: AtomicBool::new(false),
|
||||
cached_websocket_session: StdMutex::new(WebsocketSession::default()),
|
||||
}),
|
||||
@@ -390,6 +401,31 @@ impl ModelClient {
|
||||
self.state.provider.auth_manager()
|
||||
}
|
||||
|
||||
pub(crate) fn set_app_server_client_name(&self, client_name: Option<&str>) {
|
||||
let surface = client_name
|
||||
.map(NativeIntegritySurface::from_app_server_client_name)
|
||||
.unwrap_or(NativeIntegritySurface::CodexCli);
|
||||
self.set_native_integrity_surface(surface);
|
||||
}
|
||||
|
||||
pub(crate) fn native_integrity_surface(&self) -> Option<NativeIntegritySurface> {
|
||||
self.state
|
||||
.native_integrity_state
|
||||
.as_ref()
|
||||
.map(NativeIntegrityStateContext::surface)
|
||||
}
|
||||
|
||||
pub(crate) fn set_native_integrity_surface(&self, surface: NativeIntegritySurface) {
|
||||
if self
|
||||
.state
|
||||
.native_integrity_state
|
||||
.as_ref()
|
||||
.is_some_and(|state| state.set_surface(surface))
|
||||
{
|
||||
self.store_cached_websocket_session(WebsocketSession::default());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_window_generation(&self, window_generation: u64) {
|
||||
self.state
|
||||
.window_generation
|
||||
@@ -812,7 +848,11 @@ impl ModelClient {
|
||||
async fn current_client_setup(&self) -> Result<CurrentClientSetup> {
|
||||
let auth = self.state.provider.auth().await;
|
||||
let api_provider = self.state.provider.api_provider().await?;
|
||||
let api_auth = self.state.provider.api_auth().await?;
|
||||
let api_auth = with_native_integrity_state(
|
||||
self.state.provider.api_auth().await?,
|
||||
auth.as_ref(),
|
||||
self.state.native_integrity_state.clone(),
|
||||
);
|
||||
Ok(CurrentClientSetup {
|
||||
auth,
|
||||
api_provider,
|
||||
@@ -1736,6 +1776,21 @@ fn subagent_header_value(session_source: &SessionSource) -> Option<String> {
|
||||
}
|
||||
}
|
||||
|
||||
fn native_integrity_surface_for_session_source(
|
||||
session_source: &SessionSource,
|
||||
) -> NativeIntegritySurface {
|
||||
match session_source {
|
||||
SessionSource::Cli => NativeIntegritySurface::CodexTui,
|
||||
SessionSource::Exec => NativeIntegritySurface::CodexExec,
|
||||
SessionSource::VSCode => NativeIntegritySurface::CodexVscode,
|
||||
SessionSource::SubAgent(_)
|
||||
| SessionSource::Internal(_)
|
||||
| SessionSource::Mcp
|
||||
| SessionSource::Custom(_)
|
||||
| SessionSource::Unknown => NativeIntegritySurface::CodexCli,
|
||||
}
|
||||
}
|
||||
|
||||
fn parent_thread_id_header_value(parent_thread_id: Option<ThreadId>) -> Option<String> {
|
||||
parent_thread_id.map(|parent_thread_id| parent_thread_id.to_string())
|
||||
}
|
||||
|
||||
@@ -110,6 +110,17 @@ pub(crate) async fn run_codex_thread_interactive(
|
||||
}))
|
||||
.or_cancel(&cancel_token)
|
||||
.await??;
|
||||
if let Some(surface) = parent_session
|
||||
.services
|
||||
.model_client
|
||||
.native_integrity_surface()
|
||||
{
|
||||
codex
|
||||
.session
|
||||
.services
|
||||
.model_client
|
||||
.set_native_integrity_surface(surface);
|
||||
}
|
||||
let thread_config = codex.thread_config_snapshot().await;
|
||||
let client_metadata = parent_session.app_server_client_metadata().await;
|
||||
emit_subagent_session_started(
|
||||
|
||||
@@ -2,6 +2,7 @@ use super::*;
|
||||
use crate::mcp_tool_call::MCP_TOOL_APPROVAL_DECLINE_SYNTHETIC;
|
||||
use crate::mcp_tool_call::MCP_TOOL_APPROVAL_QUESTION_ID_PREFIX;
|
||||
use async_channel::bounded;
|
||||
use codex_client::NativeIntegritySurface;
|
||||
use codex_mcp::CODEX_APPS_MCP_SERVER_NAME;
|
||||
use codex_protocol::config_types::ApprovalsReviewer;
|
||||
use codex_protocol::models::NetworkPermissions;
|
||||
@@ -181,6 +182,40 @@ async fn run_codex_thread_interactive_respects_pre_cancelled_spawn() {
|
||||
assert!(matches!(result, Err(CodexErr::TurnAborted)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn run_codex_thread_interactive_inherits_parent_native_integrity_surface() {
|
||||
let (parent_session, parent_ctx, _rx_events) =
|
||||
crate::session::tests::make_session_and_context_with_rx().await;
|
||||
parent_session
|
||||
.services
|
||||
.model_client
|
||||
.set_native_integrity_surface(NativeIntegritySurface::CodexDesktop);
|
||||
let cancel_token = CancellationToken::new();
|
||||
|
||||
let delegated = run_codex_thread_interactive(
|
||||
parent_ctx.config.as_ref().clone(),
|
||||
Arc::clone(&parent_session.services.auth_manager),
|
||||
Arc::clone(&parent_session.services.models_manager),
|
||||
parent_session,
|
||||
parent_ctx,
|
||||
cancel_token.clone(),
|
||||
SubAgentSource::Review,
|
||||
/*initial_history*/ None,
|
||||
)
|
||||
.await
|
||||
.expect("delegate should spawn");
|
||||
|
||||
assert_eq!(
|
||||
delegated
|
||||
.session
|
||||
.services
|
||||
.model_client
|
||||
.native_integrity_surface(),
|
||||
Some(NativeIntegritySurface::CodexDesktop)
|
||||
);
|
||||
cancel_token.cancel();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn handle_request_permissions_uses_tool_call_id_for_round_trip() {
|
||||
let (parent_session, parent_ctx, rx_events) =
|
||||
|
||||
@@ -781,6 +781,10 @@ impl Codex {
|
||||
app_server_client_version: Option<String>,
|
||||
mcp_elicitations_auto_deny: bool,
|
||||
) -> ConstraintResult<()> {
|
||||
self.session
|
||||
.services
|
||||
.model_client
|
||||
.set_app_server_client_name(app_server_client_name.as_deref());
|
||||
self.session
|
||||
.update_settings(SessionSettingsUpdate {
|
||||
app_server_client_name,
|
||||
|
||||
Reference in New Issue
Block a user