From 28f3f1f9ef4e9578a5f023f6b6eba018914a5342 Mon Sep 17 00:00:00 2001 From: Eric Ning Date: Wed, 29 Jul 2026 01:11:19 +0000 Subject: [PATCH] Expose plugin installation timestamps in app-server summaries (#35859) ## What changed - Add nullable `installedAt` metadata to `PluginSummary` and its generated schemas, expressed as a Unix timestamp in seconds. - Preserve the backend installation time for remote plugins across plugin list, installed, read, and share-list responses. Return `null` when the timestamp is unavailable, including for local or uninstalled plugins and older backend responses. ## Testing - Cover timestamp conversion in the remote plugin list integration test and the field's serialization and backward-compatible default in protocol tests. GitOrigin-RevId: 8a1640320adb298874e1074e9891d1017ad06937 --- .../schema/json/codex_app_server_protocol.schemas.json | 9 +++++++++ .../json/codex_app_server_protocol.v2.schemas.json | 9 +++++++++ .../schema/json/v2/PluginInstalledResponse.json | 9 +++++++++ .../schema/json/v2/PluginListResponse.json | 9 +++++++++ .../schema/json/v2/PluginReadResponse.json | 9 +++++++++ .../schema/json/v2/PluginShareListResponse.json | 9 +++++++++ .../schema/typescript/v2/PluginSummary.ts | 6 +++++- codex-rs/app-server-protocol/src/protocol/v2/plugin.rs | 4 ++++ codex-rs/app-server-protocol/src/protocol/v2/tests.rs | 4 ++++ codex-rs/app-server/README.md | 2 +- codex-rs/app-server/src/request_processors/plugins.rs | 5 +++++ codex-rs/app-server/tests/suite/v2/plugin_list.rs | 8 ++++++++ codex-rs/app-server/tests/suite/v2/plugin_share.rs | 3 +++ codex-rs/core-plugins/src/manager_tests.rs | 1 + codex-rs/core-plugins/src/remote.rs | 9 +++++++++ codex-rs/core-plugins/src/remote/share/tests.rs | 2 ++ codex-rs/core-plugins/src/remote_tests.rs | 4 ++++ codex-rs/tui/src/app/plugin_mentions.rs | 1 + codex-rs/tui/src/chatwidget/tests/helpers.rs | 2 ++ 19 files changed, 103 insertions(+), 2 deletions(-) diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json index ce5216b4d0..723b469575 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json @@ -15143,6 +15143,15 @@ "installed": { "type": "boolean" }, + "installedAt": { + "default": null, + "description": "Unix timestamp in seconds when the remote plugin was installed, when available.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "interface": { "anyOf": [ { diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json index 7a09995f87..0e281dc566 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json @@ -11485,6 +11485,15 @@ "installed": { "type": "boolean" }, + "installedAt": { + "default": null, + "description": "Unix timestamp in seconds when the remote plugin was installed, when available.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "interface": { "anyOf": [ { diff --git a/codex-rs/app-server-protocol/schema/json/v2/PluginInstalledResponse.json b/codex-rs/app-server-protocol/schema/json/v2/PluginInstalledResponse.json index 345995f382..e258544028 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/PluginInstalledResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/PluginInstalledResponse.json @@ -548,6 +548,15 @@ "installed": { "type": "boolean" }, + "installedAt": { + "default": null, + "description": "Unix timestamp in seconds when the remote plugin was installed, when available.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "interface": { "anyOf": [ { diff --git a/codex-rs/app-server-protocol/schema/json/v2/PluginListResponse.json b/codex-rs/app-server-protocol/schema/json/v2/PluginListResponse.json index 17e7c8aa45..38207ac285 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/PluginListResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/PluginListResponse.json @@ -548,6 +548,15 @@ "installed": { "type": "boolean" }, + "installedAt": { + "default": null, + "description": "Unix timestamp in seconds when the remote plugin was installed, when available.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "interface": { "anyOf": [ { diff --git a/codex-rs/app-server-protocol/schema/json/v2/PluginReadResponse.json b/codex-rs/app-server-protocol/schema/json/v2/PluginReadResponse.json index 83e2a1ec2b..572d6def09 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/PluginReadResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/PluginReadResponse.json @@ -698,6 +698,15 @@ "installed": { "type": "boolean" }, + "installedAt": { + "default": null, + "description": "Unix timestamp in seconds when the remote plugin was installed, when available.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "interface": { "anyOf": [ { diff --git a/codex-rs/app-server-protocol/schema/json/v2/PluginShareListResponse.json b/codex-rs/app-server-protocol/schema/json/v2/PluginShareListResponse.json index 621d02e9bd..bcae24d57c 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/PluginShareListResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/PluginShareListResponse.json @@ -504,6 +504,15 @@ "installed": { "type": "boolean" }, + "installedAt": { + "default": null, + "description": "Unix timestamp in seconds when the remote plugin was installed, when available.", + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "interface": { "anyOf": [ { diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/PluginSummary.ts b/codex-rs/app-server-protocol/schema/typescript/v2/PluginSummary.ts index db261af26f..6dedc1bad9 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/PluginSummary.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/PluginSummary.ts @@ -26,7 +26,11 @@ localVersion: string | null, name: string, /** * Remote sharing context associated with this plugin when available. */ -shareContext: PluginShareContext | null, source: PluginSource, installed: boolean, enabled: boolean, installPolicy: PluginInstallPolicy, installPolicySource: PluginInstallPolicySource | null, mustShowInstallationInterstitial: boolean | null, authPolicy: PluginAuthPolicy, +shareContext: PluginShareContext | null, source: PluginSource, installed: boolean, +/** + * Unix timestamp in seconds when the remote plugin was installed, when available. + */ +installedAt: number | null, enabled: boolean, installPolicy: PluginInstallPolicy, installPolicySource: PluginInstallPolicySource | null, mustShowInstallationInterstitial: boolean | null, authPolicy: PluginAuthPolicy, /** * Availability state for installing and using the plugin. */ diff --git a/codex-rs/app-server-protocol/src/protocol/v2/plugin.rs b/codex-rs/app-server-protocol/src/protocol/v2/plugin.rs index 126e50ed59..458a0247f5 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2/plugin.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2/plugin.rs @@ -648,6 +648,10 @@ pub struct PluginSummary { pub share_context: Option, pub source: PluginSource, pub installed: bool, + /// Unix timestamp in seconds when the remote plugin was installed, when available. + #[serde(default)] + #[ts(type = "number | null")] + pub installed_at: Option, pub enabled: bool, pub install_policy: PluginInstallPolicy, pub install_policy_source: Option, diff --git a/codex-rs/app-server-protocol/src/protocol/v2/tests.rs b/codex-rs/app-server-protocol/src/protocol/v2/tests.rs index 0bd6e3242a..501d5ef752 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2/tests.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2/tests.rs @@ -4009,6 +4009,7 @@ fn plugin_share_list_response_serializes_share_items() { share_context: None, source: PluginSource::Remote, installed: false, + installed_at: None, enabled: false, install_policy: PluginInstallPolicy::Available, install_policy_source: Some(PluginInstallPolicySource::WorkspaceSetting), @@ -4035,6 +4036,7 @@ fn plugin_share_list_response_serializes_share_items() { "shareContext": null, "source": { "type": "remote" }, "installed": false, + "installedAt": null, "enabled": false, "installPolicy": "AVAILABLE", "installPolicySource": "WORKSPACE_SETTING", @@ -4067,6 +4069,7 @@ fn plugin_summary_defaults_missing_availability_to_available() { .unwrap(); assert_eq!(summary.availability, PluginAvailability::Available); + assert_eq!(summary.installed_at, None); assert_eq!(summary.local_version, None); assert_eq!(summary.share_context, None); assert_eq!(summary.must_show_installation_interstitial, None); @@ -4085,6 +4088,7 @@ fn plugin_summary_round_trips_plan_eligibility_metadata() { "shareContext": null, "source": { "type": "remote" }, "installed": false, + "installedAt": null, "enabled": false, "installPolicy": "NOT_AVAILABLE", "installPolicySource": null, diff --git a/codex-rs/app-server/README.md b/codex-rs/app-server/README.md index 9a0b22ca2c..0d97360376 100644 --- a/codex-rs/app-server/README.md +++ b/codex-rs/app-server/README.md @@ -220,7 +220,7 @@ Example with notification opt-out: - `marketplace/remove` — remove a configured marketplace by name from the user marketplace config, and delete its installed marketplace root when one exists. - `marketplace/upgrade` — upgrade all configured Git plugin marketplaces, or one named marketplace when `marketplaceName` is provided. Returns selected marketplace names, upgraded roots, and per-marketplace errors. - `plugin/list` — list discovered plugin marketplaces and plugin state, including effective marketplace install/auth policy metadata, nullable remote install-policy provenance in `installPolicySource` (`WORKSPACE_SETTING` or `IMPLICIT_CANONICAL_APP`), the remote marketplace `version` and locally materialized `localVersion` when available, plugin `availability` (`AVAILABLE` by default or `DISABLED_BY_ADMIN` for remote plugins blocked upstream), fail-open `marketplaceLoadErrors` entries for marketplace files that could not be parsed or loaded, and best-effort `featuredPluginIds` for the official curated marketplace. Every `PluginSummary` returned by plugin list, installed, read, and share-list methods includes nullable `disabledReason` and `eligiblePlanTypes`, preserving plugin-service availability metadata and raw plan identifiers for remote plugins while returning `null` for local plugins or older remote responses. The same summaries include `mustShowInstallationInterstitial`: remote service values preserve `true` or `false`, while local plugins and remote responses that omit the policy return `null`. Clients should fail closed when the value is `null`. Clients can explicitly request the remote `workspace-directory`, `shared-with-me`, or `created-by-me-remote` marketplace kinds. Set `forceRefetch: true` to bypass TTL-backed remote catalog caches for the requested marketplaces and wait for fresh data; cache entries are replaced only after a successful fetch. When local marketplaces are included, the request also waits for configured plugin caches to reconcile before marketplace summaries are returned. At app-server startup, existing cached catalogs remain available to `plugin/list` while they refresh in the background. `interface.category` uses the marketplace category when present; otherwise it falls back to the plugin manifest category (**under development; do not call from production clients yet**). -- `plugin/installed` — list installed plugin rows plus any explicitly requested local install-suggestion plugin names, without fetching the broader remote catalog. Remote rows include nullable `installPolicySource`; local rows return `null`. Mention surfaces can use this narrower view when they need plugin mention payloads rather than plugin-page discovery data (**under development; do not call from production clients yet**). +- `plugin/installed` — list installed plugin rows plus any explicitly requested local install-suggestion plugin names, without fetching the broader remote catalog. Remote rows include nullable `installPolicySource` and `installedAt`, the backend installation timestamp in Unix seconds. `installedAt` is also returned by `plugin/list`, `plugin/read`, and `plugin/share/list`; it is `null` for local plugins, uninstalled plugins, plugins installed by default, and older backend responses that do not include an installation timestamp. Mention surfaces can use this narrower view when they need plugin mention payloads rather than plugin-page discovery data (**under development; do not call from production clients yet**). - `plugin/read` — read one plugin by `marketplacePath` plus `pluginName`, returning marketplace info, a list-style `summary`, manifest descriptions/interface metadata, and bundled skills/hooks/apps/MCP server names. Remote plugin details can include scheduled task summaries from the catalog; `scheduledTasks: null` means the metadata is unavailable, while an empty array means the catalog found no scheduled tasks. Remote plugin details expose the canonical `shareUrl` supplied by the remote catalog when available; it is `null` for local plugins or when the catalog omits it. This field is separate from `summary.shareContext`, which continues to describe user and workspace sharing state. For owned workspace plugins, `summary.shareContext.canPublishToWorkspace` reports whether the current user may add the plugin to the workspace directory; `plugin/share/save` returns the same capability after creating or updating a share, and clients should fail closed when either value is `null`. Remote skill interfaces expose `iconSmallUrl` and `iconLargeUrl` when the catalog supplies icon URLs. Returned plugin skills include their current `enabled` state after local config filtering; bundled hooks are returned as lightweight declaration summaries keyed for correlation with `hooks/list`. Use `plugin/install`'s `appsNeedingAuth` to drive post-install authentication and `app/list`'s `isAccessible` to determine current connector accessibility (**under development; do not call from production clients yet**). - `plugin/skill/read` — read remote plugin skill markdown on demand by `remoteMarketplaceName`, `remotePluginId`, and `skillName`. This lets clients preview uninstalled remote plugin skills without downloading the plugin bundle. - `skills/changed` — notification emitted when watched local skill files change. diff --git a/codex-rs/app-server/src/request_processors/plugins.rs b/codex-rs/app-server/src/request_processors/plugins.rs index 4e2cf9de28..7324e2aa2f 100644 --- a/codex-rs/app-server/src/request_processors/plugins.rs +++ b/codex-rs/app-server/src/request_processors/plugins.rs @@ -177,6 +177,7 @@ fn convert_configured_marketplace_plugin_to_plugin_summary( version: None, local_version: plugin.local_version, installed: plugin.installed, + installed_at: None, enabled: plugin.enabled, name: plugin.name, share_context, @@ -1128,6 +1129,7 @@ impl PluginRequestProcessor { share_context, source: marketplace_plugin_source_to_info(outcome.plugin.source), installed: outcome.plugin.installed, + installed_at: None, enabled: outcome.plugin.enabled, install_policy: outcome.plugin.policy.installation.into(), install_policy_source: None, @@ -2240,6 +2242,9 @@ fn remote_plugin_summary_to_info(summary: RemoteCatalogPluginSummary) -> PluginS .map(remote_plugin_share_context_to_info), source: PluginSource::Remote, installed: summary.installed, + installed_at: summary + .installed_at + .map(|installed_at| installed_at.timestamp()), enabled: summary.enabled, install_policy: summary.install_policy, install_policy_source: summary.install_policy_source, diff --git a/codex-rs/app-server/tests/suite/v2/plugin_list.rs b/codex-rs/app-server/tests/suite/v2/plugin_list.rs index 7b9aaf73a1..0139c68272 100644 --- a/codex-rs/app-server/tests/suite/v2/plugin_list.rs +++ b/codex-rs/app-server/tests/suite/v2/plugin_list.rs @@ -586,6 +586,7 @@ async fn plugin_list_keeps_valid_marketplaces_when_another_marketplace_fails_to_ path: valid_plugin_path, }, installed: false, + installed_at: None, enabled: false, install_policy: PluginInstallPolicy::Available, install_policy_source: None, @@ -875,6 +876,7 @@ async fn plugin_list_uses_alternate_discoverable_manifest_and_keeps_undiscoverab path: valid_plugin_path, }, installed: false, + installed_at: None, enabled: false, install_policy: PluginInstallPolicy::Available, install_policy_source: None, @@ -919,6 +921,7 @@ async fn plugin_list_uses_alternate_discoverable_manifest_and_keeps_undiscoverab )?, }, installed: false, + installed_at: None, enabled: false, install_policy: PluginInstallPolicy::Available, install_policy_source: None, @@ -1968,6 +1971,7 @@ async fn plugin_list_includes_remote_marketplaces_when_remote_plugin_enabled() - "scope": "GLOBAL", "installation_policy": "AVAILABLE", "installation_policy_source": "WORKSPACE_SETTING", + "installed_at": "2026-01-02T00:00:00Z", "must_show_installation_interstitial": false, "authentication_policy": "ON_USE", "status": "ENABLED", @@ -2091,6 +2095,10 @@ async fn plugin_list_includes_remote_marketplaces_when_remote_plugin_enabled() - Some("1.2.3") ); assert_eq!(remote_marketplace.plugins[0].installed, true); + assert_eq!( + remote_marketplace.plugins[0].installed_at, + Some(1_767_312_000) + ); assert_eq!(remote_marketplace.plugins[0].enabled, true); assert_eq!( remote_marketplace.plugins[0].install_policy_source, diff --git a/codex-rs/app-server/tests/suite/v2/plugin_share.rs b/codex-rs/app-server/tests/suite/v2/plugin_share.rs index bf58e4e851..d64c52ca23 100644 --- a/codex-rs/app-server/tests/suite/v2/plugin_share.rs +++ b/codex-rs/app-server/tests/suite/v2/plugin_share.rs @@ -172,6 +172,7 @@ async fn plugin_share_save_uploads_local_plugin() -> Result<()> { share_context: Some(expected_share_context("plugins_123")), source: PluginSource::Remote, installed: true, + installed_at: None, enabled: true, install_policy: PluginInstallPolicy::Available, install_policy_source: None, @@ -591,6 +592,7 @@ async fn plugin_share_list_returns_created_workspace_plugins() -> Result<()> { share_context: Some(expected_share_context("plugins_123")), source: PluginSource::Remote, installed: true, + installed_at: None, enabled: true, install_policy: PluginInstallPolicy::Available, install_policy_source: None, @@ -1253,6 +1255,7 @@ async fn plugin_share_delete_removes_created_workspace_plugin() -> Result<()> { share_context: Some(expected_share_context("plugins_123")), source: PluginSource::Remote, installed: true, + installed_at: None, enabled: true, install_policy: PluginInstallPolicy::Available, install_policy_source: None, diff --git a/codex-rs/core-plugins/src/manager_tests.rs b/codex-rs/core-plugins/src/manager_tests.rs index 1e521264ab..82fa89b83b 100644 --- a/codex-rs/core-plugins/src/manager_tests.rs +++ b/codex-rs/core-plugins/src/manager_tests.rs @@ -830,6 +830,7 @@ fn remote_installed_plugin_in_marketplace( id: format!("plugins~Plugin_{name}"), version: None, name: name.to_string(), + installed_at: None, enabled: true, install_policy: codex_app_server_protocol::PluginInstallPolicy::Available, install_policy_source: None, diff --git a/codex-rs/core-plugins/src/remote.rs b/codex-rs/core-plugins/src/remote.rs index bf7011f850..fc620a6515 100644 --- a/codex-rs/core-plugins/src/remote.rs +++ b/codex-rs/core-plugins/src/remote.rs @@ -3,6 +3,8 @@ use crate::http_client_selector::HttpClientSelector; use crate::loader::plugin_app_declarations_from_value; use crate::store::PLUGINS_CACHE_DIR; use crate::store::PluginStore; +use chrono::DateTime; +use chrono::Utc; use codex_app_server_protocol::JSONRPCErrorError; use codex_app_server_protocol::PluginAuthPolicy; use codex_app_server_protocol::PluginAvailability; @@ -208,6 +210,7 @@ pub struct RemoteInstalledPlugin { pub id: String, pub version: Option, pub name: String, + pub installed_at: Option>, pub enabled: bool, pub install_policy: PluginInstallPolicy, pub install_policy_source: Option, @@ -229,6 +232,7 @@ pub struct RemotePluginSummary { pub name: String, pub share_context: Option, pub installed: bool, + pub installed_at: Option>, pub enabled: bool, pub install_policy: PluginInstallPolicy, pub install_policy_source: Option, @@ -689,6 +693,8 @@ struct RemotePluginDirectorySharePrincipal { struct RemotePluginInstalledItem { #[serde(flatten)] plugin: RemotePluginDirectoryItem, + #[serde(default)] + installed_at: Option>, enabled: bool, #[serde(default)] disabled_skill_names: Vec, @@ -1204,6 +1210,7 @@ pub fn group_remote_installed_plugins_by_marketplaces( name: plugin.name.clone(), share_context: None, installed: true, + installed_at: plugin.installed_at, enabled: plugin.enabled, install_policy: plugin.install_policy, install_policy_source: plugin.install_policy_source, @@ -1638,6 +1645,7 @@ fn build_remote_plugin_summary( name: plugin.name.clone(), share_context: remote_plugin_share_context(plugin)?, installed: installed_plugin.is_some(), + installed_at: installed_plugin.and_then(|installed| installed.installed_at), enabled: installed_plugin.is_some_and(|plugin| plugin.enabled), install_policy: plugin.installation_policy, install_policy_source: plugin @@ -1724,6 +1732,7 @@ fn remote_installed_plugin_to_cache_entry( id: plugin.id.clone(), version: plugin.release.version.clone(), name: plugin.name.clone(), + installed_at: installed_plugin.installed_at, enabled: installed_plugin.enabled, install_policy: plugin.installation_policy, install_policy_source: plugin diff --git a/codex-rs/core-plugins/src/remote/share/tests.rs b/codex-rs/core-plugins/src/remote/share/tests.rs index 1bcc48fb76..13d4bdbfcf 100644 --- a/codex-rs/core-plugins/src/remote/share/tests.rs +++ b/codex-rs/core-plugins/src/remote/share/tests.rs @@ -665,6 +665,7 @@ async fn list_remote_plugin_shares_fetches_created_workspace_plugins() { can_publish_to_workspace: None, }), installed: false, + installed_at: None, enabled: false, install_policy: PluginInstallPolicy::Available, install_policy_source: None, @@ -709,6 +710,7 @@ async fn list_remote_plugin_shares_fetches_created_workspace_plugins() { can_publish_to_workspace: None, }), installed: true, + installed_at: None, enabled: true, install_policy: PluginInstallPolicy::Available, install_policy_source: None, diff --git a/codex-rs/core-plugins/src/remote_tests.rs b/codex-rs/core-plugins/src/remote_tests.rs index 9ed28de7fa..4af981b6eb 100644 --- a/codex-rs/core-plugins/src/remote_tests.rs +++ b/codex-rs/core-plugins/src/remote_tests.rs @@ -78,6 +78,7 @@ fn build_remote_marketplace_preserves_directory_order_and_appends_installed_only ]; let installed_plugins = vec![RemotePluginInstalledItem { plugin: directory_plugin("plugin-a", "alpha"), + installed_at: None, enabled: true, disabled_skill_names: Vec::new(), }]; @@ -109,6 +110,7 @@ fn installation_policy_source_is_preserved_across_remote_summary_paths() { Some(RemotePluginInstallPolicySource::ImplicitCanonicalApp); let installed_plugin = RemotePluginInstalledItem { plugin: directory_plugin.clone(), + installed_at: None, enabled: true, disabled_skill_names: Vec::new(), }; @@ -163,6 +165,7 @@ fn plan_eligibility_is_preserved_across_remote_summary_paths() { ]); let installed_plugin = RemotePluginInstalledItem { plugin: directory_plugin.clone(), + installed_at: None, enabled: false, disabled_skill_names: Vec::new(), }; @@ -255,6 +258,7 @@ fn installation_interstitial_requirement_is_preserved_across_remote_summary_path directory_plugin.must_show_installation_interstitial = Some(false); let installed_plugin = remote_installed_plugin_to_cache_entry(&RemotePluginInstalledItem { plugin: directory_plugin, + installed_at: None, enabled: true, disabled_skill_names: Vec::new(), }) diff --git a/codex-rs/tui/src/app/plugin_mentions.rs b/codex-rs/tui/src/app/plugin_mentions.rs index 597c5b543c..dd162af0d6 100644 --- a/codex-rs/tui/src/app/plugin_mentions.rs +++ b/codex-rs/tui/src/app/plugin_mentions.rs @@ -172,6 +172,7 @@ mod tests { share_context: None, source: PluginSource::Remote, installed: true, + installed_at: None, enabled: true, install_policy: PluginInstallPolicy::Available, install_policy_source: None, diff --git a/codex-rs/tui/src/chatwidget/tests/helpers.rs b/codex-rs/tui/src/chatwidget/tests/helpers.rs index 3c71c0bcb8..5bba3a8345 100644 --- a/codex-rs/tui/src/chatwidget/tests/helpers.rs +++ b/codex-rs/tui/src/chatwidget/tests/helpers.rs @@ -1354,6 +1354,7 @@ pub(super) fn plugins_test_summary( path: plugins_test_absolute_path(&format!("plugins/{name}")), }, installed, + installed_at: None, enabled, install_policy, install_policy_source: None, @@ -1387,6 +1388,7 @@ pub(super) fn plugins_test_remote_summary( share_context: None, source: PluginSource::Remote, installed, + installed_at: None, enabled: true, install_policy: PluginInstallPolicy::Available, install_policy_source: None,