chore: use AVAILABLE and ON_INSTALL as default plugin install and auth policies (#14407)

make `AVAILABLE` the default plugin installPolicy when unset in
`marketplace.json`. similarly, make `ON_INSTALL` the default authPolicy.

this means, when unset, plugins are available to be installed (but not
auto-installed), and the contained connectors will be authed at
install-time.

updated tests.
This commit is contained in:
sayan-oai 2026-03-11 20:33:17 -07:00 committed by GitHub
parent 5bc82c5b93
commit 917c2df201
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 111 additions and 133 deletions

View file

@ -12805,18 +12805,12 @@
"type": "array"
},
"authPolicy": {
"anyOf": [
{
"$ref": "#/definitions/v2/PluginAuthPolicy"
},
{
"type": "null"
}
]
"$ref": "#/definitions/v2/PluginAuthPolicy"
}
},
"required": [
"appsNeedingAuth"
"appsNeedingAuth",
"authPolicy"
],
"title": "PluginInstallResponse",
"type": "object"
@ -13014,14 +13008,7 @@
"PluginSummary": {
"properties": {
"authPolicy": {
"anyOf": [
{
"$ref": "#/definitions/v2/PluginAuthPolicy"
},
{
"type": "null"
}
]
"$ref": "#/definitions/v2/PluginAuthPolicy"
},
"enabled": {
"type": "boolean"
@ -13030,14 +13017,7 @@
"type": "string"
},
"installPolicy": {
"anyOf": [
{
"$ref": "#/definitions/v2/PluginInstallPolicy"
},
{
"type": "null"
}
]
"$ref": "#/definitions/v2/PluginInstallPolicy"
},
"installed": {
"type": "boolean"
@ -13060,8 +13040,10 @@
}
},
"required": [
"authPolicy",
"enabled",
"id",
"installPolicy",
"installed",
"name",
"source"

View file

@ -9153,18 +9153,12 @@
"type": "array"
},
"authPolicy": {
"anyOf": [
{
"$ref": "#/definitions/PluginAuthPolicy"
},
{
"type": "null"
}
]
"$ref": "#/definitions/PluginAuthPolicy"
}
},
"required": [
"appsNeedingAuth"
"appsNeedingAuth",
"authPolicy"
],
"title": "PluginInstallResponse",
"type": "object"
@ -9362,14 +9356,7 @@
"PluginSummary": {
"properties": {
"authPolicy": {
"anyOf": [
{
"$ref": "#/definitions/PluginAuthPolicy"
},
{
"type": "null"
}
]
"$ref": "#/definitions/PluginAuthPolicy"
},
"enabled": {
"type": "boolean"
@ -9378,14 +9365,7 @@
"type": "string"
},
"installPolicy": {
"anyOf": [
{
"$ref": "#/definitions/PluginInstallPolicy"
},
{
"type": "null"
}
]
"$ref": "#/definitions/PluginInstallPolicy"
},
"installed": {
"type": "boolean"
@ -9408,8 +9388,10 @@
}
},
"required": [
"authPolicy",
"enabled",
"id",
"installPolicy",
"installed",
"name",
"source"

View file

@ -45,18 +45,12 @@
"type": "array"
},
"authPolicy": {
"anyOf": [
{
"$ref": "#/definitions/PluginAuthPolicy"
},
{
"type": "null"
}
]
"$ref": "#/definitions/PluginAuthPolicy"
}
},
"required": [
"appsNeedingAuth"
"appsNeedingAuth",
"authPolicy"
],
"title": "PluginInstallResponse",
"type": "object"

View file

@ -170,14 +170,7 @@
"PluginSummary": {
"properties": {
"authPolicy": {
"anyOf": [
{
"$ref": "#/definitions/PluginAuthPolicy"
},
{
"type": "null"
}
]
"$ref": "#/definitions/PluginAuthPolicy"
},
"enabled": {
"type": "boolean"
@ -186,14 +179,7 @@
"type": "string"
},
"installPolicy": {
"anyOf": [
{
"$ref": "#/definitions/PluginInstallPolicy"
},
{
"type": "null"
}
]
"$ref": "#/definitions/PluginInstallPolicy"
},
"installed": {
"type": "boolean"
@ -216,8 +202,10 @@
}
},
"required": [
"authPolicy",
"enabled",
"id",
"installPolicy",
"installed",
"name",
"source"

View file

@ -4,4 +4,4 @@
import type { AppSummary } from "./AppSummary";
import type { PluginAuthPolicy } from "./PluginAuthPolicy";
export type PluginInstallResponse = { authPolicy: PluginAuthPolicy | null, appsNeedingAuth: Array<AppSummary>, };
export type PluginInstallResponse = { authPolicy: PluginAuthPolicy, appsNeedingAuth: Array<AppSummary>, };

View file

@ -6,4 +6,4 @@ import type { PluginInstallPolicy } from "./PluginInstallPolicy";
import type { PluginInterface } from "./PluginInterface";
import type { PluginSource } from "./PluginSource";
export type PluginSummary = { id: string, name: string, source: PluginSource, installed: boolean, enabled: boolean, installPolicy: PluginInstallPolicy | null, authPolicy: PluginAuthPolicy | null, interface: PluginInterface | null, };
export type PluginSummary = { id: string, name: string, source: PluginSource, installed: boolean, enabled: boolean, installPolicy: PluginInstallPolicy, authPolicy: PluginAuthPolicy, interface: PluginInterface | null, };

View file

@ -3087,8 +3087,8 @@ pub struct PluginSummary {
pub source: PluginSource,
pub installed: bool,
pub enabled: bool,
pub install_policy: Option<PluginInstallPolicy>,
pub auth_policy: Option<PluginAuthPolicy>,
pub install_policy: PluginInstallPolicy,
pub auth_policy: PluginAuthPolicy,
pub interface: Option<PluginInterface>,
}
@ -3149,7 +3149,7 @@ pub struct PluginInstallParams {
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct PluginInstallResponse {
pub auth_policy: Option<PluginAuthPolicy>,
pub auth_policy: PluginAuthPolicy,
pub apps_needing_auth: Vec<AppSummary>,
}

View file

@ -157,13 +157,13 @@ Example with notification opt-out:
- `experimentalFeature/list` — list feature flags with stage metadata (`beta`, `underDevelopment`, `stable`, etc.), enabled/default-enabled state, and cursor pagination. For non-beta flags, `displayName`/`description`/`announcement` are `null`.
- `collaborationMode/list` — list available collaboration mode presets (experimental, no pagination). This response omits built-in developer instructions; clients should either pass `settings.developer_instructions: null` when setting a mode to use Codex's built-in instructions, or provide their own instructions explicitly.
- `skills/list` — list skills for one or more `cwd` values (optional `forceReload`).
- `plugin/list` — list discovered plugin marketplaces and plugin state, including marketplace install/auth policy metadata. `interface.category` uses the marketplace category when present; otherwise it falls back to the plugin manifest category. Pass `forceRemoteSync: true` to refresh curated plugin state before listing (**under development; do not call from production clients yet**).
- `plugin/list` — list discovered plugin marketplaces and plugin state, including effective marketplace install/auth policy metadata. `interface.category` uses the marketplace category when present; otherwise it falls back to the plugin manifest category. Pass `forceRemoteSync: true` to refresh curated plugin state before listing (**under development; do not call from production clients yet**).
- `skills/changed` — notification emitted when watched local skill files change.
- `skills/remote/list` — list public remote skills (**under development; do not call from production clients yet**).
- `skills/remote/export` — download a remote skill by `hazelnutId` into `skills` under `codex_home` (**under development; do not call from production clients yet**).
- `app/list` — list available apps.
- `skills/config/write` — write user-level skill config by path.
- `plugin/install` — install a plugin from a discovered marketplace entry, rejecting marketplace entries marked unavailable for install, and return the plugin auth policy plus any apps that still need auth (**under development; do not call from production clients yet**).
- `plugin/install` — install a plugin from a discovered marketplace entry, rejecting marketplace entries marked unavailable for install, and return the effective plugin auth policy plus any apps that still need auth (**under development; do not call from production clients yet**).
- `plugin/uninstall` — uninstall a plugin by id by removing its cached files and clearing its user-level config entry (**under development; do not call from production clients yet**).
- `mcpServer/oauth/login` — start an OAuth login for a configured MCP server; returns an `authorization_url` and later emits `mcpServer/oauthLogin/completed` once the browser flow finishes.
- `tool/requestUserInput` — prompt the user with 13 short questions for a tool call and return their answers (experimental).

View file

@ -5465,8 +5465,8 @@ impl CodexMessageProcessor {
PluginSource::Local { path }
}
},
install_policy: plugin.install_policy.map(Into::into),
auth_policy: plugin.auth_policy.map(Into::into),
install_policy: plugin.install_policy.into(),
auth_policy: plugin.auth_policy.into(),
interface: plugin.interface.map(|interface| PluginInterface {
display_name: interface.display_name,
short_description: interface.short_description,
@ -5719,7 +5719,7 @@ impl CodexMessageProcessor {
.send_response(
request_id,
PluginInstallResponse {
auth_policy: result.auth_policy.map(Into::into),
auth_policy: result.auth_policy.into(),
apps_needing_auth,
},
)

View file

@ -191,7 +191,7 @@ async fn plugin_install_returns_apps_needing_auth() -> Result<()> {
"sample-plugin",
"./sample-plugin",
None,
Some("ON_INSTALL"),
None,
)?;
write_plugin_source(repo_root.path(), "sample-plugin", &["alpha", "beta"])?;
let marketplace_path =
@ -217,7 +217,7 @@ async fn plugin_install_returns_apps_needing_auth() -> Result<()> {
assert_eq!(
response,
PluginInstallResponse {
auth_policy: Some(PluginAuthPolicy::OnInstall),
auth_policy: PluginAuthPolicy::OnInstall,
apps_needing_auth: vec![AppSummary {
id: "alpha".to_string(),
name: "Alpha".to_string(),
@ -299,7 +299,7 @@ async fn plugin_install_filters_disallowed_apps_needing_auth() -> Result<()> {
assert_eq!(
response,
PluginInstallResponse {
auth_policy: Some(PluginAuthPolicy::OnUse),
auth_policy: PluginAuthPolicy::OnUse,
apps_needing_auth: vec![AppSummary {
id: "alpha".to_string(),
name: "Alpha".to_string(),

View file

@ -224,10 +224,26 @@ enabled = false
assert_eq!(marketplace.plugins[0].name, "enabled-plugin");
assert_eq!(marketplace.plugins[0].installed, true);
assert_eq!(marketplace.plugins[0].enabled, true);
assert_eq!(
marketplace.plugins[0].install_policy,
PluginInstallPolicy::Available
);
assert_eq!(
marketplace.plugins[0].auth_policy,
PluginAuthPolicy::OnInstall
);
assert_eq!(marketplace.plugins[1].id, "disabled-plugin@codex-curated");
assert_eq!(marketplace.plugins[1].name, "disabled-plugin");
assert_eq!(marketplace.plugins[1].installed, true);
assert_eq!(marketplace.plugins[1].enabled, false);
assert_eq!(
marketplace.plugins[1].install_policy,
PluginInstallPolicy::Available
);
assert_eq!(
marketplace.plugins[1].auth_policy,
PluginAuthPolicy::OnInstall
);
assert_eq!(
marketplace.plugins[2].id,
"uninstalled-plugin@codex-curated"
@ -235,6 +251,14 @@ enabled = false
assert_eq!(marketplace.plugins[2].name, "uninstalled-plugin");
assert_eq!(marketplace.plugins[2].installed, false);
assert_eq!(marketplace.plugins[2].enabled, false);
assert_eq!(
marketplace.plugins[2].install_policy,
PluginInstallPolicy::Available
);
assert_eq!(
marketplace.plugins[2].auth_policy,
PluginAuthPolicy::OnInstall
);
Ok(())
}
@ -418,8 +442,8 @@ async fn plugin_list_returns_plugin_interface_with_absolute_asset_paths() -> Res
assert_eq!(plugin.id, "demo-plugin@codex-curated");
assert_eq!(plugin.installed, false);
assert_eq!(plugin.enabled, false);
assert_eq!(plugin.install_policy, Some(PluginInstallPolicy::Available));
assert_eq!(plugin.auth_policy, Some(PluginAuthPolicy::OnInstall));
assert_eq!(plugin.install_policy, PluginInstallPolicy::Available);
assert_eq!(plugin.auth_policy, PluginAuthPolicy::OnInstall);
let interface = plugin
.interface
.as_ref()

View file

@ -75,7 +75,7 @@ pub struct PluginInstallOutcome {
pub plugin_id: PluginId,
pub plugin_version: String,
pub installed_path: AbsolutePathBuf,
pub auth_policy: Option<MarketplacePluginAuthPolicy>,
pub auth_policy: MarketplacePluginAuthPolicy,
}
#[derive(Debug, Clone, PartialEq, Eq)]
@ -90,8 +90,8 @@ pub struct ConfiguredMarketplacePluginSummary {
pub id: String,
pub name: String,
pub source: MarketplacePluginSourceSummary,
pub install_policy: Option<MarketplacePluginInstallPolicy>,
pub auth_policy: Option<MarketplacePluginAuthPolicy>,
pub install_policy: MarketplacePluginInstallPolicy,
pub auth_policy: MarketplacePluginAuthPolicy,
pub interface: Option<PluginManifestInterfaceSummary>,
pub installed: bool,
pub enabled: bool,
@ -1972,7 +1972,7 @@ mod tests {
plugin_id: PluginId::new("sample-plugin".to_string(), "debug".to_string()).unwrap(),
plugin_version: "local".to_string(),
installed_path: AbsolutePathBuf::try_from(installed_path).unwrap(),
auth_policy: Some(MarketplacePluginAuthPolicy::OnUse),
auth_policy: MarketplacePluginAuthPolicy::OnUse,
}
);
@ -2102,8 +2102,8 @@ enabled = false
path: AbsolutePathBuf::try_from(tmp.path().join("repo/enabled-plugin"))
.unwrap(),
},
install_policy: None,
auth_policy: None,
install_policy: MarketplacePluginInstallPolicy::Available,
auth_policy: MarketplacePluginAuthPolicy::OnInstall,
interface: None,
installed: true,
enabled: true,
@ -2117,8 +2117,8 @@ enabled = false
)
.unwrap(),
},
install_policy: None,
auth_policy: None,
install_policy: MarketplacePluginInstallPolicy::Available,
auth_policy: MarketplacePluginAuthPolicy::OnInstall,
interface: None,
installed: true,
enabled: false,
@ -2184,8 +2184,8 @@ enabled = false
path: AbsolutePathBuf::try_from(curated_root.join("plugins/linear"))
.unwrap(),
},
install_policy: None,
auth_policy: None,
install_policy: MarketplacePluginInstallPolicy::Available,
auth_policy: MarketplacePluginAuthPolicy::OnInstall,
interface: None,
installed: false,
enabled: false,
@ -2284,8 +2284,8 @@ enabled = false
source: MarketplacePluginSourceSummary::Local {
path: AbsolutePathBuf::try_from(tmp.path().join("repo-a/from-a")).unwrap(),
},
install_policy: None,
auth_policy: None,
install_policy: MarketplacePluginInstallPolicy::Available,
auth_policy: MarketplacePluginAuthPolicy::OnInstall,
interface: None,
installed: false,
enabled: true,
@ -2310,8 +2310,8 @@ enabled = false
source: MarketplacePluginSourceSummary::Local {
path: AbsolutePathBuf::try_from(tmp.path().join("repo-b/from-b-only")).unwrap(),
},
install_policy: None,
auth_policy: None,
install_policy: MarketplacePluginInstallPolicy::Available,
auth_policy: MarketplacePluginAuthPolicy::OnInstall,
interface: None,
installed: false,
enabled: false,
@ -2389,8 +2389,8 @@ enabled = true
path: AbsolutePathBuf::try_from(tmp.path().join("repo/sample-plugin"))
.unwrap(),
},
install_policy: None,
auth_policy: None,
install_policy: MarketplacePluginInstallPolicy::Available,
auth_policy: MarketplacePluginAuthPolicy::OnInstall,
interface: None,
installed: false,
enabled: true,

View file

@ -21,7 +21,7 @@ const MARKETPLACE_RELATIVE_PATH: &str = ".agents/plugins/marketplace.json";
pub struct ResolvedMarketplacePlugin {
pub plugin_id: PluginId,
pub source_path: AbsolutePathBuf,
pub auth_policy: Option<MarketplacePluginAuthPolicy>,
pub auth_policy: MarketplacePluginAuthPolicy,
}
#[derive(Debug, Clone, PartialEq, Eq)]
@ -35,8 +35,8 @@ pub struct MarketplaceSummary {
pub struct MarketplacePluginSummary {
pub name: String,
pub source: MarketplacePluginSourceSummary,
pub install_policy: Option<MarketplacePluginInstallPolicy>,
pub auth_policy: Option<MarketplacePluginAuthPolicy>,
pub install_policy: MarketplacePluginInstallPolicy,
pub auth_policy: MarketplacePluginAuthPolicy,
pub interface: Option<PluginManifestInterfaceSummary>,
}
@ -45,18 +45,20 @@ pub enum MarketplacePluginSourceSummary {
Local { path: AbsolutePathBuf },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Deserialize)]
pub enum MarketplacePluginInstallPolicy {
#[serde(rename = "NOT_AVAILABLE")]
NotAvailable,
#[default]
#[serde(rename = "AVAILABLE")]
Available,
#[serde(rename = "INSTALLED_BY_DEFAULT")]
InstalledByDefault,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Deserialize)]
pub enum MarketplacePluginAuthPolicy {
#[default]
#[serde(rename = "ON_INSTALL")]
OnInstall,
#[serde(rename = "ON_USE")]
@ -148,7 +150,7 @@ pub fn resolve_marketplace_plugin(
auth_policy,
..
} = plugin;
if install_policy == Some(MarketplacePluginInstallPolicy::NotAvailable) {
if install_policy == MarketplacePluginInstallPolicy::NotAvailable {
return Err(MarketplaceError::PluginNotAvailable {
plugin_name: name,
marketplace_name,
@ -365,9 +367,9 @@ struct MarketplacePlugin {
name: String,
source: MarketplacePluginSource,
#[serde(default)]
install_policy: Option<MarketplacePluginInstallPolicy>,
install_policy: MarketplacePluginInstallPolicy,
#[serde(default)]
auth_policy: Option<MarketplacePluginAuthPolicy>,
auth_policy: MarketplacePluginAuthPolicy,
#[serde(default)]
category: Option<String>,
}
@ -420,7 +422,7 @@ mod tests {
plugin_id: PluginId::new("local-plugin".to_string(), "codex-curated".to_string())
.unwrap(),
source_path: AbsolutePathBuf::try_from(repo_root.join("plugin-1")).unwrap(),
auth_policy: None,
auth_policy: MarketplacePluginAuthPolicy::OnInstall,
}
);
}
@ -527,8 +529,8 @@ mod tests {
path: AbsolutePathBuf::try_from(home_root.join("home-shared"))
.unwrap(),
},
install_policy: None,
auth_policy: None,
install_policy: MarketplacePluginInstallPolicy::Available,
auth_policy: MarketplacePluginAuthPolicy::OnInstall,
interface: None,
},
MarketplacePluginSummary {
@ -537,8 +539,8 @@ mod tests {
path: AbsolutePathBuf::try_from(home_root.join("home-only"))
.unwrap(),
},
install_policy: None,
auth_policy: None,
install_policy: MarketplacePluginInstallPolicy::Available,
auth_policy: MarketplacePluginAuthPolicy::OnInstall,
interface: None,
},
],
@ -556,8 +558,8 @@ mod tests {
path: AbsolutePathBuf::try_from(repo_root.join("repo-shared"))
.unwrap(),
},
install_policy: None,
auth_policy: None,
install_policy: MarketplacePluginInstallPolicy::Available,
auth_policy: MarketplacePluginAuthPolicy::OnInstall,
interface: None,
},
MarketplacePluginSummary {
@ -566,8 +568,8 @@ mod tests {
path: AbsolutePathBuf::try_from(repo_root.join("repo-only"))
.unwrap(),
},
install_policy: None,
auth_policy: None,
install_policy: MarketplacePluginInstallPolicy::Available,
auth_policy: MarketplacePluginAuthPolicy::OnInstall,
interface: None,
},
],
@ -638,8 +640,8 @@ mod tests {
source: MarketplacePluginSourceSummary::Local {
path: AbsolutePathBuf::try_from(home_root.join("home-plugin")).unwrap(),
},
install_policy: None,
auth_policy: None,
install_policy: MarketplacePluginInstallPolicy::Available,
auth_policy: MarketplacePluginAuthPolicy::OnInstall,
interface: None,
}],
},
@ -651,8 +653,8 @@ mod tests {
source: MarketplacePluginSourceSummary::Local {
path: AbsolutePathBuf::try_from(repo_root.join("repo-plugin")).unwrap(),
},
install_policy: None,
auth_policy: None,
install_policy: MarketplacePluginInstallPolicy::Available,
auth_policy: MarketplacePluginAuthPolicy::OnInstall,
interface: None,
}],
},
@ -717,8 +719,8 @@ mod tests {
source: MarketplacePluginSourceSummary::Local {
path: AbsolutePathBuf::try_from(repo_root.join("plugin")).unwrap(),
},
install_policy: None,
auth_policy: None,
install_policy: MarketplacePluginInstallPolicy::Available,
auth_policy: MarketplacePluginAuthPolicy::OnInstall,
interface: None,
}],
}]
@ -774,11 +776,11 @@ mod tests {
assert_eq!(
marketplaces[0].plugins[0].install_policy,
Some(MarketplacePluginInstallPolicy::Available)
MarketplacePluginInstallPolicy::Available
);
assert_eq!(
marketplaces[0].plugins[0].auth_policy,
Some(MarketplacePluginAuthPolicy::OnInstall)
MarketplacePluginAuthPolicy::OnInstall
);
assert_eq!(
marketplaces[0].plugins[0].interface,
@ -868,8 +870,14 @@ mod tests {
screenshots: Vec::new(),
})
);
assert_eq!(marketplaces[0].plugins[0].install_policy, None);
assert_eq!(marketplaces[0].plugins[0].auth_policy, None);
assert_eq!(
marketplaces[0].plugins[0].install_policy,
MarketplacePluginInstallPolicy::Available
);
assert_eq!(
marketplaces[0].plugins[0].auth_policy,
MarketplacePluginAuthPolicy::OnInstall
);
}
#[test]