Feat: appServer.requirementList for requirement.toml (#8800)

### Summary
We are exposing requirements via `requirement/list` method from
app-server so that we can conditionally disable the agent mode dropdown
selection in VSCE and correctly setting the default value.

### Sample output
#### `etc/codex/requirements.toml`
<img width="497" height="49" alt="Screenshot 2026-01-06 at 11 32 06 PM"
src="https://github.com/user-attachments/assets/fbd9402e-515f-4b9e-a158-2abb23e866a0"
/>

#### App server response
<img width="1107" height="79" alt="Screenshot 2026-01-06 at 11 30 18 PM"
src="https://github.com/user-attachments/assets/c0d669cd-54ef-4789-a26c-adb2c41950af"
/>
This commit is contained in:
Shijie Rao
2026-01-07 13:57:44 -08:00
committed by GitHub
parent 61e81af887
commit efd0c21b9b
12 changed files with 191 additions and 5 deletions

View File

@@ -58,6 +58,10 @@ impl From<SandboxMode> for SandboxModeRequirement {
}
impl ConfigRequirementsToml {
pub fn is_empty(&self) -> bool {
self.allowed_approval_policies.is_none() && self.allowed_sandbox_modes.is_none()
}
/// For every field in `other` that is `Some`, if the corresponding field in
/// `self` is `None`, copy the value from `other` into `self`.
pub fn merge_unset_fields(&mut self, mut other: ConfigRequirementsToml) {

View File

@@ -12,7 +12,6 @@ mod tests;
use crate::config::CONFIG_TOML_FILE;
use crate::config::ConfigToml;
use crate::config_loader::config_requirements::ConfigRequirementsToml;
use crate::config_loader::layer_io::LoadedConfigLayers;
use codex_app_server_protocol::ConfigLayerSource;
use codex_protocol::config_types::SandboxMode;
@@ -25,6 +24,8 @@ use std::path::Path;
use toml::Value as TomlValue;
pub use config_requirements::ConfigRequirements;
pub use config_requirements::ConfigRequirementsToml;
pub use config_requirements::SandboxModeRequirement;
pub use merge::merge_toml_values;
pub use state::ConfigLayerEntry;
pub use state::ConfigLayerStack;
@@ -201,7 +202,9 @@ pub async fn load_config_layers_state(
));
}
ConfigLayerStack::new(layers, config_requirements_toml.try_into()?)
let requirements_toml = config_requirements_toml.clone();
let requirements = config_requirements_toml.try_into()?;
ConfigLayerStack::new(layers, requirements, requirements_toml)
}
/// Attempts to load a config.toml file from `config_toml`.

View File

@@ -1,4 +1,5 @@
use crate::config_loader::ConfigRequirements;
use crate::config_loader::ConfigRequirementsToml;
use super::fingerprint::record_origins;
use super::fingerprint::version_for_toml;
@@ -86,18 +87,25 @@ pub struct ConfigLayerStack {
/// Constraints that must be enforced when deriving a [Config] from the
/// layers.
requirements: ConfigRequirements,
/// Raw requirements data as loaded from requirements.toml/MDM/legacy
/// sources. This preserves the original allow-lists so they can be
/// surfaced via APIs.
requirements_toml: ConfigRequirementsToml,
}
impl ConfigLayerStack {
pub fn new(
layers: Vec<ConfigLayerEntry>,
requirements: ConfigRequirements,
requirements_toml: ConfigRequirementsToml,
) -> std::io::Result<Self> {
let user_layer_index = verify_layer_ordering(&layers)?;
Ok(Self {
layers,
user_layer_index,
requirements,
requirements_toml,
})
}
@@ -111,6 +119,10 @@ impl ConfigLayerStack {
&self.requirements
}
pub fn requirements_toml(&self) -> &ConfigRequirementsToml {
&self.requirements_toml
}
/// Creates a new [ConfigLayerStack] using the specified values to inject a
/// "user layer" into the stack. If such a layer already exists, it is
/// replaced; otherwise, it is inserted into the stack at the appropriate
@@ -131,6 +143,7 @@ impl ConfigLayerStack {
layers,
user_layer_index: self.user_layer_index,
requirements: self.requirements.clone(),
requirements_toml: self.requirements_toml.clone(),
}
}
None => {
@@ -151,6 +164,7 @@ impl ConfigLayerStack {
layers,
user_layer_index: Some(user_layer_index),
requirements: self.requirements.clone(),
requirements_toml: self.requirements_toml.clone(),
}
}
}