Add runtime extra skill roots API (#24977)

## Summary
- Add v2 `skills/extraRoots/set` to replace app-server process-local
standalone skill roots. The setting is not persisted, accepts missing
roots, and `extraRoots: []` clears the runtime set.
- Wire runtime roots into core skill discovery for `skills/list` and
turn loads, clear skill caches on set, and register the roots with the
skills watcher so later filesystem changes emit `skills/changed`.
- Update app-server docs, generated JSON/TypeScript schemas, and
coverage for serialization, missing roots, empty clears, and restart
behavior.

## Testing
- `cargo test -p codex-app-server-protocol`
- `cargo test -p codex-core-skills`
- `cargo test -p codex-app-server
skills_extra_roots_set_updates_process_runtime_roots`
- `just fix -p codex-app-server-protocol`
- `just fix -p codex-core-skills`
- `just fix -p codex-app-server`
This commit is contained in:
xl-openai authored and GitHub committed 2026-05-28 21:14:34 -07:00
1 parent 42c80385cd
commit f0a839ea0c
23 files changed
+632 -4

No files matched your search

@@ -4,6 +4,8 @@ use futures::StreamExt;
#[derive(Clone)]
pub(crate) struct CatalogRequestProcessor {
pub(super) outgoing: Arc<OutgoingMessageSender>,
pub(super) skills_watcher: Arc<SkillsWatcher>,
pub(super) auth_manager: Arc<AuthManager>,
pub(super) thread_manager: Arc<ThreadManager>,
pub(super) config: Arc<Config>,
@@ -96,6 +98,8 @@ fn errors_to_info(
impl CatalogRequestProcessor {
pub(crate) fn new(
outgoing: Arc<OutgoingMessageSender>,
skills_watcher: Arc<SkillsWatcher>,
auth_manager: Arc<AuthManager>,
thread_manager: Arc<ThreadManager>,
config: Arc<Config>,
@@ -103,6 +107,8 @@ impl CatalogRequestProcessor {
workspace_settings_cache: Arc<workspace_settings::WorkspaceSettingsCache>,
) -> Self {
Self {
outgoing,
skills_watcher,
auth_manager,
thread_manager,
config,
@@ -138,6 +144,15 @@ impl CatalogRequestProcessor {
.map(|response| Some(response.into()))
}
pub(crate) async fn skills_extra_roots_set(
&self,
params: SkillsExtraRootsSetParams,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
self.skills_extra_roots_set_response(params)
.await
.map(|response| Some(response.into()))
}
pub(crate) async fn model_list(
&self,
params: ModelListParams,
@@ -567,6 +582,24 @@ impl CatalogRequestProcessor {
Ok(SkillsListResponse { data })
}
async fn skills_extra_roots_set_response(
&self,
params: SkillsExtraRootsSetParams,
) -> Result<SkillsExtraRootsSetResponse, JSONRPCErrorError> {
let SkillsExtraRootsSetParams { extra_roots } = params;
self.skills_watcher
.register_runtime_extra_roots(&extra_roots);
self.thread_manager
.skills_manager()
.set_extra_roots(extra_roots);
self.outgoing
.send_server_notification(ServerNotification::SkillsChanged(
codex_app_server_protocol::SkillsChangedNotification {},
))
.await;
Ok(SkillsExtraRootsSetResponse {})
}
/// Handle `hooks/list` by resolving hooks for each requested cwd.
async fn hooks_list_response(
&self,