mirror of
https://github.com/openai/codex.git
synced 2026-11-07 20:30:33 +00:00
Enforce required environment skills before model inference (#51157)
## What changed - Accept per-environment `skills.required` lists through `environment/add` and `environments.toml`. - Before inference, require each named skill to be enabled and supplied by the selected environment. Missing skills fail the turn before a model request, including when environment setup fails. - Stop applying an environment's requirements after it is deselected. Exempt isolated Guardian reviewers, which have no skill catalog. ## Testing Add tests for available, missing, disabled, and wrong-environment skills, failed environment setup, recovery after deselection, and propagation of TOML requirements to the correct environment. GitOrigin-RevId: 302ced4774697d2f414f745fe0535e71f747eda2
This commit is contained in:
committed by
copyberry
parent
c9253c4977
commit
42312d4ff4
@@ -10,6 +10,7 @@ use crate::ExecServerClientConnectOptions;
|
||||
use crate::ExecServerError;
|
||||
use crate::client::LazyRemoteExecServerClient;
|
||||
use axum::extract::ws::WebSocket;
|
||||
use codex_config::ScopedSkillsConfig;
|
||||
use codex_http_client::HttpClientFactory;
|
||||
|
||||
impl EnvironmentManager {
|
||||
@@ -29,7 +30,9 @@ impl EnvironmentManager {
|
||||
let client =
|
||||
LazyRemoteExecServerClient::from_connected(client, http_client_factory.clone());
|
||||
let environment = Arc::new(Environment::remote_with_client(
|
||||
client, /*local_runtime_paths*/ None,
|
||||
client,
|
||||
/*local_runtime_paths*/ None,
|
||||
ScopedSkillsConfig::default(),
|
||||
));
|
||||
Ok(Self {
|
||||
default_environment: Some(environment_id.clone()),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
|
||||
use codex_config::ScopedSkillsConfig;
|
||||
use codex_http_client::HttpClientFactory;
|
||||
use codex_http_client::OutboundProxyPolicy;
|
||||
use futures::SinkExt;
|
||||
@@ -59,6 +60,7 @@ fn trusted_headers_require_tls_for_non_loopback_destinations() {
|
||||
"customer-session".to_string(),
|
||||
)]),
|
||||
},
|
||||
ScopedSkillsConfig::default(),
|
||||
)
|
||||
.expect_err("trusted headers must not be sent over an insecure remote connection");
|
||||
|
||||
@@ -86,6 +88,7 @@ fn duplicate_case_insensitive_websocket_headers_fail_before_registration() {
|
||||
("x-session-id".to_string(), "second-session".to_string()),
|
||||
]),
|
||||
},
|
||||
ScopedSkillsConfig::default(),
|
||||
)
|
||||
.expect_err("duplicate header names must fail regardless of case");
|
||||
|
||||
@@ -110,6 +113,7 @@ fn invalid_websocket_header_names_fail_before_registration() {
|
||||
connect_timeout: None,
|
||||
http_headers: HashMap::from([("bad header".to_string(), "value".to_string())]),
|
||||
},
|
||||
ScopedSkillsConfig::default(),
|
||||
)
|
||||
.expect_err("invalid header name should fail");
|
||||
|
||||
@@ -137,6 +141,7 @@ fn invalid_websocket_header_values_fail_before_registration() {
|
||||
"customer\nspoofed".to_string(),
|
||||
)]),
|
||||
},
|
||||
ScopedSkillsConfig::default(),
|
||||
)
|
||||
.expect_err("invalid header value should fail");
|
||||
|
||||
@@ -162,6 +167,7 @@ fn websocket_controlled_headers_fail_before_registration() {
|
||||
connect_timeout: None,
|
||||
http_headers: HashMap::from([(header.to_string(), "overridden".to_string())]),
|
||||
},
|
||||
ScopedSkillsConfig::default(),
|
||||
)
|
||||
.expect_err("connection-controlled header should fail");
|
||||
|
||||
@@ -228,6 +234,7 @@ async fn trusted_headers_are_sent_on_initial_websocket_and_session_reconnect() {
|
||||
),
|
||||
]),
|
||||
},
|
||||
ScopedSkillsConfig::default(),
|
||||
)
|
||||
.expect("environment with routing headers should register");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user