@@ -1246,6 +1246,20 @@ pub struct SessionConfig {
12461246 ///
12471247 /// Defaults to `None` (treated as `false`).
12481248 pub enable_mcp_apps : Option < bool > ,
1249+ /// Disable, force-enable, or inherit the experimental feature-flag tier
1250+ /// for this session only.
1251+ ///
1252+ /// - `Some(false)` — the runtime re-resolves this session's feature flags
1253+ /// as if experimental mode were off, stripping experimental-tier flags.
1254+ /// - `Some(true)` — force-enables the experimental tier even if the CLI
1255+ /// process didn't start with it.
1256+ /// - `None` (default) — inherits the CLI process's flags unchanged.
1257+ ///
1258+ /// This never persists anything to the user's shared config; it only
1259+ /// affects the feature-flag resolution for this one session. Serializes
1260+ /// as `isExperimentalMode` and is omitted from the wire when `None`, so
1261+ /// older CLIs that don't understand it are unaffected.
1262+ pub is_experimental_mode : Option < bool > ,
12491263 /// Skill directory paths passed through to the GitHub Copilot CLI.
12501264 pub skill_directories : Option < Vec < PathBuf > > ,
12511265 /// Additional directories to search for custom instruction files.
@@ -1418,6 +1432,7 @@ impl std::fmt::Debug for SessionConfig {
14181432 . field ( "enable_session_store" , & self . enable_session_store )
14191433 . field ( "enable_skills" , & self . enable_skills )
14201434 . field ( "enable_mcp_apps" , & self . enable_mcp_apps )
1435+ . field ( "is_experimental_mode" , & self . is_experimental_mode )
14211436 . field ( "skill_directories" , & self . skill_directories )
14221437 . field ( "instruction_directories" , & self . instruction_directories )
14231438 . field ( "plugin_directories" , & self . plugin_directories )
@@ -1517,6 +1532,7 @@ impl Default for SessionConfig {
15171532 enable_skills : None ,
15181533 embedding_cache_storage : None ,
15191534 enable_mcp_apps : None ,
1535+ is_experimental_mode : None ,
15201536 skill_directories : None ,
15211537 instruction_directories : None ,
15221538 plugin_directories : None ,
@@ -1659,6 +1675,7 @@ impl SessionConfig {
16591675 request_auto_mode_switch,
16601676 request_elicitation,
16611677 request_mcp_apps : self . enable_mcp_apps . unwrap_or ( false ) ,
1678+ is_experimental_mode : self . is_experimental_mode ,
16621679 hooks : hooks_flag,
16631680 skill_directories : self . skill_directories ,
16641681 instruction_directories : self . instruction_directories ,
@@ -2003,6 +2020,14 @@ impl SessionConfig {
20032020 self
20042021 }
20052022
2023+ /// Disable (`false`) or force-enable (`true`) the experimental feature-flag
2024+ /// tier for this session only. `None` (default) inherits the CLI process's
2025+ /// flags. Never persists to config. See the field docs for resume caveats.
2026+ pub fn with_is_experimental_mode ( mut self , is_experimental_mode : bool ) -> Self {
2027+ self . is_experimental_mode = Some ( is_experimental_mode) ;
2028+ self
2029+ }
2030+
20062031 /// Set skill directory paths passed through to the CLI.
20072032 pub fn with_skill_directories < I , P > ( mut self , paths : I ) -> Self
20082033 where
@@ -2252,6 +2277,20 @@ pub struct ResumeSessionConfig {
22522277 /// Enable MCP Apps (SEP-1865) UI passthrough on resume. See
22532278 /// [`SessionConfig::enable_mcp_apps`]. Defaults to `None` (treated as `false`).
22542279 pub enable_mcp_apps : Option < bool > ,
2280+ /// Disable, force-enable, or inherit the experimental feature-flag tier
2281+ /// for this resumed session only.
2282+ ///
2283+ /// - `Some(false)` — re-resolves this session's feature flags as if
2284+ /// experimental mode were off, stripping experimental-tier flags.
2285+ /// - `Some(true)` — force-enables the experimental tier even if the CLI
2286+ /// process didn't start with it.
2287+ /// - `None` (default) — inherits the CLI process's flags unchanged.
2288+ ///
2289+ /// Never persists to config. Note: resume only re-resolves flags on the
2290+ /// cold-load path (the session is not already live in-process); an
2291+ /// already-active session keeps the flags it was created with. Serializes
2292+ /// as `isExperimentalMode` and is omitted from the wire when `None`.
2293+ pub is_experimental_mode : Option < bool > ,
22552294 /// Skill directory paths passed through to the GitHub Copilot CLI on resume.
22562295 pub skill_directories : Option < Vec < PathBuf > > ,
22572296 /// Additional directories to search for custom instruction files on
@@ -2395,6 +2434,7 @@ impl std::fmt::Debug for ResumeSessionConfig {
23952434 . field ( "enable_session_store" , & self . enable_session_store )
23962435 . field ( "enable_skills" , & self . enable_skills )
23972436 . field ( "enable_mcp_apps" , & self . enable_mcp_apps )
2437+ . field ( "is_experimental_mode" , & self . is_experimental_mode )
23982438 . field ( "skill_directories" , & self . skill_directories )
23992439 . field ( "instruction_directories" , & self . instruction_directories )
24002440 . field ( "plugin_directories" , & self . plugin_directories )
@@ -2538,6 +2578,7 @@ impl ResumeSessionConfig {
25382578 request_auto_mode_switch,
25392579 request_elicitation,
25402580 request_mcp_apps : self . enable_mcp_apps . unwrap_or ( false ) ,
2581+ is_experimental_mode : self . is_experimental_mode ,
25412582 hooks : hooks_flag,
25422583 skill_directories : self . skill_directories ,
25432584 instruction_directories : self . instruction_directories ,
@@ -2614,6 +2655,7 @@ impl ResumeSessionConfig {
26142655 enable_skills : None ,
26152656 embedding_cache_storage : None ,
26162657 enable_mcp_apps : None ,
2658+ is_experimental_mode : None ,
26172659 skill_directories : None ,
26182660 instruction_directories : None ,
26192661 plugin_directories : None ,
@@ -2933,6 +2975,14 @@ impl ResumeSessionConfig {
29332975 self
29342976 }
29352977
2978+ /// Disable (`false`) or force-enable (`true`) the experimental feature-flag
2979+ /// tier for this session only. `None` (default) inherits the CLI process's
2980+ /// flags. Never persists to config. See the field docs for resume caveats.
2981+ pub fn with_is_experimental_mode ( mut self , is_experimental_mode : bool ) -> Self {
2982+ self . is_experimental_mode = Some ( is_experimental_mode) ;
2983+ self
2984+ }
2985+
29362986 /// Set skill directory paths passed through to the CLI on resume.
29372987 pub fn with_skill_directories < I , P > ( mut self , paths : I ) -> Self
29382988 where
@@ -4453,6 +4503,63 @@ mod tests {
44534503 assert_eq ! ( json[ "requestMcpApps" ] , serde_json:: Value :: Bool ( true ) ) ;
44544504 }
44554505
4506+ #[ test]
4507+ fn session_config_is_experimental_mode_serializes_when_set ( ) {
4508+ let cfg = SessionConfig :: default ( ) . with_is_experimental_mode ( false ) ;
4509+ assert_eq ! ( cfg. is_experimental_mode, Some ( false ) ) ;
4510+
4511+ let ( wire, _runtime) = cfg
4512+ . into_wire ( Some ( SessionId :: from ( "experimental-mode" ) ) )
4513+ . expect ( "is_experimental_mode config has no duplicate handlers" ) ;
4514+ assert_eq ! ( wire. is_experimental_mode, Some ( false ) ) ;
4515+
4516+ let json = serde_json:: to_value ( & wire) . unwrap ( ) ;
4517+ assert_eq ! ( json[ "isExperimentalMode" ] , serde_json:: Value :: Bool ( false ) ) ;
4518+ }
4519+
4520+ #[ test]
4521+ fn session_config_is_experimental_mode_omitted_when_none ( ) {
4522+ let cfg = SessionConfig :: default ( ) ;
4523+ assert_eq ! ( cfg. is_experimental_mode, None ) ;
4524+
4525+ let ( wire, _runtime) = cfg
4526+ . into_wire ( Some ( SessionId :: from ( "no-experimental-mode" ) ) )
4527+ . expect ( "default config has no duplicate handlers" ) ;
4528+ assert_eq ! ( wire. is_experimental_mode, None ) ;
4529+
4530+ let json = serde_json:: to_value ( & wire) . unwrap ( ) ;
4531+ assert ! ( json. get( "isExperimentalMode" ) . is_none( ) ) ;
4532+ }
4533+
4534+ #[ test]
4535+ fn resume_session_config_is_experimental_mode_serializes_when_set ( ) {
4536+ let cfg = ResumeSessionConfig :: new ( SessionId :: from ( "resume-experimental-mode" ) )
4537+ . with_is_experimental_mode ( false ) ;
4538+ assert_eq ! ( cfg. is_experimental_mode, Some ( false ) ) ;
4539+
4540+ let ( wire, _runtime) = cfg
4541+ . into_wire ( )
4542+ . expect ( "resume is_experimental_mode config has no duplicate handlers" ) ;
4543+ assert_eq ! ( wire. is_experimental_mode, Some ( false ) ) ;
4544+
4545+ let json = serde_json:: to_value ( & wire) . unwrap ( ) ;
4546+ assert_eq ! ( json[ "isExperimentalMode" ] , serde_json:: Value :: Bool ( false ) ) ;
4547+ }
4548+
4549+ #[ test]
4550+ fn resume_session_config_is_experimental_mode_omitted_when_none ( ) {
4551+ let cfg = ResumeSessionConfig :: new ( SessionId :: from ( "resume-no-experimental-mode" ) ) ;
4552+ assert_eq ! ( cfg. is_experimental_mode, None ) ;
4553+
4554+ let ( wire, _runtime) = cfg
4555+ . into_wire ( )
4556+ . expect ( "default resume config has no duplicate handlers" ) ;
4557+ assert_eq ! ( wire. is_experimental_mode, None ) ;
4558+
4559+ let json = serde_json:: to_value ( & wire) . unwrap ( ) ;
4560+ assert ! ( json. get( "isExperimentalMode" ) . is_none( ) ) ;
4561+ }
4562+
44564563 #[ test]
44574564 #[ allow( clippy:: field_reassign_with_default) ]
44584565 fn session_config_into_wire_serializes_bucket_b_fields ( ) {
0 commit comments