Expose disabled plugin settings in the app-server API (#44905)

## What changed

- Accept `disabledPluginIds` in `thread/settings/update` and `turn/start`. A supplied list replaces the saved selection; omission or `null` preserves it, and `[]` clears it.
- Return the selection in thread start, resume, and fork responses and `thread/settings/updated` notifications. Update generated schemas and client types.
- Restore disabled plugin IDs from the history retained at the requested fork boundary, preserving explicit overrides.

The selection persists across resume but does not yet filter plugin capabilities.

## Testing

Add coverage for replacing, preserving, and clearing selections without inference; turn-start notifications; resume; and fork boundaries across legacy and paginated history, with loaded and restarted parents. Add a core regression test for history restoration and explicit clearing.

GitOrigin-RevId: 654a8c2a0527228d422c0dd4919228447e2663db
This commit is contained in:
Matthew Zeng
2026-09-11 20:27:37 +00:00
committed by copyberry
parent 42cd1ec497
commit c62d191c4c
44 changed files with 549 additions and 19 deletions
@@ -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,
@@ -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![],
@@ -183,6 +183,9 @@ pub struct ThreadStartResponse {
pub model: String,
pub model_provider: String,
pub service_tier: Option<String>,
/// Saved list of disabled plugin IDs. Does not yet filter plugin capabilities.
#[serde(default)]
pub disabled_plugin_ids: Vec<String>,
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<Vec<String>>,
/// Override the working directory for subsequent turns.
#[ts(optional = nullable)]
pub cwd: Option<PathBuf>,
@@ -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<String>,
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<String>,
/// Saved list of disabled plugin IDs. Does not yet filter plugin capabilities.
#[serde(default)]
pub disabled_plugin_ids: Vec<String>,
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<String>,
/// Saved list of disabled plugin IDs. Does not yet filter plugin capabilities.
#[serde(default)]
pub disabled_plugin_ids: Vec<String>,
pub cwd: AbsolutePathBuf,
/// Thread-scoped runtime workspace roots used to materialize
/// `:workspace_roots`.
@@ -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<Vec<String>>,
#[ts(optional = nullable)]
pub client_user_message_id: Option<String>,
pub input: Vec<UserInput>,