Skip to content

Conversation

@shijie-oai
Copy link
Collaborator

@shijie-oai shijie-oai commented Jan 6, 2026

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

Screenshot 2026-01-06 at 11 32 06 PM

App server response

Screenshot 2026-01-06 at 11 30 18 PM

@shijie-oai shijie-oai force-pushed the shijie/app-server-requirement-list branch from 4e9dff8 to 336891b Compare January 7, 2026 06:19
]),
allowed_sandbox_modes: Some(vec![
CoreSandboxModeRequirement::ReadOnly,
CoreSandboxModeRequirement::ExternalSandbox,
Copy link
Collaborator Author

@shijie-oai shijie-oai Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little bit counterintuitive - ExternalSandbox is not supported in config.toml but it is in requirements.toml. Because here we are doing a conversion to sandbox mode applicable to VSCE only for rendering purpose (which does not support ExternalSandbox mode anyway), we will drop this value from the allow list.

#[ts(export_to = "v2/")]
pub struct Requirements {
pub allowed_approval_policies: Option<Vec<AskForApproval>>,
pub allowed_sandbox_modes: Option<Vec<SandboxMode>>,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now we are not support any additional params for workspace-write because requirements.toml does not support that yet either.

@shijie-oai shijie-oai marked this pull request as ready for review January 7, 2026 07:32
@shijie-oai shijie-oai force-pushed the shijie/app-server-requirement-list branch from 336891b to 12aa81e Compare January 7, 2026 07:33
Comment on lines 219 to 224
async fn handle_requirement_list(&self, request_id: RequestId) {
match self.config_api.requirement_list().await {
Ok(response) => self.outgoing.send_response(request_id, response).await,
Err(error) => self.outgoing.send_error(request_id, error).await,
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me know if folks prefer this in codex_message_processor.

Copy link
Collaborator Author

@shijie-oai shijie-oai Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also did not use a tokio::spawn because I don't expect this to take long and therefore not blocking.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fine to put it here alongside the config RPCs, there's a reason for this that I can't quite remember

- `config/read` — fetch the effective config on disk after resolving config layering.
- `config/value/write` — write a single config key/value to the user's config.toml on disk.
- `config/batchWrite` — apply multiple config edits atomically to the user's config.toml on disk.
- `requirements/list` — fetch the loaded requirements allow-lists from `requirements.toml` and/or MDM (or `null` if none are configured).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely one valid option. But it would require re-implementing the "is this value selectable" on the front end and corresponding error messages.

An alternate approach could be to implement config/value/canWrite (as a compliment to config/value/write) -- this is how the CLI works at the moment: when you present each value to the user, you first test if that value is acceptable. Then the validation logic is only implemented once. See here:

let disabled_reason = match self.config.approval_policy.can_set(&preset.approval) {

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely one valid option. But it would require re-implementing the "is this value selectable" on the front end and corresponding error messages.

I think we are okay with updating the frontend rendering logic.

An alternate approach could be to implement config/value/canWrite (as a compliment to config/value/write) -- this is how the CLI works at the moment: when you present each value to the user, you first test if that value is acceptable. Then the validation logic is only implemented once.

hm this is an interesting approach, instead of getting an allowlist, we ask to see what is allowed on rendering. Also to clarify, we do not hide approval policies in CLI currently if it is not allowed. It does render an error when we attempt to select.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chatted offline - I will stick with the current approach for now and we can consider alternative options when there is a need for it.

Copy link
Contributor

@gt-oai gt-oai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API LGTM assuming we want to keep requirements/list instead of either (1) putting it on ConfigReadResponse or (2) implementing as config/value/canWrite (discussed offline).

You may want an approval from someone who knows more about app-server.

@@ -446,4 +444,4 @@
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should requirements just be a field on this? Rather than a new endpoint?

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct Requirements {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: rename this ConfigRequirements?


let requirements = layers.requirements_toml().clone();
if requirements.allowed_approval_policies.is_none()
&& requirements.allowed_sandbox_modes.is_none()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be nice to have an is_empty() helper defined on ConfigRequirementsToml that checks for these fields, otherwise seems easy to miss this code when we add new fields to it

Copy link
Collaborator

@owenlin0 owenlin0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small comments but otherwise lgtm

@shijie-oai shijie-oai force-pushed the shijie/app-server-requirement-list branch from cc1d4e4 to f8f33ee Compare January 7, 2026 20:15
@shijie-oai shijie-oai merged commit efd0c21 into main Jan 7, 2026
26 checks passed
@shijie-oai shijie-oai deleted the shijie/app-server-requirement-list branch January 7, 2026 21:57
@github-actions github-actions bot locked and limited conversation to collaborators Jan 7, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants