mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Trust hooks from materialized workspace plugins (#32301)
## What changed - Carry newly installed or updated remote plugin metadata through effective-plugin refresh callbacks, including coalesced refreshes. - After a successful refresh, record the current hook hashes for listed workspace plugins associated with the active account while preserving existing hook settings and unrelated state. - Serialize the background trust write with config mutations, and leave hooks untrusted if the write fails or the active account changes. ## Testing - Cover eligible plugin selection and escaped hook config keys. - Verify end-to-end trust for newly materialized plugin hooks, preservation of existing config, and fail-closed behavior when config cannot be written. GitOrigin-RevId: 0b150766415be6fccc117f7856a241e10ad456f2
This commit is contained in:
1 parent
dffe1f02a3
commit
076a110eb8
13 files changed
+815
-139
No files matched your search
@@ -44,7 +44,9 @@ use crate::marketplace_upgrade::upgrade_configured_git_marketplaces;
|
||||
use crate::remote::REMOTE_GLOBAL_MARKETPLACE_NAME;
|
||||
use crate::remote::RecommendedPluginsMode;
|
||||
use crate::remote::RemoteInstalledPlugin;
|
||||
use crate::remote::RemoteInstalledPluginBundleSyncOutcome;
|
||||
use crate::remote::RemotePluginCatalogError;
|
||||
use crate::remote::RemotePluginMaterialization;
|
||||
use crate::remote::RemotePluginServiceConfig;
|
||||
use crate::remote_legacy::RemotePluginFetchError;
|
||||
use crate::remote_legacy::RemotePluginMutationError;
|
||||
@@ -104,6 +106,8 @@ static CURATED_REPO_SYNC_STARTED: AtomicBool = AtomicBool::new(false);
|
||||
const FEATURED_PLUGIN_IDS_CACHE_TTL: std::time::Duration =
|
||||
std::time::Duration::from_secs(60 * 60 * 3);
|
||||
|
||||
type EffectivePluginsChangedCallback = Arc<dyn Fn(EffectivePluginsChange) + Send + Sync + 'static>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PluginsConfigInput {
|
||||
pub config_layer_stack: ConfigLayerStack,
|
||||
@@ -128,6 +132,13 @@ impl PluginsConfigInput {
|
||||
}
|
||||
}
|
||||
|
||||
/// Effective-plugin changes that downstream composition layers may act on.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct EffectivePluginsChange {
|
||||
/// Remote bundles installed or updated by background installed-plugin sync.
|
||||
pub materialized_remote_plugins: Vec<RemotePluginMaterialization>,
|
||||
}
|
||||
|
||||
/// Inputs used to select endpoint-backed plugin install candidates.
|
||||
pub struct RecommendedPluginCandidatesInput<'a> {
|
||||
pub plugins_config: &'a PluginsConfigInput,
|
||||
@@ -163,7 +174,8 @@ struct RemoteInstalledPluginsCacheRefreshRequest {
|
||||
notify: RemoteInstalledPluginsCacheRefreshNotify,
|
||||
// App-server attaches side effects such as skills metadata invalidation and MCP refreshes when
|
||||
// remote installed state changes.
|
||||
on_effective_plugins_changed: Option<Arc<dyn Fn() + Send + Sync + 'static>>,
|
||||
on_effective_plugins_changed: Option<EffectivePluginsChangedCallback>,
|
||||
change: EffectivePluginsChange,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -879,7 +891,7 @@ impl PluginsManager {
|
||||
config: &PluginsConfigInput,
|
||||
auth: Option<&CodexAuth>,
|
||||
visible_marketplaces: &[&str],
|
||||
on_effective_plugins_changed: Option<Arc<dyn Fn() + Send + Sync + 'static>>,
|
||||
on_effective_plugins_changed: Option<EffectivePluginsChangedCallback>,
|
||||
) -> Result<Vec<crate::remote::RemoteMarketplace>, RemotePluginCatalogError> {
|
||||
let plugins = crate::remote::fetch_remote_installed_plugins(
|
||||
&remote_plugin_service_config(config),
|
||||
@@ -892,7 +904,7 @@ impl PluginsManager {
|
||||
);
|
||||
let changed = self.write_remote_installed_plugins_cache(plugins);
|
||||
if changed && let Some(on_effective_plugins_changed) = on_effective_plugins_changed {
|
||||
on_effective_plugins_changed();
|
||||
on_effective_plugins_changed(EffectivePluginsChange::default());
|
||||
}
|
||||
Ok(marketplaces)
|
||||
}
|
||||
@@ -929,13 +941,14 @@ impl PluginsManager {
|
||||
self: &Arc<Self>,
|
||||
config: &PluginsConfigInput,
|
||||
auth: Option<CodexAuth>,
|
||||
on_effective_plugins_changed: Option<Arc<dyn Fn() + Send + Sync + 'static>>,
|
||||
on_effective_plugins_changed: Option<EffectivePluginsChangedCallback>,
|
||||
) {
|
||||
self.maybe_start_remote_installed_plugins_cache_refresh_with_notify(
|
||||
config,
|
||||
auth.clone(),
|
||||
RemoteInstalledPluginsCacheRefreshNotify::IfCacheChanged,
|
||||
on_effective_plugins_changed,
|
||||
EffectivePluginsChange::default(),
|
||||
);
|
||||
|
||||
let manager = Arc::clone(self);
|
||||
@@ -951,13 +964,14 @@ impl PluginsManager {
|
||||
self: &Arc<Self>,
|
||||
config: &PluginsConfigInput,
|
||||
auth: Option<CodexAuth>,
|
||||
on_effective_plugins_changed: Option<Arc<dyn Fn() + Send + Sync + 'static>>,
|
||||
on_effective_plugins_changed: Option<EffectivePluginsChangedCallback>,
|
||||
) {
|
||||
self.maybe_start_remote_installed_plugins_cache_refresh_with_notify(
|
||||
config,
|
||||
auth,
|
||||
RemoteInstalledPluginsCacheRefreshNotify::AfterSuccessfulRefresh,
|
||||
on_effective_plugins_changed,
|
||||
EffectivePluginsChange::default(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -966,7 +980,8 @@ impl PluginsManager {
|
||||
config: &PluginsConfigInput,
|
||||
auth: Option<CodexAuth>,
|
||||
notify: RemoteInstalledPluginsCacheRefreshNotify,
|
||||
on_effective_plugins_changed: Option<Arc<dyn Fn() + Send + Sync + 'static>>,
|
||||
on_effective_plugins_changed: Option<EffectivePluginsChangedCallback>,
|
||||
change: EffectivePluginsChange,
|
||||
) {
|
||||
if !config.plugins_enabled {
|
||||
return;
|
||||
@@ -978,6 +993,7 @@ impl PluginsManager {
|
||||
auth,
|
||||
notify,
|
||||
on_effective_plugins_changed,
|
||||
change,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -986,7 +1002,7 @@ impl PluginsManager {
|
||||
self: &Arc<Self>,
|
||||
config: &PluginsConfigInput,
|
||||
auth: Option<CodexAuth>,
|
||||
on_effective_plugins_changed: Option<Arc<dyn Fn() + Send + Sync + 'static>>,
|
||||
on_effective_plugins_changed: Option<EffectivePluginsChangedCallback>,
|
||||
) {
|
||||
if !config.plugins_enabled {
|
||||
return;
|
||||
@@ -995,13 +1011,18 @@ impl PluginsManager {
|
||||
let manager = Arc::clone(self);
|
||||
let config_for_refresh = config.clone();
|
||||
let auth_for_refresh = auth.clone();
|
||||
let on_local_cache_changed = Arc::new(move || {
|
||||
manager.maybe_start_remote_installed_plugins_cache_refresh_after_mutation(
|
||||
&config_for_refresh,
|
||||
auth_for_refresh.clone(),
|
||||
on_effective_plugins_changed.clone(),
|
||||
);
|
||||
});
|
||||
let on_local_cache_changed =
|
||||
Arc::new(move |outcome: RemoteInstalledPluginBundleSyncOutcome| {
|
||||
manager.maybe_start_remote_installed_plugins_cache_refresh_with_notify(
|
||||
&config_for_refresh,
|
||||
auth_for_refresh.clone(),
|
||||
RemoteInstalledPluginsCacheRefreshNotify::AfterSuccessfulRefresh,
|
||||
on_effective_plugins_changed.clone(),
|
||||
EffectivePluginsChange {
|
||||
materialized_remote_plugins: outcome.materialized_remote_plugins,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
crate::remote::maybe_start_remote_installed_plugin_bundle_sync(
|
||||
self.codex_home.clone(),
|
||||
@@ -1032,7 +1053,7 @@ impl PluginsManager {
|
||||
auth: Option<CodexAuth>,
|
||||
roots: &[AbsolutePathBuf],
|
||||
options: PluginListBackgroundTaskOptions,
|
||||
on_effective_plugins_changed: Option<Arc<dyn Fn() + Send + Sync + 'static>>,
|
||||
on_effective_plugins_changed: Option<EffectivePluginsChangedCallback>,
|
||||
) {
|
||||
self.maybe_start_non_curated_plugin_cache_refresh(config, roots);
|
||||
if options.refresh_global_remote_catalog_cache {
|
||||
@@ -1936,7 +1957,7 @@ impl PluginsManager {
|
||||
self: &Arc<Self>,
|
||||
config: &PluginsConfigInput,
|
||||
auth_manager: Arc<AuthManager>,
|
||||
on_effective_plugins_changed: Option<Arc<dyn Fn() + Send + Sync + 'static>>,
|
||||
on_effective_plugins_changed: Option<EffectivePluginsChangedCallback>,
|
||||
) {
|
||||
if config.plugins_enabled {
|
||||
let use_remote_global_catalog =
|
||||
@@ -2139,10 +2160,35 @@ impl PluginsManager {
|
||||
request.notify =
|
||||
RemoteInstalledPluginsCacheRefreshNotify::AfterSuccessfulRefresh;
|
||||
}
|
||||
if request.on_effective_plugins_changed.is_none() {
|
||||
if !existing_request
|
||||
.change
|
||||
.materialized_remote_plugins
|
||||
.is_empty()
|
||||
&& let Some(existing_callback) =
|
||||
existing_request.on_effective_plugins_changed.as_ref()
|
||||
{
|
||||
request.on_effective_plugins_changed = Some(Arc::clone(existing_callback));
|
||||
} else if request.on_effective_plugins_changed.is_none() {
|
||||
request.on_effective_plugins_changed =
|
||||
existing_request.on_effective_plugins_changed.clone();
|
||||
}
|
||||
for materialization in &existing_request.change.materialized_remote_plugins {
|
||||
if !request
|
||||
.change
|
||||
.materialized_remote_plugins
|
||||
.iter()
|
||||
.any(|pending| pending.plugin_id == materialization.plugin_id)
|
||||
{
|
||||
request
|
||||
.change
|
||||
.materialized_remote_plugins
|
||||
.push(materialization.clone());
|
||||
}
|
||||
}
|
||||
request
|
||||
.change
|
||||
.materialized_remote_plugins
|
||||
.sort_by_key(|materialization| materialization.plugin_id.as_key());
|
||||
}
|
||||
state.requested = Some(request);
|
||||
if state.in_flight {
|
||||
@@ -2366,6 +2412,7 @@ impl PluginsManager {
|
||||
// publishing remote installed state as effective local plugin config.
|
||||
let changed = self.write_remote_installed_plugins_cache(installed_plugins);
|
||||
let should_notify = changed
|
||||
|| !request.change.materialized_remote_plugins.is_empty()
|
||||
|| matches!(
|
||||
request.notify,
|
||||
RemoteInstalledPluginsCacheRefreshNotify::AfterSuccessfulRefresh
|
||||
@@ -2374,7 +2421,7 @@ impl PluginsManager {
|
||||
&& let Some(on_effective_plugins_changed) =
|
||||
request.on_effective_plugins_changed
|
||||
{
|
||||
on_effective_plugins_changed();
|
||||
on_effective_plugins_changed(request.change);
|
||||
}
|
||||
}
|
||||
Err(
|
||||
@@ -2386,12 +2433,16 @@ impl PluginsManager {
|
||||
&& let Some(on_effective_plugins_changed) =
|
||||
request.on_effective_plugins_changed
|
||||
{
|
||||
on_effective_plugins_changed();
|
||||
on_effective_plugins_changed(EffectivePluginsChange::default());
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(
|
||||
error = %err,
|
||||
materialized_remote_plugin_count = request
|
||||
.change
|
||||
.materialized_remote_plugins
|
||||
.len(),
|
||||
"failed to refresh remote installed plugins cache"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user