Move skills watcher to app-server (#21287)

## Why

Skills update notifications are app-server API behavior, but the watcher
lived in `codex-core` and surfaced through
`EventMsg::SkillsUpdateAvailable`. Moving the watcher out keeps core
focused on thread execution and lets app-server own both cache
invalidation and the `skills/changed` notification.

## What changed

- Added an app-server-owned skills watcher that watches local skill
roots, clears the shared skills cache, and emits `skills/changed`
directly.
- Registers skill watches from the common app-server thread listener
attach path, including direct starts, resumes, and app-server-observed
child or forked threads.
- Stores the `WatchRegistration` on `ThreadState`, so listener
replacement, thread teardown, idle unload, and app-server shutdown
deregister by dropping the RAII guard.
- Removed `EventMsg::SkillsUpdateAvailable`, the core watcher, and the
old core live-reload test.
- Extended the app-server skills change test to verify a cached skills
list is refreshed after a filesystem change without forcing reload.

## Validation

- `cargo check -p codex-core -p codex-app-server -p codex-mcp-server -p
codex-rollout -p codex-rollout-trace`
- `cargo test -p codex-app-server
skills_changed_notification_is_emitted_after_skill_change`
This commit is contained in:
pakrym-oai
2026-05-06 15:38:11 -07:00
committed by GitHub
parent 8f5d68f9d2
commit d5eea229cc
27 changed files with 198 additions and 418 deletions
+5 -4
View File
@@ -1,6 +1,5 @@
use crate::agent::AgentStatus;
use crate::config::ConstraintResult;
use crate::file_watcher::WatchRegistration;
use crate::goals::ExternalGoalSet;
use crate::goals::GoalRuntimeEvent;
use crate::session::Codex;
@@ -31,6 +30,7 @@ use codex_protocol::protocol::Submission;
use codex_protocol::protocol::ThreadMemoryMode;
use codex_protocol::protocol::ThreadSource;
use codex_protocol::protocol::TokenUsageInfo;
use codex_protocol::protocol::TurnEnvironmentSelection;
use codex_protocol::protocol::W3cTraceContext;
use codex_protocol::user_input::UserInput;
use codex_thread_store::StoredThread;
@@ -101,7 +101,6 @@ pub struct CodexThread {
session_configured: SessionConfiguredEvent,
rollout_path: Option<PathBuf>,
out_of_band_elicitation_count: Mutex<u64>,
_watch_registration: WatchRegistration,
}
/// Conduit for the bidirectional stream of messages that compose a thread
@@ -112,7 +111,6 @@ impl CodexThread {
session_configured: SessionConfiguredEvent,
rollout_path: Option<PathBuf>,
session_source: SessionSource,
watch_registration: WatchRegistration,
) -> Self {
Self {
codex,
@@ -120,7 +118,6 @@ impl CodexThread {
session_configured,
rollout_path,
out_of_band_elicitation_count: Mutex::new(0),
_watch_registration: watch_registration,
}
}
@@ -464,6 +461,10 @@ impl CodexThread {
self.codex.session.get_config().await
}
pub async fn environment_selections(&self) -> Vec<TurnEnvironmentSelection> {
self.codex.thread_environment_selections().await
}
pub async fn read_mcp_resource(
&self,
server: &str,