diff --git a/codex-rs/analytics/src/analytics_client_tests.rs b/codex-rs/analytics/src/analytics_client_tests.rs index 7a49f7221e..ec09ed63e4 100644 --- a/codex-rs/analytics/src/analytics_client_tests.rs +++ b/codex-rs/analytics/src/analytics_client_tests.rs @@ -287,6 +287,7 @@ fn sample_thread_start_response( model: &str, ) -> ClientResponsePayload { ClientResponsePayload::ThreadStart(ThreadStartResponse { + disabled_plugin_ids: Vec::new(), thread: sample_thread_with_metadata( thread_id, ephemeral, @@ -352,6 +353,7 @@ fn sample_thread_resume_response_with_source( parent_thread_id: Option, ) -> ClientResponsePayload { ClientResponsePayload::ThreadResume(ThreadResumeResponse { + disabled_plugin_ids: Vec::new(), thread: sample_thread_with_metadata( thread_id, ephemeral, diff --git a/codex-rs/analytics/src/client_tests.rs b/codex-rs/analytics/src/client_tests.rs index 494c23a358..ccb5a6ff4e 100644 --- a/codex-rs/analytics/src/client_tests.rs +++ b/codex-rs/analytics/src/client_tests.rs @@ -625,6 +625,7 @@ fn sample_thread(thread_id: &str) -> Thread { fn sample_thread_start_response() -> ClientResponsePayload { ClientResponsePayload::ThreadStart(ThreadStartResponse { + disabled_plugin_ids: Vec::new(), thread: sample_thread("thread-1"), model: "gpt-5".to_string(), model_provider: "openai".to_string(), @@ -643,6 +644,7 @@ fn sample_thread_start_response() -> ClientResponsePayload { fn sample_thread_resume_response() -> ClientResponsePayload { ClientResponsePayload::ThreadResume(ThreadResumeResponse { + disabled_plugin_ids: Vec::new(), thread: sample_thread("thread-2"), model: "gpt-5".to_string(), model_provider: "openai".to_string(), @@ -664,6 +666,7 @@ fn sample_thread_resume_response() -> ClientResponsePayload { fn sample_thread_fork_response() -> ClientResponsePayload { ClientResponsePayload::ThreadFork(ThreadForkResponse { + disabled_plugin_ids: Vec::new(), thread: sample_thread("thread-3"), model: "gpt-5".to_string(), model_provider: "openai".to_string(), diff --git a/codex-rs/app-server-protocol/schema/json/ClientRequest.json b/codex-rs/app-server-protocol/schema/json/ClientRequest.json index 35f5b33bcb..641708d961 100644 --- a/codex-rs/app-server-protocol/schema/json/ClientRequest.json +++ b/codex-rs/app-server-protocol/schema/json/ClientRequest.json @@ -5470,6 +5470,16 @@ "null" ] }, + "disabledPluginIds": { + "description": "Replace this thread's disabled plugin IDs. Omitted/null preserves the list; [] clears it.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, "effort": { "anyOf": [ { diff --git a/codex-rs/app-server-protocol/schema/json/ServerNotification.json b/codex-rs/app-server-protocol/schema/json/ServerNotification.json index 48efe6aed4..83129264a9 100644 --- a/codex-rs/app-server-protocol/schema/json/ServerNotification.json +++ b/codex-rs/app-server-protocol/schema/json/ServerNotification.json @@ -5895,6 +5895,14 @@ "cwd": { "$ref": "#/definitions/AbsolutePathBuf" }, + "disabledPluginIds": { + "default": [], + "description": "Saved list of disabled plugin IDs. Does not yet filter plugin capabilities.", + "items": { + "type": "string" + }, + "type": "array" + }, "effort": { "anyOf": [ { 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 ec5053bc3d..6bbd4aedbd 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 @@ -21105,6 +21105,14 @@ "cwd": { "$ref": "#/definitions/v2/AbsolutePathBuf" }, + "disabledPluginIds": { + "default": [], + "description": "Saved list of disabled plugin IDs. Does not yet filter plugin capabilities.", + "items": { + "type": "string" + }, + "type": "array" + }, "instructionSources": { "default": [], "description": "Environment-native paths to instruction source files currently loaded for this thread.", @@ -23353,6 +23361,14 @@ "cwd": { "$ref": "#/definitions/v2/AbsolutePathBuf" }, + "disabledPluginIds": { + "default": [], + "description": "Saved list of disabled plugin IDs. Does not yet filter plugin capabilities.", + "items": { + "type": "string" + }, + "type": "array" + }, "instructionSources": { "default": [], "description": "Environment-native paths to instruction source files currently loaded for this thread.", @@ -23832,6 +23848,14 @@ "cwd": { "$ref": "#/definitions/v2/AbsolutePathBuf" }, + "disabledPluginIds": { + "default": [], + "description": "Saved list of disabled plugin IDs. Does not yet filter plugin capabilities.", + "items": { + "type": "string" + }, + "type": "array" + }, "effort": { "anyOf": [ { @@ -24105,6 +24129,14 @@ "cwd": { "$ref": "#/definitions/v2/AbsolutePathBuf" }, + "disabledPluginIds": { + "default": [], + "description": "Saved list of disabled plugin IDs. Does not yet filter plugin capabilities.", + "items": { + "type": "string" + }, + "type": "array" + }, "instructionSources": { "default": [], "description": "Environment-native paths to instruction source files currently loaded for this thread.", @@ -25135,6 +25167,16 @@ "null" ] }, + "disabledPluginIds": { + "description": "Replace this thread's disabled plugin IDs. Omitted/null preserves the list; [] clears it.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, "effort": { "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 61cf3b0eba..45f5721082 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 @@ -18819,6 +18819,14 @@ "cwd": { "$ref": "#/definitions/AbsolutePathBuf" }, + "disabledPluginIds": { + "default": [], + "description": "Saved list of disabled plugin IDs. Does not yet filter plugin capabilities.", + "items": { + "type": "string" + }, + "type": "array" + }, "instructionSources": { "default": [], "description": "Environment-native paths to instruction source files currently loaded for this thread.", @@ -21067,6 +21075,14 @@ "cwd": { "$ref": "#/definitions/AbsolutePathBuf" }, + "disabledPluginIds": { + "default": [], + "description": "Saved list of disabled plugin IDs. Does not yet filter plugin capabilities.", + "items": { + "type": "string" + }, + "type": "array" + }, "instructionSources": { "default": [], "description": "Environment-native paths to instruction source files currently loaded for this thread.", @@ -21546,6 +21562,14 @@ "cwd": { "$ref": "#/definitions/AbsolutePathBuf" }, + "disabledPluginIds": { + "default": [], + "description": "Saved list of disabled plugin IDs. Does not yet filter plugin capabilities.", + "items": { + "type": "string" + }, + "type": "array" + }, "effort": { "anyOf": [ { @@ -21819,6 +21843,14 @@ "cwd": { "$ref": "#/definitions/AbsolutePathBuf" }, + "disabledPluginIds": { + "default": [], + "description": "Saved list of disabled plugin IDs. Does not yet filter plugin capabilities.", + "items": { + "type": "string" + }, + "type": "array" + }, "instructionSources": { "default": [], "description": "Environment-native paths to instruction source files currently loaded for this thread.", @@ -22849,6 +22881,16 @@ "null" ] }, + "disabledPluginIds": { + "description": "Replace this thread's disabled plugin IDs. Omitted/null preserves the list; [] clears it.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, "effort": { "anyOf": [ { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json index d5b3c56db3..829579af3c 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json @@ -2942,6 +2942,14 @@ "cwd": { "$ref": "#/definitions/AbsolutePathBuf" }, + "disabledPluginIds": { + "default": [], + "description": "Saved list of disabled plugin IDs. Does not yet filter plugin capabilities.", + "items": { + "type": "string" + }, + "type": "array" + }, "instructionSources": { "default": [], "description": "Environment-native paths to instruction source files currently loaded for this thread.", diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json index efdcfc5d9b..9569af82c3 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json @@ -2968,6 +2968,14 @@ "cwd": { "$ref": "#/definitions/AbsolutePathBuf" }, + "disabledPluginIds": { + "default": [], + "description": "Saved list of disabled plugin IDs. Does not yet filter plugin capabilities.", + "items": { + "type": "string" + }, + "type": "array" + }, "instructionSources": { "default": [], "description": "Environment-native paths to instruction source files currently loaded for this thread.", diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadSettingsUpdatedNotification.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadSettingsUpdatedNotification.json index f3296fa376..a1809df664 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadSettingsUpdatedNotification.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadSettingsUpdatedNotification.json @@ -323,6 +323,14 @@ "cwd": { "$ref": "#/definitions/AbsolutePathBuf" }, + "disabledPluginIds": { + "default": [], + "description": "Saved list of disabled plugin IDs. Does not yet filter plugin capabilities.", + "items": { + "type": "string" + }, + "type": "array" + }, "effort": { "anyOf": [ { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json index 0a9cc8b2ab..a39db2c15d 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json @@ -2942,6 +2942,14 @@ "cwd": { "$ref": "#/definitions/AbsolutePathBuf" }, + "disabledPluginIds": { + "default": [], + "description": "Saved list of disabled plugin IDs. Does not yet filter plugin capabilities.", + "items": { + "type": "string" + }, + "type": "array" + }, "instructionSources": { "default": [], "description": "Environment-native paths to instruction source files currently loaded for this thread.", diff --git a/codex-rs/app-server-protocol/schema/json/v2/TurnStartParams.json b/codex-rs/app-server-protocol/schema/json/v2/TurnStartParams.json index 2ba2e1f483..9f8637ea17 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/TurnStartParams.json +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnStartParams.json @@ -737,6 +737,16 @@ "null" ] }, + "disabledPluginIds": { + "description": "Replace this thread's disabled plugin IDs. Omitted/null preserves the list; [] clears it.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, "effort": { "anyOf": [ { diff --git a/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-experimental.json.zst b/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-experimental.json.zst index 963ea9088e..88c9c28a76 100644 Binary files a/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-experimental.json.zst and b/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-experimental.json.zst differ diff --git a/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-stable.json.zst b/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-stable.json.zst index 0c24ab3b61..416d36cff5 100644 Binary files a/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-stable.json.zst and b/codex-rs/app-server-protocol/schema/precomputed/app-server-exports-stable.json.zst differ diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadForkResponse.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadForkResponse.ts index 9577562476..5fab371154 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadForkResponse.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadForkResponse.ts @@ -9,7 +9,10 @@ import type { AskForApproval } from "./AskForApproval"; import type { SandboxPolicy } from "./SandboxPolicy"; import type { Thread } from "./Thread"; -export type ThreadForkResponse = {thread: Thread, model: string, modelProvider: string, serviceTier: string | null, cwd: AbsolutePathBuf, /** +export type ThreadForkResponse = {thread: Thread, model: string, modelProvider: string, serviceTier: string | null, /** + * Saved list of disabled plugin IDs. Does not yet filter plugin capabilities. + */ +disabledPluginIds: Array, cwd: AbsolutePathBuf, /** * Environment-native paths to instruction source files currently loaded for this thread. */ instructionSources: Array, approvalPolicy: AskForApproval, /** diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadResumeResponse.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadResumeResponse.ts index d1282c7a2d..eaea856658 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadResumeResponse.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadResumeResponse.ts @@ -9,7 +9,10 @@ import type { AskForApproval } from "./AskForApproval"; import type { SandboxPolicy } from "./SandboxPolicy"; import type { Thread } from "./Thread"; -export type ThreadResumeResponse = {thread: Thread, model: string, modelProvider: string, serviceTier: string | null, cwd: AbsolutePathBuf, /** +export type ThreadResumeResponse = {thread: Thread, model: string, modelProvider: string, serviceTier: string | null, /** + * Saved list of disabled plugin IDs. Does not yet filter plugin capabilities. + */ +disabledPluginIds: Array, cwd: AbsolutePathBuf, /** * Environment-native paths to instruction source files currently loaded for this thread. */ instructionSources: Array, approvalPolicy: AskForApproval, /** diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadSettings.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadSettings.ts index b034ea80bb..71b566b99b 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadSettings.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadSettings.ts @@ -11,4 +11,7 @@ import type { ApprovalsReviewer } from "./ApprovalsReviewer"; import type { AskForApproval } from "./AskForApproval"; import type { SandboxPolicy } from "./SandboxPolicy"; -export type ThreadSettings = {cwd: AbsolutePathBuf, approvalPolicy: AskForApproval, approvalsReviewer: ApprovalsReviewer, sandboxPolicy: SandboxPolicy, activePermissionProfile: ActivePermissionProfile | null, model: string, modelProvider: string, serviceTier: string | null, effort: ReasoningEffort | null, summary: ReasoningSummary | null, collaborationMode: CollaborationMode, personality: Personality | null}; +export type ThreadSettings = {/** + * Saved list of disabled plugin IDs. Does not yet filter plugin capabilities. + */ +disabledPluginIds: Array, cwd: AbsolutePathBuf, approvalPolicy: AskForApproval, approvalsReviewer: ApprovalsReviewer, sandboxPolicy: SandboxPolicy, activePermissionProfile: ActivePermissionProfile | null, model: string, modelProvider: string, serviceTier: string | null, effort: ReasoningEffort | null, summary: ReasoningSummary | null, collaborationMode: CollaborationMode, personality: Personality | null}; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadStartResponse.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadStartResponse.ts index 992ab5dba7..c4ff08409c 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadStartResponse.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadStartResponse.ts @@ -9,7 +9,10 @@ import type { AskForApproval } from "./AskForApproval"; import type { SandboxPolicy } from "./SandboxPolicy"; import type { Thread } from "./Thread"; -export type ThreadStartResponse = {thread: Thread, model: string, modelProvider: string, serviceTier: string | null, cwd: AbsolutePathBuf, /** +export type ThreadStartResponse = {thread: Thread, model: string, modelProvider: string, serviceTier: string | null, /** + * Saved list of disabled plugin IDs. Does not yet filter plugin capabilities. + */ +disabledPluginIds: Array, cwd: AbsolutePathBuf, /** * Environment-native paths to instruction source files currently loaded for this thread. */ instructionSources: Array, approvalPolicy: AskForApproval, /** diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/TurnStartParams.ts b/codex-rs/app-server-protocol/schema/typescript/v2/TurnStartParams.ts index 97ed892f8f..b201450e73 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/TurnStartParams.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/TurnStartParams.ts @@ -11,7 +11,11 @@ import type { SandboxPolicy } from "./SandboxPolicy"; import type { TurnToolOutput } from "./TurnToolOutput"; import type { UserInput } from "./UserInput"; -export type TurnStartParams = {threadId: string, clientUserMessageId?: string | null, input: Array, /** +export type TurnStartParams = {threadId: string, /** + * Replace this thread's disabled plugin IDs. + * Omitted/null preserves the list; [] clears it. + */ +disabledPluginIds?: Array | null, clientUserMessageId?: string | null, input: Array, /** * Optional source classification for the caller that starts this turn. * Ignored when this request steers an already-active turn. */ diff --git a/codex-rs/app-server-protocol/src/protocol/common.rs b/codex-rs/app-server-protocol/src/protocol/common.rs index 70954a3d60..26ac59ec6b 100644 --- a/codex-rs/app-server-protocol/src/protocol/common.rs +++ b/codex-rs/app-server-protocol/src/protocol/common.rs @@ -3184,6 +3184,7 @@ mod tests { let response = ClientResponse::ThreadStart { request_id: RequestId::Integer(7), response: v2::ThreadStartResponse { + disabled_plugin_ids: Vec::new(), thread: v2::Thread { originator: None, environments: None, @@ -3283,6 +3284,7 @@ mod tests { "model": "gpt-5", "modelProvider": "openai", "serviceTier": null, + "disabledPluginIds": [], "cwd": absolute_path_string("tmp"), "runtimeWorkspaceRoots": [], "instructionSources": [absolute_path_string("tmp/AGENTS.md")], @@ -4477,6 +4479,7 @@ mod tests { ServerNotification::ThreadSettingsUpdated(v2::ThreadSettingsUpdatedNotification { thread_id: "thr_123".to_string(), thread_settings: v2::ThreadSettings { + disabled_plugin_ids: Vec::new(), cwd: absolute_path("/tmp/repo"), approval_policy: v2::AskForApproval::Never, approvals_reviewer: v2::ApprovalsReviewer::User, 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 9e535721ad..62414af295 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2/tests.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2/tests.rs @@ -271,6 +271,7 @@ fn thread_resume_params_accept_turns_page_bootstrap() { #[test] fn thread_resume_response_round_trips_initial_turns_page() { let response = ThreadResumeResponse { + disabled_plugin_ids: Vec::new(), thread: Thread { originator: Some("future_client".to_string()), environments: Some(vec![ThreadEnvironment { @@ -4815,6 +4816,7 @@ fn turn_start_params_preserve_explicit_null_service_tier() { ); let without_override = TurnStartParams { + disabled_plugin_ids: None, thread_id: "thread_123".to_string(), client_user_message_id: None, input: vec![], diff --git a/codex-rs/app-server-protocol/src/protocol/v2/thread.rs b/codex-rs/app-server-protocol/src/protocol/v2/thread.rs index 28ca20f7b4..f13ab99817 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2/thread.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2/thread.rs @@ -183,6 +183,9 @@ pub struct ThreadStartResponse { pub model: String, pub model_provider: String, pub service_tier: Option, + /// Saved list of disabled plugin IDs. Does not yet filter plugin capabilities. + #[serde(default)] + pub disabled_plugin_ids: Vec, pub cwd: AbsolutePathBuf, /// Thread-scoped runtime workspace roots used to materialize /// `:workspace_roots`. @@ -225,6 +228,10 @@ impl ThreadStartResponse { #[ts(export_to = "v2/")] pub struct ThreadSettingsUpdateParams { pub thread_id: String, + /// Replace this thread's disabled plugin IDs. + /// Omitted/null preserves the list; [] clears it. + #[ts(optional = nullable)] + pub disabled_plugin_ids: Option>, /// Override the working directory for subsequent turns. #[ts(optional = nullable)] pub cwd: Option, @@ -287,6 +294,9 @@ pub struct ThreadSettingsUpdateResponse {} #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct ThreadSettings { + /// Saved list of disabled plugin IDs. Does not yet filter plugin capabilities. + #[serde(default)] + pub disabled_plugin_ids: Vec, pub cwd: AbsolutePathBuf, pub approval_policy: AskForApproval, pub approvals_reviewer: ApprovalsReviewer, @@ -417,6 +427,9 @@ pub struct ThreadResumeResponse { pub model: String, pub model_provider: String, pub service_tier: Option, + /// Saved list of disabled plugin IDs. Does not yet filter plugin capabilities. + #[serde(default)] + pub disabled_plugin_ids: Vec, pub cwd: AbsolutePathBuf, /// Thread-scoped runtime workspace roots used to materialize /// `:workspace_roots`. @@ -608,6 +621,9 @@ pub struct ThreadForkResponse { pub model: String, pub model_provider: String, pub service_tier: Option, + /// Saved list of disabled plugin IDs. Does not yet filter plugin capabilities. + #[serde(default)] + pub disabled_plugin_ids: Vec, pub cwd: AbsolutePathBuf, /// Thread-scoped runtime workspace roots used to materialize /// `:workspace_roots`. diff --git a/codex-rs/app-server-protocol/src/protocol/v2/turn.rs b/codex-rs/app-server-protocol/src/protocol/v2/turn.rs index 394dec951c..a219c5f660 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2/turn.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2/turn.rs @@ -165,6 +165,10 @@ pub struct TurnToolOutput { #[ts(export_to = "v2/")] pub struct TurnStartParams { pub thread_id: String, + /// Replace this thread's disabled plugin IDs. + /// Omitted/null preserves the list; [] clears it. + #[ts(optional = nullable)] + pub disabled_plugin_ids: Option>, #[ts(optional = nullable)] pub client_user_message_id: Option, pub input: Vec, diff --git a/codex-rs/app-server/README.md b/codex-rs/app-server/README.md index 509f1f994a..75b318f7b6 100644 --- a/codex-rs/app-server/README.md +++ b/codex-rs/app-server/README.md @@ -183,6 +183,20 @@ Attachments record the resources currently associated with a thread, independent Attachment creation and deletion requests using the same thread ID are serialized across connections. The requesting client receives its response before the compact update is broadcast, and duplicate creates or absent deletes do not emit updates. Deleting the owning thread removes its attachments under the same lifecycle exclusion; queued attachment mutations then report that the thread was not found. +# Thread plugin settings + +`thread/settings/update` and `turn/start` accept `disabledPluginIds`, a list of +`PluginSummary.id` values from `plugin/list`, in the +`@` format. A supplied list replaces the selection; +omission or `null` preserves it, and `[]` clears it. Saving this selection does +not yet filter plugin capabilities. + +Read the selection from `threadSettings.disabledPluginIds` in +`thread/settings/updated` notifications, or from `disabledPluginIds` in +`thread/start`, `thread/resume`, and `thread/fork` responses. Selections persist +across resume. Forks restore the selection from the history retained at the +requested fork boundary. + # MCP server capabilities `mcpServerStatus/list` returns `serverCapabilities` for each initialized MCP server diff --git a/codex-rs/app-server/src/message_processor_tracing_tests.rs b/codex-rs/app-server/src/message_processor_tracing_tests.rs index 260092a4e7..6715805dc9 100644 --- a/codex-rs/app-server/src/message_processor_tracing_tests.rs +++ b/codex-rs/app-server/src/message_processor_tracing_tests.rs @@ -662,6 +662,7 @@ async fn turn_start_jsonrpc_span_parents_core_turn_spans() -> Result<()> { ClientRequest::TurnStart { request_id: RequestId::Integer(3), params: TurnStartParams { + disabled_plugin_ids: None, environments: None, thread_id, client_user_message_id: None, diff --git a/codex-rs/app-server/src/request_processors/thread_lifecycle.rs b/codex-rs/app-server/src/request_processors/thread_lifecycle.rs index 07e5145980..fbc4d32cf1 100644 --- a/codex-rs/app-server/src/request_processors/thread_lifecycle.rs +++ b/codex-rs/app-server/src/request_processors/thread_lifecycle.rs @@ -730,6 +730,7 @@ pub(super) async fn handle_pending_thread_resume_request( let cwd = config_snapshot.cwd().clone(); let ThreadConfigSnapshot { model, + disabled_plugin_ids, model_provider_id, service_tier, approval_policy, @@ -748,6 +749,7 @@ pub(super) async fn handle_pending_thread_resume_request( let response = ThreadResumeResponse { thread, + disabled_plugin_ids, model, model_provider: model_provider_id, service_tier, diff --git a/codex-rs/app-server/src/request_processors/thread_processor.rs b/codex-rs/app-server/src/request_processors/thread_processor.rs index 4a5f226a87..f694d03c3b 100644 --- a/codex-rs/app-server/src/request_processors/thread_processor.rs +++ b/codex-rs/app-server/src/request_processors/thread_processor.rs @@ -1617,6 +1617,7 @@ impl ThreadRequestProcessor { let response = ThreadStartResponse { thread: thread.clone(), + disabled_plugin_ids: config_snapshot.disabled_plugin_ids, model: config_snapshot.model, model_provider: config_snapshot.model_provider_id, service_tier: config_snapshot.service_tier, @@ -4177,6 +4178,7 @@ impl ThreadRequestProcessor { let thread_originator = config_snapshot.originator.clone(); let response = ThreadResumeResponse { thread, + disabled_plugin_ids: config_snapshot.disabled_plugin_ids, model: session_configured.model, model_provider: session_configured.model_provider_id, service_tier: session_configured.service_tier, @@ -5369,6 +5371,7 @@ impl ThreadRequestProcessor { let thread_originator = config_snapshot.originator.clone(); let response = ThreadForkResponse { thread: thread.clone(), + disabled_plugin_ids: config_snapshot.disabled_plugin_ids, model: session_configured.model, model_provider: session_configured.model_provider_id, service_tier: session_configured.service_tier, diff --git a/codex-rs/app-server/src/request_processors/thread_processor_tests.rs b/codex-rs/app-server/src/request_processors/thread_processor_tests.rs index 818ed4ba95..949011c9f6 100644 --- a/codex-rs/app-server/src/request_processors/thread_processor_tests.rs +++ b/codex-rs/app-server/src/request_processors/thread_processor_tests.rs @@ -679,6 +679,7 @@ mod thread_processor_behavior_tests { initial_turns_page: None, }; let config_snapshot = ThreadConfigSnapshot { + disabled_plugin_ids: Vec::new(), model: "gpt-5".to_string(), model_provider_id: "openai".to_string(), service_tier: Some("flex".to_string()), diff --git a/codex-rs/app-server/src/request_processors/thread_summary.rs b/codex-rs/app-server/src/request_processors/thread_summary.rs index 75bf249c63..42562e4084 100644 --- a/codex-rs/app-server/src/request_processors/thread_summary.rs +++ b/codex-rs/app-server/src/request_processors/thread_summary.rs @@ -176,6 +176,7 @@ pub(crate) fn thread_settings_from_config_snapshot( config_snapshot: &ThreadConfigSnapshot, ) -> ThreadSettings { ThreadSettings { + disabled_plugin_ids: config_snapshot.disabled_plugin_ids.clone(), cwd: config_snapshot.cwd().clone(), approval_policy: config_snapshot.approval_policy.into(), approvals_reviewer: config_snapshot.approvals_reviewer.into(), diff --git a/codex-rs/app-server/src/request_processors/turn_processor.rs b/codex-rs/app-server/src/request_processors/turn_processor.rs index edceb7b167..f2a84fe623 100644 --- a/codex-rs/app-server/src/request_processors/turn_processor.rs +++ b/codex-rs/app-server/src/request_processors/turn_processor.rs @@ -122,6 +122,7 @@ struct ThreadEnvironmentOverride { struct ThreadSettingsBuildParams { method: &'static str, + disabled_plugin_ids: Option>, environment_override: ThreadEnvironmentOverride, approval_policy: Option, approvals_reviewer: Option, @@ -627,6 +628,7 @@ impl TurnRequestProcessor { thread.as_ref(), ThreadSettingsBuildParams { method: "turn/start", + disabled_plugin_ids: params.disabled_plugin_ids, environment_override, approval_policy: params.approval_policy, approvals_reviewer: params.approvals_reviewer, @@ -774,6 +776,7 @@ impl TurnRequestProcessor { ) -> Result { let ThreadSettingsBuildParams { method, + disabled_plugin_ids, environment_override: ThreadEnvironmentOverride { environments, @@ -810,6 +813,7 @@ impl TurnRequestProcessor { }; let has_any_overrides = has_environment_override + || disabled_plugin_ids.is_some() || approval_policy.is_some() || approvals_reviewer.is_some() || sandbox_policy.is_some() @@ -874,7 +878,7 @@ impl TurnRequestProcessor { if has_any_overrides { thread .preview_thread_settings_overrides(CodexThreadSettingsOverrides { - disabled_plugin_ids: None, + disabled_plugin_ids: disabled_plugin_ids.clone(), environments: environments.clone(), runtime_workspace_roots: runtime_workspace_roots.clone(), approval_policy, @@ -898,7 +902,7 @@ impl TurnRequestProcessor { } Ok(codex_protocol::protocol::ThreadSettingsOverrides { - disabled_plugin_ids: None, + disabled_plugin_ids, environments, runtime_workspace_roots, profile_workspace_roots, @@ -939,6 +943,7 @@ impl TurnRequestProcessor { thread.as_ref(), ThreadSettingsBuildParams { method: "thread/settings/update", + disabled_plugin_ids: params.disabled_plugin_ids, environment_override, approval_policy: params.approval_policy, approvals_reviewer: params.approvals_reviewer, diff --git a/codex-rs/app-server/src/thread_state.rs b/codex-rs/app-server/src/thread_state.rs index ba011dd73a..dcba292310 100644 --- a/codex-rs/app-server/src/thread_state.rs +++ b/codex-rs/app-server/src/thread_state.rs @@ -291,6 +291,7 @@ mod tests { fn thread_settings(model: &str) -> ThreadSettings { ThreadSettings { + disabled_plugin_ids: Vec::new(), cwd: AbsolutePathBuf::from_absolute_path("/tmp").expect("absolute path"), approval_policy: AskForApproval::OnRequest, approvals_reviewer: ApprovalsReviewer::User, diff --git a/codex-rs/app-server/tests/suite/v2/thread_settings_update.rs b/codex-rs/app-server/tests/suite/v2/thread_settings_update.rs index d51578dfdb..52725e5fad 100644 --- a/codex-rs/app-server/tests/suite/v2/thread_settings_update.rs +++ b/codex-rs/app-server/tests/suite/v2/thread_settings_update.rs @@ -5,12 +5,21 @@ use app_test_support::TestAppServer; use app_test_support::create_final_assistant_message_sse_response; use app_test_support::create_mock_responses_server_sequence_unchecked; use app_test_support::write_models_cache; +use codex_app_server_protocol::ApprovalsReviewer; +use codex_app_server_protocol::AskForApproval; +use codex_app_server_protocol::ClientRequest; use codex_app_server_protocol::JSONRPCError; use codex_app_server_protocol::RequestId; +use codex_app_server_protocol::SandboxMode; use codex_app_server_protocol::SandboxPolicy; +use codex_app_server_protocol::ThreadForkParams; +use codex_app_server_protocol::ThreadForkResponse; +use codex_app_server_protocol::ThreadHistoryMode; use codex_app_server_protocol::ThreadListResponse; use codex_app_server_protocol::ThreadReadParams; use codex_app_server_protocol::ThreadReadResponse; +use codex_app_server_protocol::ThreadResumeParams; +use codex_app_server_protocol::ThreadResumeResponse; use codex_app_server_protocol::ThreadSettingsUpdateParams; use codex_app_server_protocol::ThreadSettingsUpdateResponse; use codex_app_server_protocol::ThreadSettingsUpdatedNotification; @@ -35,10 +44,188 @@ use serde_json::Value; use serde_json::json; use std::time::Duration; use tempfile::TempDir; +use test_case::test_case; use tokio::time::timeout; const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10); +#[tokio::test] +async fn disabled_plugin_ids_replace_preserve_and_clear_without_inference() -> Result<()> { + let server = responses::start_mock_server().await; + let codex_home = TempDir::new()?; + create_config_toml(codex_home.path(), &server.uri())?; + let mut mcp = TestAppServer::builder() + .with_codex_home(codex_home.path()) + .build_initialized() + .await?; + let started = start_thread(&mut mcp).await?; + assert_eq!(started.disabled_plugin_ids, Vec::::new()); + + for (mut params, expected) in [ + ( + json!({"disabledPluginIds": ["slack@openai", "notion@openai"]}), + vec!["slack@openai", "notion@openai"], + ), + ( + json!({"disabledPluginIds": ["slack@openai"]}), + vec!["slack@openai"], + ), + (json!({"model": "mock-model-2"}), vec!["slack@openai"]), + ( + json!({"disabledPluginIds": null, "model": "mock-model-3"}), + vec!["slack@openai"], + ), + (json!({"disabledPluginIds": []}), vec![]), + ] { + params["threadId"] = json!(started.thread.id); + let request_id = mcp + .send_raw_request("thread/settings/update", Some(params)) + .await?; + let _: ThreadSettingsUpdateResponse = + timeout(DEFAULT_TIMEOUT, mcp.read_response(request_id)).await??; + let updated = read_thread_settings_updated(&mut mcp).await?; + assert_eq!(updated.thread_settings.disabled_plugin_ids, expected); + } + assert!(received_response_bodies(&server).await?.is_empty()); + Ok(()) +} + +#[test_case(ThreadHistoryMode::Legacy, true; "cold legacy parent")] +#[test_case(ThreadHistoryMode::Legacy, false; "loaded legacy parent")] +#[test_case(ThreadHistoryMode::Paginated, true; "cold paginated parent")] +#[test_case(ThreadHistoryMode::Paginated, false; "loaded paginated parent")] +#[tokio::test] +async fn disabled_plugin_ids_restore_from_fork_boundary( + history_mode: ThreadHistoryMode, + restart_before_fork: bool, +) -> Result<()> { + let server = create_mock_responses_server_sequence_unchecked(vec![ + create_final_assistant_message_sse_response("done")?, + create_final_assistant_message_sse_response("done again")?, + ]) + .await; + let codex_home = TempDir::new()?; + create_config_toml(codex_home.path(), &server.uri())?; + let mut mcp = TestAppServer::builder() + .with_codex_home(codex_home.path()) + .build_initialized() + .await?; + let started = mcp + .start_thread(ThreadStartParams { + history_mode: Some(history_mode), + ..Default::default() + }) + .await?; + let thread_id = started.thread.id; + let initial_selection = vec!["slack@openai".to_string()]; + let first_turn = mcp + .start_turn_and_wait_for_completion(TurnStartParams { + thread_id: thread_id.clone(), + disabled_plugin_ids: Some(initial_selection.clone()), + input: vec![V2UserInput::Text { + text: "materialize the thread".to_string(), + text_elements: Vec::new(), + }], + ..Default::default() + }) + .await?; + assert_eq!( + read_thread_settings_updated(&mut mcp) + .await? + .thread_settings + .disabled_plugin_ids, + initial_selection + ); + let second_turn = mcp + .start_turn_and_wait_for_completion(TurnStartParams { + thread_id: thread_id.clone(), + input: vec![V2UserInput::Text { + text: "continue beyond the fork cutoff".to_string(), + text_elements: Vec::new(), + }], + ..Default::default() + }) + .await?; + // Clearing after the second turn must not affect forks at an earlier boundary. + let current_selection = Vec::::new(); + send_thread_settings_update( + &mut mcp, + ThreadSettingsUpdateParams { + thread_id: thread_id.clone(), + disabled_plugin_ids: Some(current_selection.clone()), + ..Default::default() + }, + ) + .await?; + assert_eq!( + read_thread_settings_updated(&mut mcp) + .await? + .thread_settings + .disabled_plugin_ids, + current_selection + ); + if restart_before_fork { + mcp.shutdown_gracefully().await?; + mcp = TestAppServer::builder() + .with_codex_home(codex_home.path()) + .build_initialized() + .await?; + } + for (params, expected_selection) in [ + ( + ThreadForkParams { + thread_id: thread_id.clone(), + ..Default::default() + }, + ¤t_selection, + ), + ( + ThreadForkParams { + thread_id: thread_id.clone(), + last_turn_id: Some(first_turn.turn.id.clone()), + ..Default::default() + }, + &initial_selection, + ), + ( + ThreadForkParams { + thread_id: thread_id.clone(), + before_turn_id: Some(second_turn.turn.id), + ..Default::default() + }, + &initial_selection, + ), + ( + ThreadForkParams { + thread_id: thread_id.clone(), + last_turn_id: Some(first_turn.turn.id), + // Selection restoration must not depend on restoring permissions. + approval_policy: Some(AskForApproval::Never), + approvals_reviewer: Some(ApprovalsReviewer::User), + sandbox: Some(SandboxMode::DangerFullAccess), + ..Default::default() + }, + &initial_selection, + ), + ] { + let forked: ThreadForkResponse = mcp + .request(|request_id| ClientRequest::ThreadFork { request_id, params }) + .await?; + assert_eq!(&forked.disabled_plugin_ids, expected_selection); + } + let resumed: ThreadResumeResponse = mcp + .request(|request_id| ClientRequest::ThreadResume { + request_id, + params: ThreadResumeParams { + thread_id, + ..Default::default() + }, + }) + .await?; + assert_eq!(resumed.disabled_plugin_ids, current_selection); + Ok(()) +} + #[tokio::test] async fn thread_settings_update_emits_notification_and_updates_future_turns() -> Result<()> { let server = create_mock_responses_server_sequence_unchecked(vec![ @@ -416,6 +603,7 @@ async fn turn_start_settings_override_emits_thread_settings_updated() -> Result< text_elements: Vec::new(), }], model: Some("mock-model-3".to_string()), + disabled_plugin_ids: Some(vec!["slack@openai".to_string()]), ..Default::default() }) .await?; @@ -426,6 +614,10 @@ async fn turn_start_settings_override_emits_thread_settings_updated() -> Result< let updated = read_thread_settings_updated(&mut mcp).await?; assert_eq!(updated.thread_id, thread.id); assert_eq!(updated.thread_settings.model, "mock-model-3"); + assert_eq!( + updated.thread_settings.disabled_plugin_ids, + vec!["slack@openai"] + ); timeout( DEFAULT_TIMEOUT, diff --git a/codex-rs/app-server/tests/suite/v2/turn_start.rs b/codex-rs/app-server/tests/suite/v2/turn_start.rs index 0c3490bc8a..c622f1a91f 100644 --- a/codex-rs/app-server/tests/suite/v2/turn_start.rs +++ b/codex-rs/app-server/tests/suite/v2/turn_start.rs @@ -3232,6 +3232,7 @@ async fn turn_start_explicit_local_environment_updates_legacy_cwd_between_turns( .request(|request_id| ClientRequest::TurnStart { request_id, params: TurnStartParams { + disabled_plugin_ids: None, environments: None, thread_id: thread.id.clone(), client_user_message_id: None, @@ -3280,6 +3281,7 @@ async fn turn_start_explicit_local_environment_updates_legacy_cwd_between_turns( .request(|request_id| ClientRequest::TurnStart { request_id, params: TurnStartParams { + disabled_plugin_ids: None, environments: Some(vec![TurnEnvironmentParams { environment_id: LOCAL_ENVIRONMENT_ID.to_string(), cwd: second_cwd.abs().into(), diff --git a/codex-rs/core/src/codex_thread.rs b/codex-rs/core/src/codex_thread.rs index 9fa43519f2..50ad565c3c 100644 --- a/codex-rs/core/src/codex_thread.rs +++ b/codex-rs/core/src/codex_thread.rs @@ -103,6 +103,7 @@ pub struct ThreadConfigSnapshot { pub parent_thread_id: Option, pub thread_source: Option, pub originator: String, + pub disabled_plugin_ids: Vec, } impl ThreadConfigSnapshot { diff --git a/codex-rs/core/src/environment_selection.rs b/codex-rs/core/src/environment_selection.rs index 59375c92a8..3af81b3286 100644 --- a/codex-rs/core/src/environment_selection.rs +++ b/codex-rs/core/src/environment_selection.rs @@ -1456,6 +1456,10 @@ url = "ws://127.0.0.1:8765" .with_span_events(FmtSpan::NEW) .with_writer(MockWriter::new(buffer)) .finish(); + // Avoid tracing-core's single-dispatch path caching no interest when another + // test first reaches these shared callsites without a subscriber. + let _interest_cache_guard = + tracing::Dispatch::new(tracing::subscriber::NoSubscriber::default()); let _subscriber_guard = tracing::subscriber::set_default(subscriber); let listener = TcpListener::bind("127.0.0.1:0") @@ -1487,6 +1491,13 @@ url = "ws://127.0.0.1:8765" )); environments .update_selections(std::slice::from_ref(&selection), &test_environment_config()); + // Exercise first-use callsite registration from a thread without a subscriber. + std::thread::spawn({ + let environments = Arc::clone(&environments); + move || assert!(environments.snapshot().now_or_never().is_none()) + }) + .join() + .expect("unsubscribed snapshot"); let snapshot_task = tokio::spawn({ let environments = Arc::clone(&environments); async move { environments.snapshot().await } diff --git a/codex-rs/core/src/session/session.rs b/codex-rs/core/src/session/session.rs index 7dd4fe066a..b3abc3d5c9 100644 --- a/codex-rs/core/src/session/session.rs +++ b/codex-rs/core/src/session/session.rs @@ -291,6 +291,7 @@ impl SessionConfiguration { parent_thread_id: self.parent_thread_id, thread_source: self.thread_source.clone(), originator: self.originator.clone(), + disabled_plugin_ids: self.disabled_plugin_ids.clone(), } } diff --git a/codex-rs/core/src/thread_manager.rs b/codex-rs/core/src/thread_manager.rs index fe9d373182..3c6bfccf88 100644 --- a/codex-rs/core/src/thread_manager.rs +++ b/codex-rs/core/src/thread_manager.rs @@ -1453,18 +1453,6 @@ impl ThreadManager { options.thread_instructions_provider.clone(), ) .await; - // Capture the source's settings before truncating its model history. - options.disabled_plugin_ids = Some(options.disabled_plugin_ids.unwrap_or_else(|| { - source_thread_id - .and_then(|thread_id| { - codex_history::latest_disabled_plugin_ids( - history.get_rollout_items(), - thread_id, - ) - }) - .map(<[String]>::to_vec) - .unwrap_or_default() - })); let multi_agent_version = self .state .effective_multi_agent_version_for_spawn( diff --git a/codex-rs/core/tests/suite/fork_thread.rs b/codex-rs/core/tests/suite/fork_thread.rs index 51471b44e2..a51aa68d07 100644 --- a/codex-rs/core/tests/suite/fork_thread.rs +++ b/codex-rs/core/tests/suite/fork_thread.rs @@ -13,12 +13,14 @@ use codex_protocol::mcp::ClientMcpExtensions; use codex_protocol::protocol::EventMsg; use codex_protocol::protocol::ThreadHistoryMode; use codex_protocol::protocol::ThreadSettingsAppliedEvent; +use codex_protocol::protocol::ThreadSettingsOverrides; use codex_protocol::protocol::ThreadSettingsSnapshot; use codex_protocol::user_input::UserInput; use core_test_support::responses::ev_completed; use core_test_support::responses::ev_response_created; use core_test_support::responses::sse; use core_test_support::skip_if_no_network; +use core_test_support::submit_thread_settings; use core_test_support::test_codex::test_codex; use core_test_support::wait_for_event; use wiremock::Mock; @@ -166,6 +168,79 @@ fn thread_settings_applied_item( )) } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn fork_thread_restores_history_selection_and_preserves_explicit_clear() -> anyhow::Result<()> +{ + skip_if_no_network!(Ok(())); + + let server = MockServer::start().await; + let mut builder = test_codex(); + let test = builder.build_with_auto_env(&server).await?; + let selected = vec!["slack@openai".to_string()]; + submit_thread_settings( + &test.codex, + ThreadSettingsOverrides { + disabled_plugin_ids: Some(selected.clone()), + ..Default::default() + }, + ) + .await?; + let history = InitialHistory::Resumed(ResumedHistory { + conversation_id: test.session_configured.thread_id, + history: Arc::new(vec![thread_settings_applied_item( + test.session_configured.thread_id, + test.codex.thread_settings_snapshot().await, + )]), + rollout_path: None, + }); + submit_thread_settings( + &test.codex, + ThreadSettingsOverrides { + disabled_plugin_ids: Some(Vec::new()), + ..Default::default() + }, + ) + .await?; + + let inherited = test + .thread_manager + .fork_thread_from_history( + ForkSnapshot::Interrupted, + codex_core::StartThreadOptions::new(test.config.clone()), + history.clone(), + ) + .await?; + pretty_assertions::assert_eq!( + inherited + .thread + .thread_settings_snapshot() + .await + .disabled_plugin_ids, + selected + ); + + let explicit = test + .thread_manager + .fork_thread_from_history( + ForkSnapshot::Interrupted, + codex_core::StartThreadOptions { + disabled_plugin_ids: Some(Vec::new()), + ..codex_core::StartThreadOptions::new(test.config.clone()) + }, + history, + ) + .await?; + pretty_assertions::assert_eq!( + explicit + .thread + .thread_settings_snapshot() + .await + .disabled_plugin_ids, + Vec::::new() + ); + Ok(()) +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn fork_thread_from_history_does_not_require_source_rollout_path() { assert_copied_fork_persists_inherited_history(ThreadHistoryMode::Legacy).await; diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index c7c54fe833..b56a56f1eb 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -1139,6 +1139,7 @@ async fn run_exec_session(args: ExecRunArgs) -> anyhow::Result<()> { ClientRequest::TurnStart { request_id: request_ids.next(), params: TurnStartParams { + disabled_plugin_ids: None, thread_id: primary_thread_id_for_span.clone(), turn_trigger: None, client_user_message_id: None, diff --git a/codex-rs/exec/src/lib_tests.rs b/codex-rs/exec/src/lib_tests.rs index fb5b5891a3..fb79e7b839 100644 --- a/codex-rs/exec/src/lib_tests.rs +++ b/codex-rs/exec/src/lib_tests.rs @@ -843,6 +843,7 @@ async fn session_configured_from_thread_response_preserves_parent_thread_id() { fn sample_thread_start_response() -> ThreadStartResponse { ThreadStartResponse { + disabled_plugin_ids: Vec::new(), thread: codex_app_server_protocol::Thread { originator: None, environments: None, diff --git a/codex-rs/tui/src/app/app_server_event_targets.rs b/codex-rs/tui/src/app/app_server_event_targets.rs index 0054f79248..4753a2afde 100644 --- a/codex-rs/tui/src/app/app_server_event_targets.rs +++ b/codex-rs/tui/src/app/app_server_event_targets.rs @@ -248,6 +248,7 @@ mod tests { fn test_thread_settings() -> ThreadSettings { ThreadSettings { + disabled_plugin_ids: Vec::new(), cwd: test_path_buf("/tmp/thread-settings").abs(), approval_policy: codex_app_server_protocol::AskForApproval::Never, approvals_reviewer: codex_app_server_protocol::ApprovalsReviewer::User, diff --git a/codex-rs/tui/src/app/tests.rs b/codex-rs/tui/src/app/tests.rs index b5614c497f..59bdbc5493 100644 --- a/codex-rs/tui/src/app/tests.rs +++ b/codex-rs/tui/src/app/tests.rs @@ -9236,6 +9236,7 @@ async fn inactive_thread_settings_notification_updates_cached_collaboration_mode let notification = ThreadSettingsUpdatedNotification { thread_id: inactive_thread_id.to_string(), thread_settings: ThreadSettings { + disabled_plugin_ids: Vec::new(), cwd: test_absolute_path("/tmp/thread-settings"), approval_policy: AskForApproval::OnRequest, approvals_reviewer: codex_app_server_protocol::ApprovalsReviewer::AutoReview, diff --git a/codex-rs/tui/src/app_server_session.rs b/codex-rs/tui/src/app_server_session.rs index cd0d3ea36b..0e86ee157a 100644 --- a/codex-rs/tui/src/app_server_session.rs +++ b/codex-rs/tui/src/app_server_session.rs @@ -1336,6 +1336,7 @@ impl AppServerSession { .request_typed(ClientRequest::TurnStart { request_id, params: TurnStartParams { + disabled_plugin_ids: None, thread_id: thread_id.to_string(), turn_trigger: None, client_user_message_id: Some(client_user_message_id), @@ -3877,6 +3878,7 @@ mod tests { let forked_from_id = ThreadId::new(); let read_only_profile = PermissionProfile::read_only(); let response = ThreadResumeResponse { + disabled_plugin_ids: Vec::new(), thread: codex_app_server_protocol::Thread { originator: None, environments: None, diff --git a/codex-rs/tui/src/chatwidget/tests/app_server.rs b/codex-rs/tui/src/chatwidget/tests/app_server.rs index c6ad08d768..5e0046c311 100644 --- a/codex-rs/tui/src/chatwidget/tests/app_server.rs +++ b/codex-rs/tui/src/chatwidget/tests/app_server.rs @@ -13,6 +13,7 @@ fn thread_settings_for_test( codex_app_server_protocol::ThreadSettingsUpdatedNotification { thread_id: thread_id.to_string(), thread_settings: codex_app_server_protocol::ThreadSettings { + disabled_plugin_ids: Vec::new(), cwd: test_path_buf("/tmp/thread-settings").abs(), approval_policy: AskForApproval::OnRequest, approvals_reviewer: codex_app_server_protocol::ApprovalsReviewer::AutoReview, diff --git a/sdk/python/src/openai_codex/generated/v2_all.py b/sdk/python/src/openai_codex/generated/v2_all.py index cc90634fd2..f5e248e124 100644 --- a/sdk/python/src/openai_codex/generated/v2_all.py +++ b/sdk/python/src/openai_codex/generated/v2_all.py @@ -9899,6 +9899,13 @@ class ThreadSettings(BaseModel): approvals_reviewer: Annotated[ApprovalsReviewer, Field(alias="approvalsReviewer")] collaboration_mode: Annotated[CollaborationMode, Field(alias="collaborationMode")] cwd: AbsolutePathBuf + disabled_plugin_ids: Annotated[ + list[str] | None, + Field( + alias="disabledPluginIds", + description="Saved list of disabled plugin IDs. Does not yet filter plugin capabilities.", + ), + ] = [] effort: ReasoningEffort | None = None model: str model_provider: Annotated[str, Field(alias="modelProvider")] @@ -11785,6 +11792,13 @@ class ThreadForkResponse(BaseModel): ), ] cwd: AbsolutePathBuf + disabled_plugin_ids: Annotated[ + list[str] | None, + Field( + alias="disabledPluginIds", + description="Saved list of disabled plugin IDs. Does not yet filter plugin capabilities.", + ), + ] = [] instruction_sources: Annotated[ list[LegacyAppPathString] | None, Field( @@ -11853,6 +11867,13 @@ class ThreadResumeResponse(BaseModel): ), ] cwd: AbsolutePathBuf + disabled_plugin_ids: Annotated[ + list[str] | None, + Field( + alias="disabledPluginIds", + description="Saved list of disabled plugin IDs. Does not yet filter plugin capabilities.", + ), + ] = [] instruction_sources: Annotated[ list[LegacyAppPathString] | None, Field( @@ -11946,6 +11967,13 @@ class ThreadStartResponse(BaseModel): ), ] cwd: AbsolutePathBuf + disabled_plugin_ids: Annotated[ + list[str] | None, + Field( + alias="disabledPluginIds", + description="Saved list of disabled plugin IDs. Does not yet filter plugin capabilities.", + ), + ] = [] instruction_sources: Annotated[ list[LegacyAppPathString] | None, Field( @@ -12024,6 +12052,13 @@ class TurnStartParams(BaseModel): str | None, Field(description="Override the working directory for this turn and subsequent turns."), ] = None + disabled_plugin_ids: Annotated[ + list[str] | None, + Field( + alias="disabledPluginIds", + description="Replace this thread's disabled plugin IDs. Omitted/null preserves the list; [] clears it.", + ), + ] = None effort: Annotated[ ReasoningEffort | None, Field(description="Override the reasoning effort for this turn and subsequent turns."),