From 835cff9fdaa51e9bc14eac965627e839cd426f8c Mon Sep 17 00:00:00 2001 From: Abhinav Vedmala Date: Tue, 7 Jul 2026 21:29:09 -0400 Subject: [PATCH] [codex] expose scheduled task summaries in plugin/read --- .../codex_app_server_protocol.schemas.json | 146 ++++++++++++++++++ .../codex_app_server_protocol.v2.schemas.json | 146 ++++++++++++++++++ .../schema/json/v2/PluginReadResponse.json | 146 ++++++++++++++++++ .../schema/typescript/v2/PluginDetail.ts | 3 +- .../typescript/v2/ScheduledTaskSchedule.ts | 6 + .../typescript/v2/ScheduledTaskSummary.ts | 6 + .../typescript/v2/ScheduledTaskWeekday.ts | 5 + .../schema/typescript/v2/index.ts | 3 + .../src/protocol/v2/plugin.rs | 49 ++++++ codex-rs/app-server/README.md | 2 +- .../src/request_processors/plugins.rs | 2 + .../app-server/tests/suite/v2/plugin_read.rs | 51 ++++++ codex-rs/core-plugins/src/remote.rs | 4 + codex-rs/core-plugins/src/remote_tests.rs | 19 +++ codex-rs/tui/src/chatwidget/tests/helpers.rs | 2 + .../chatwidget/tests/popups_and_settings.rs | 1 + 16 files changed, 589 insertions(+), 2 deletions(-) create mode 100644 codex-rs/app-server-protocol/schema/typescript/v2/ScheduledTaskSchedule.ts create mode 100644 codex-rs/app-server-protocol/schema/typescript/v2/ScheduledTaskSummary.ts create mode 100644 codex-rs/app-server-protocol/schema/typescript/v2/ScheduledTaskWeekday.ts 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 b7526b080da1..5a4128ac6351 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 @@ -13373,6 +13373,15 @@ }, "type": "array" }, + "scheduledTasks": { + "items": { + "$ref": "#/definitions/v2/ScheduledTaskSummary" + }, + "type": [ + "array", + "null" + ] + }, "shareUrl": { "type": [ "string", @@ -16366,6 +16375,143 @@ }, "type": "object" }, + "ScheduledTaskSchedule": { + "oneOf": [ + { + "properties": { + "days": { + "items": { + "$ref": "#/definitions/v2/ScheduledTaskWeekday" + }, + "type": [ + "array", + "null" + ] + }, + "intervalHours": { + "format": "uint32", + "minimum": 0.0, + "type": "integer" + }, + "type": { + "enum": [ + "hourly" + ], + "title": "HourlyScheduledTaskScheduleType", + "type": "string" + } + }, + "required": [ + "intervalHours", + "type" + ], + "title": "HourlyScheduledTaskSchedule", + "type": "object" + }, + { + "properties": { + "time": { + "type": "string" + }, + "type": { + "enum": [ + "daily" + ], + "title": "DailyScheduledTaskScheduleType", + "type": "string" + } + }, + "required": [ + "time", + "type" + ], + "title": "DailyScheduledTaskSchedule", + "type": "object" + }, + { + "properties": { + "time": { + "type": "string" + }, + "type": { + "enum": [ + "weekdays" + ], + "title": "WeekdaysScheduledTaskScheduleType", + "type": "string" + } + }, + "required": [ + "time", + "type" + ], + "title": "WeekdaysScheduledTaskSchedule", + "type": "object" + }, + { + "properties": { + "days": { + "items": { + "$ref": "#/definitions/v2/ScheduledTaskWeekday" + }, + "type": "array" + }, + "time": { + "type": "string" + }, + "type": { + "enum": [ + "weekly" + ], + "title": "WeeklyScheduledTaskScheduleType", + "type": "string" + } + }, + "required": [ + "days", + "time", + "type" + ], + "title": "WeeklyScheduledTaskSchedule", + "type": "object" + } + ] + }, + "ScheduledTaskSummary": { + "properties": { + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "prompt": { + "type": "string" + }, + "schedule": { + "$ref": "#/definitions/v2/ScheduledTaskSchedule" + } + }, + "required": [ + "key", + "name", + "prompt", + "schedule" + ], + "type": "object" + }, + "ScheduledTaskWeekday": { + "enum": [ + "MO", + "TU", + "WE", + "TH", + "FR", + "SA", + "SU" + ], + "type": "string" + }, "SelectedCapabilityRoot": { "description": "A user-selected root that can expose one or more runtime capabilities.", "properties": { 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 e1afc7883a51..111d2ba5fe14 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 @@ -9777,6 +9777,15 @@ }, "type": "array" }, + "scheduledTasks": { + "items": { + "$ref": "#/definitions/ScheduledTaskSummary" + }, + "type": [ + "array", + "null" + ] + }, "shareUrl": { "type": [ "string", @@ -12770,6 +12779,143 @@ }, "type": "object" }, + "ScheduledTaskSchedule": { + "oneOf": [ + { + "properties": { + "days": { + "items": { + "$ref": "#/definitions/ScheduledTaskWeekday" + }, + "type": [ + "array", + "null" + ] + }, + "intervalHours": { + "format": "uint32", + "minimum": 0.0, + "type": "integer" + }, + "type": { + "enum": [ + "hourly" + ], + "title": "HourlyScheduledTaskScheduleType", + "type": "string" + } + }, + "required": [ + "intervalHours", + "type" + ], + "title": "HourlyScheduledTaskSchedule", + "type": "object" + }, + { + "properties": { + "time": { + "type": "string" + }, + "type": { + "enum": [ + "daily" + ], + "title": "DailyScheduledTaskScheduleType", + "type": "string" + } + }, + "required": [ + "time", + "type" + ], + "title": "DailyScheduledTaskSchedule", + "type": "object" + }, + { + "properties": { + "time": { + "type": "string" + }, + "type": { + "enum": [ + "weekdays" + ], + "title": "WeekdaysScheduledTaskScheduleType", + "type": "string" + } + }, + "required": [ + "time", + "type" + ], + "title": "WeekdaysScheduledTaskSchedule", + "type": "object" + }, + { + "properties": { + "days": { + "items": { + "$ref": "#/definitions/ScheduledTaskWeekday" + }, + "type": "array" + }, + "time": { + "type": "string" + }, + "type": { + "enum": [ + "weekly" + ], + "title": "WeeklyScheduledTaskScheduleType", + "type": "string" + } + }, + "required": [ + "days", + "time", + "type" + ], + "title": "WeeklyScheduledTaskSchedule", + "type": "object" + } + ] + }, + "ScheduledTaskSummary": { + "properties": { + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "prompt": { + "type": "string" + }, + "schedule": { + "$ref": "#/definitions/ScheduledTaskSchedule" + } + }, + "required": [ + "key", + "name", + "prompt", + "schedule" + ], + "type": "object" + }, + "ScheduledTaskWeekday": { + "enum": [ + "MO", + "TU", + "WE", + "TH", + "FR", + "SA", + "SU" + ], + "type": "string" + }, "SelectedCapabilityRoot": { "description": "A user-selected root that can expose one or more runtime capabilities.", "properties": { 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 628cb131898d..2b6bdc29add3 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/PluginReadResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/PluginReadResponse.json @@ -192,6 +192,15 @@ }, "type": "array" }, + "scheduledTasks": { + "items": { + "$ref": "#/definitions/ScheduledTaskSummary" + }, + "type": [ + "array", + "null" + ] + }, "shareUrl": { "type": [ "string", @@ -718,6 +727,143 @@ ], "type": "object" }, + "ScheduledTaskSchedule": { + "oneOf": [ + { + "properties": { + "days": { + "items": { + "$ref": "#/definitions/ScheduledTaskWeekday" + }, + "type": [ + "array", + "null" + ] + }, + "intervalHours": { + "format": "uint32", + "minimum": 0.0, + "type": "integer" + }, + "type": { + "enum": [ + "hourly" + ], + "title": "HourlyScheduledTaskScheduleType", + "type": "string" + } + }, + "required": [ + "intervalHours", + "type" + ], + "title": "HourlyScheduledTaskSchedule", + "type": "object" + }, + { + "properties": { + "time": { + "type": "string" + }, + "type": { + "enum": [ + "daily" + ], + "title": "DailyScheduledTaskScheduleType", + "type": "string" + } + }, + "required": [ + "time", + "type" + ], + "title": "DailyScheduledTaskSchedule", + "type": "object" + }, + { + "properties": { + "time": { + "type": "string" + }, + "type": { + "enum": [ + "weekdays" + ], + "title": "WeekdaysScheduledTaskScheduleType", + "type": "string" + } + }, + "required": [ + "time", + "type" + ], + "title": "WeekdaysScheduledTaskSchedule", + "type": "object" + }, + { + "properties": { + "days": { + "items": { + "$ref": "#/definitions/ScheduledTaskWeekday" + }, + "type": "array" + }, + "time": { + "type": "string" + }, + "type": { + "enum": [ + "weekly" + ], + "title": "WeeklyScheduledTaskScheduleType", + "type": "string" + } + }, + "required": [ + "days", + "time", + "type" + ], + "title": "WeeklyScheduledTaskSchedule", + "type": "object" + } + ] + }, + "ScheduledTaskSummary": { + "properties": { + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "prompt": { + "type": "string" + }, + "schedule": { + "$ref": "#/definitions/ScheduledTaskSchedule" + } + }, + "required": [ + "key", + "name", + "prompt", + "schedule" + ], + "type": "object" + }, + "ScheduledTaskWeekday": { + "enum": [ + "MO", + "TU", + "WE", + "TH", + "FR", + "SA", + "SU" + ], + "type": "string" + }, "SkillInterface": { "properties": { "brandColor": { diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/PluginDetail.ts b/codex-rs/app-server-protocol/schema/typescript/v2/PluginDetail.ts index ab2e3fd5f88e..d4bf3f826153 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/PluginDetail.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/PluginDetail.ts @@ -6,6 +6,7 @@ import type { AppSummary } from "./AppSummary"; import type { AppTemplateSummary } from "./AppTemplateSummary"; import type { PluginHookSummary } from "./PluginHookSummary"; import type { PluginSummary } from "./PluginSummary"; +import type { ScheduledTaskSummary } from "./ScheduledTaskSummary"; import type { SkillSummary } from "./SkillSummary"; -export type PluginDetail = { marketplaceName: string, marketplacePath: AbsolutePathBuf | null, summary: PluginSummary, shareUrl: string | null, description: string | null, skills: Array, hooks: Array, apps: Array, appTemplates: Array, mcpServers: Array, }; +export type PluginDetail = { marketplaceName: string, marketplacePath: AbsolutePathBuf | null, summary: PluginSummary, shareUrl: string | null, description: string | null, skills: Array, hooks: Array, apps: Array, appTemplates: Array, mcpServers: Array, scheduledTasks: Array | null, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ScheduledTaskSchedule.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ScheduledTaskSchedule.ts new file mode 100644 index 000000000000..c8171273c563 --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ScheduledTaskSchedule.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ScheduledTaskWeekday } from "./ScheduledTaskWeekday"; + +export type ScheduledTaskSchedule = { "type": "hourly", intervalHours: number, days: Array | null, } | { "type": "daily", time: string, } | { "type": "weekdays", time: string, } | { "type": "weekly", days: Array, time: string, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ScheduledTaskSummary.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ScheduledTaskSummary.ts new file mode 100644 index 000000000000..91f7f954a21f --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ScheduledTaskSummary.ts @@ -0,0 +1,6 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ScheduledTaskSchedule } from "./ScheduledTaskSchedule"; + +export type ScheduledTaskSummary = { key: string, name: string, prompt: string, schedule: ScheduledTaskSchedule, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ScheduledTaskWeekday.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ScheduledTaskWeekday.ts new file mode 100644 index 000000000000..bf21096abfac --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ScheduledTaskWeekday.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ScheduledTaskWeekday = "MO" | "TU" | "WE" | "TH" | "FR" | "SA" | "SU"; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/index.ts b/codex-rs/app-server-protocol/schema/typescript/v2/index.ts index 070d21eab820..10fb369ef945 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/index.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/index.ts @@ -361,6 +361,9 @@ export type { ReviewTarget } from "./ReviewTarget"; export type { SandboxMode } from "./SandboxMode"; export type { SandboxPolicy } from "./SandboxPolicy"; export type { SandboxWorkspaceWrite } from "./SandboxWorkspaceWrite"; +export type { ScheduledTaskSchedule } from "./ScheduledTaskSchedule"; +export type { ScheduledTaskSummary } from "./ScheduledTaskSummary"; +export type { ScheduledTaskWeekday } from "./ScheduledTaskWeekday"; export type { SelectedCapabilityRoot } from "./SelectedCapabilityRoot"; export type { SendAddCreditsNudgeEmailParams } from "./SendAddCreditsNudgeEmailParams"; export type { SendAddCreditsNudgeEmailResponse } from "./SendAddCreditsNudgeEmailResponse"; 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 9bd53b5d1a2f..6cec32b68dcb 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2/plugin.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2/plugin.rs @@ -663,6 +663,55 @@ pub struct PluginDetail { pub apps: Vec, pub app_templates: Vec, pub mcp_servers: Vec, + pub scheduled_tasks: Option>, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export_to = "v2/")] +pub struct ScheduledTaskSummary { + pub key: String, + pub name: String, + pub prompt: String, + pub schedule: ScheduledTaskSchedule, +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] +#[serde(tag = "type", rename_all = "camelCase")] +#[ts(tag = "type")] +#[ts(export_to = "v2/")] +pub enum ScheduledTaskSchedule { + #[serde(rename_all = "camelCase")] + #[ts(rename_all = "camelCase")] + Hourly { + interval_hours: u32, + days: Option>, + }, + #[serde(rename_all = "camelCase")] + #[ts(rename_all = "camelCase")] + Daily { time: String }, + #[serde(rename_all = "camelCase")] + #[ts(rename_all = "camelCase")] + Weekdays { time: String }, + #[serde(rename_all = "camelCase")] + #[ts(rename_all = "camelCase")] + Weekly { + days: Vec, + time: String, + }, +} + +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +#[ts(export_to = "v2/")] +pub enum ScheduledTaskWeekday { + Mo, + Tu, + We, + Th, + Fr, + Sa, + Su, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] diff --git a/codex-rs/app-server/README.md b/codex-rs/app-server/README.md index 6ee6baf2dc6c..e23ba3a0a528 100644 --- a/codex-rs/app-server/README.md +++ b/codex-rs/app-server/README.md @@ -215,7 +215,7 @@ Example with notification opt-out: - `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. Clients can explicitly request the remote `workspace-directory`, `shared-with-me`, or `created-by-me-remote` marketplace kinds. `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/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 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. 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/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. 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. - `app/list` — list available apps. diff --git a/codex-rs/app-server/src/request_processors/plugins.rs b/codex-rs/app-server/src/request_processors/plugins.rs index f0d755852688..60e5fad6d77a 100644 --- a/codex-rs/app-server/src/request_processors/plugins.rs +++ b/codex-rs/app-server/src/request_processors/plugins.rs @@ -1129,6 +1129,7 @@ impl PluginRequestProcessor { apps: app_summaries, app_templates: Vec::new(), mcp_servers: outcome.plugin.mcp_server_names, + scheduled_tasks: None, } } Err(remote_marketplace_name) => { @@ -2276,6 +2277,7 @@ fn remote_plugin_detail_to_info( apps, app_templates, mcp_servers: detail.mcp_servers, + scheduled_tasks: detail.scheduled_tasks, } } diff --git a/codex-rs/app-server/tests/suite/v2/plugin_read.rs b/codex-rs/app-server/tests/suite/v2/plugin_read.rs index 09cede458151..b76158c17285 100644 --- a/codex-rs/app-server/tests/suite/v2/plugin_read.rs +++ b/codex-rs/app-server/tests/suite/v2/plugin_read.rs @@ -36,6 +36,9 @@ use codex_app_server_protocol::PluginSkillReadParams; use codex_app_server_protocol::PluginSkillReadResponse; use codex_app_server_protocol::PluginSource; use codex_app_server_protocol::RequestId; +use codex_app_server_protocol::ScheduledTaskSchedule; +use codex_app_server_protocol::ScheduledTaskSummary; +use codex_app_server_protocol::ScheduledTaskWeekday; use codex_config::types::AuthCredentialsStoreMode; use codex_utils_absolute_path::AbsolutePathBuf; use pretty_assertions::assert_eq; @@ -176,6 +179,27 @@ apps = true "logo_url_dark": "https://example.com/example-plugin-dark.png" }, "skills": [], + "scheduled_tasks": [ + { + "key": "weekday-triage", + "name": "Weekday triage", + "prompt": "Triage the support queue.", + "schedule": { + "type": "weekdays", + "time": "08:30" + } + }, + { + "key": "queue-monitor", + "name": "Queue monitor", + "prompt": "Check the queue.", + "schedule": { + "type": "hourly", + "intervalHours": 2, + "days": ["MO", "WE", "FR"] + } + } + ], "mcp_servers": [ { "key": "example-server", @@ -307,6 +331,32 @@ apps = true response.plugin.mcp_servers, vec!["other-server".to_string()] ); + assert_eq!( + response.plugin.scheduled_tasks, + Some(vec![ + ScheduledTaskSummary { + key: "weekday-triage".to_string(), + name: "Weekday triage".to_string(), + prompt: "Triage the support queue.".to_string(), + schedule: ScheduledTaskSchedule::Weekdays { + time: "08:30".to_string(), + }, + }, + ScheduledTaskSummary { + key: "queue-monitor".to_string(), + name: "Queue monitor".to_string(), + prompt: "Check the queue.".to_string(), + schedule: ScheduledTaskSchedule::Hourly { + interval_hours: 2, + days: Some(vec![ + ScheduledTaskWeekday::Mo, + ScheduledTaskWeekday::We, + ScheduledTaskWeekday::Fr, + ]), + }, + }, + ]) + ); assert_eq!( response .plugin @@ -975,6 +1025,7 @@ enabled = true mcp.read_stream_until_response_message(RequestId::Integer(request_id)), ) .await??; + assert_eq!(response.result["plugin"]["scheduledTasks"], json!(null)); let response: PluginReadResponse = to_response(response)?; assert_eq!(response.plugin.marketplace_name, "openai-curated"); diff --git a/codex-rs/core-plugins/src/remote.rs b/codex-rs/core-plugins/src/remote.rs index b9b94f4ded12..e5aef0bb4731 100644 --- a/codex-rs/core-plugins/src/remote.rs +++ b/codex-rs/core-plugins/src/remote.rs @@ -8,6 +8,7 @@ use codex_app_server_protocol::PluginAvailability; use codex_app_server_protocol::PluginInstallPolicy; use codex_app_server_protocol::PluginInstallPolicySource; use codex_app_server_protocol::PluginInterface; +use codex_app_server_protocol::ScheduledTaskSummary; use codex_app_server_protocol::SkillInterface; use codex_login::CodexAuth; use codex_login::default_client::build_reqwest_client; @@ -209,6 +210,7 @@ pub struct RemotePluginDetail { pub app_ids: Vec, pub app_templates: Vec, pub mcp_servers: Vec, + pub scheduled_tasks: Option>, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -504,6 +506,7 @@ struct RemotePluginReleaseResponse { skills: Vec, #[serde(default, skip_serializing_if = "Vec::is_empty")] mcp_servers: Vec, + scheduled_tasks: Option>, } #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] @@ -1290,6 +1293,7 @@ async fn build_remote_plugin_detail( }) .collect(), mcp_servers, + scheduled_tasks: plugin.release.scheduled_tasks, }) } diff --git a/codex-rs/core-plugins/src/remote_tests.rs b/codex-rs/core-plugins/src/remote_tests.rs index 29fe8add199f..ca34a86b50b8 100644 --- a/codex-rs/core-plugins/src/remote_tests.rs +++ b/codex-rs/core-plugins/src/remote_tests.rs @@ -96,6 +96,24 @@ fn unknown_installation_policy_source_maps_to_none() { assert_eq!(summary.install_policy_source, None); } +#[test] +fn scheduled_task_metadata_distinguishes_unavailable_from_empty() { + let release = serde_json::json!({ + "display_name": "Example", + "description": "Example plugin", + "interface": {}, + }); + let without_metadata: RemotePluginReleaseResponse = + serde_json::from_value(release.clone()).expect("release should deserialize"); + assert_eq!(without_metadata.scheduled_tasks, None); + + let mut with_empty_metadata = release; + with_empty_metadata["scheduled_tasks"] = serde_json::json!([]); + let with_empty_metadata: RemotePluginReleaseResponse = + serde_json::from_value(with_empty_metadata).expect("release should deserialize"); + assert_eq!(with_empty_metadata.scheduled_tasks, Some(Vec::new())); +} + fn directory_plugin(id: &str, name: &str) -> RemotePluginDirectoryItem { RemotePluginDirectoryItem { id: id.to_string(), @@ -138,6 +156,7 @@ fn directory_plugin(id: &str, name: &str) -> RemotePluginDirectoryItem { }, skills: Vec::new(), mcp_servers: Vec::new(), + scheduled_tasks: None, }, } } diff --git a/codex-rs/tui/src/chatwidget/tests/helpers.rs b/codex-rs/tui/src/chatwidget/tests/helpers.rs index 5f890ab000b9..41ff7e3b3284 100644 --- a/codex-rs/tui/src/chatwidget/tests/helpers.rs +++ b/codex-rs/tui/src/chatwidget/tests/helpers.rs @@ -1505,6 +1505,7 @@ pub(super) fn plugins_test_detail( .collect(), app_templates: Vec::new(), mcp_servers: mcp_servers.iter().map(|name| (*name).to_string()).collect(), + scheduled_tasks: None, } } @@ -1524,6 +1525,7 @@ pub(super) fn plugins_test_remote_detail( apps: Vec::new(), app_templates: Vec::new(), mcp_servers: Vec::new(), + scheduled_tasks: None, } } diff --git a/codex-rs/tui/src/chatwidget/tests/popups_and_settings.rs b/codex-rs/tui/src/chatwidget/tests/popups_and_settings.rs index 07cfc7dbbbb2..49e6cd0b70cb 100644 --- a/codex-rs/tui/src/chatwidget/tests/popups_and_settings.rs +++ b/codex-rs/tui/src/chatwidget/tests/popups_and_settings.rs @@ -876,6 +876,7 @@ async fn plugin_detail_unmaterialized_default_uses_remote_install_path() { apps: Vec::new(), app_templates: Vec::new(), mcp_servers: Vec::new(), + scheduled_tasks: None, }, }), );