@@ -1219,8 +1219,12 @@ pub(super) async fn handle_get_sandbox_config(
12191219
12201220 let settings = merge_effective_settings ( & global_settings, & sandbox_settings) ?;
12211221 let config_revision = compute_config_revision ( policy. as_ref ( ) , & settings, policy_source) ;
1222- let provider_env_revision =
1223- compute_provider_env_revision ( state. store . as_ref ( ) , & sandbox_provider_names) . await ?;
1222+ let provider_env_revision = compute_provider_env_revision (
1223+ state. store . as_ref ( ) ,
1224+ & sandbox_provider_names,
1225+ providers_v2_enabled,
1226+ )
1227+ . await ?;
12241228
12251229 Ok ( Response :: new ( GetSandboxConfigResponse {
12261230 policy,
@@ -1237,9 +1241,11 @@ pub(super) async fn handle_get_sandbox_config(
12371241pub ( super ) async fn compute_provider_env_revision (
12381242 store : & Store ,
12391243 provider_names : & [ String ] ,
1244+ providers_v2_enabled : bool ,
12401245) -> Result < u64 , Status > {
12411246 let mut hasher = Sha256 :: new ( ) ;
12421247 hasher. update ( b"openshell-provider-env-revision-v1" ) ;
1248+ hasher. update ( [ u8:: from ( providers_v2_enabled) ] ) ;
12431249
12441250 for provider_name in provider_names {
12451251 hasher. update ( provider_name. as_bytes ( ) ) ;
@@ -1366,6 +1372,16 @@ async fn profile_provider_policy_layers(
13661372 Ok ( layers)
13671373}
13681374
1375+ pub async fn is_providers_v2_enabled ( store : & Store ) -> bool {
1376+ load_global_settings ( store)
1377+ . await
1378+ . and_then ( |s| bool_setting_enabled ( & s, settings:: PROVIDERS_V2_ENABLED_KEY ) )
1379+ . unwrap_or_else ( |e| {
1380+ warn ! ( "failed to read providers_v2_enabled setting, defaulting to false: {e}" ) ;
1381+ false
1382+ } )
1383+ }
1384+
13691385fn bool_setting_enabled ( settings : & StoredSettings , key : & str ) -> Result < bool , Status > {
13701386 match settings. settings . get ( key) {
13711387 None => Ok ( false ) ,
@@ -1407,13 +1423,20 @@ pub(super) async fn handle_get_sandbox_provider_environment(
14071423 . spec
14081424 . ok_or_else ( || Status :: internal ( "sandbox has no spec" ) ) ?;
14091425
1426+ let providers_v2_enabled = is_providers_v2_enabled ( state. store . as_ref ( ) ) . await ;
1427+
14101428 let provider_names = spec. providers ;
14111429 let provider_env_revision =
1412- compute_provider_env_revision ( state. store . as_ref ( ) , & provider_names) . await ?;
1413- let provider_environment =
1414- super :: provider:: resolve_provider_environment ( state. store . as_ref ( ) , & provider_names)
1430+ compute_provider_env_revision ( state. store . as_ref ( ) , & provider_names, providers_v2_enabled)
14151431 . await ?;
14161432
1433+ let provider_environment = super :: provider:: resolve_provider_environment (
1434+ state. store . as_ref ( ) ,
1435+ & provider_names,
1436+ providers_v2_enabled,
1437+ )
1438+ . await ?;
1439+
14171440 info ! (
14181441 sandbox_id = %sandbox_id,
14191442 provider_count = provider_names. len( ) ,
@@ -5001,10 +5024,13 @@ mod tests {
50015024 . await
50025025 . unwrap ( ) ;
50035026
5004- let first =
5005- compute_provider_env_revision ( state. store . as_ref ( ) , & [ "work-custom-token" . to_string ( ) ] )
5006- . await
5007- . unwrap ( ) ;
5027+ let first = compute_provider_env_revision (
5028+ state. store . as_ref ( ) ,
5029+ & [ "work-custom-token" . to_string ( ) ] ,
5030+ false ,
5031+ )
5032+ . await
5033+ . unwrap ( ) ;
50085034
50095035 tokio:: time:: sleep ( Duration :: from_millis ( 2 ) ) . await ;
50105036 state
@@ -5015,17 +5041,43 @@ mod tests {
50155041 . await
50165042 . unwrap ( ) ;
50175043
5018- let second =
5019- compute_provider_env_revision ( state. store . as_ref ( ) , & [ "work-custom-token" . to_string ( ) ] )
5020- . await
5021- . unwrap ( ) ;
5044+ let second = compute_provider_env_revision (
5045+ state. store . as_ref ( ) ,
5046+ & [ "work-custom-token" . to_string ( ) ] ,
5047+ false ,
5048+ )
5049+ . await
5050+ . unwrap ( ) ;
50225051
50235052 assert_ne ! (
50245053 first, second,
50255054 "custom provider profile updates must trigger sandbox dynamic credential refresh"
50265055 ) ;
50275056 }
50285057
5058+ #[ tokio:: test]
5059+ async fn provider_env_revision_changes_when_providers_v2_enabled_toggles ( ) {
5060+ let state = test_server_state ( ) . await ;
5061+ let store = state. store . as_ref ( ) ;
5062+ let provider = test_provider ( "work-github" , "github" ) ;
5063+ store. put_message ( & provider) . await . unwrap ( ) ;
5064+
5065+ let revision_v2_off =
5066+ compute_provider_env_revision ( store, & [ "work-github" . to_string ( ) ] , false )
5067+ . await
5068+ . unwrap ( ) ;
5069+
5070+ let revision_v2_on =
5071+ compute_provider_env_revision ( store, & [ "work-github" . to_string ( ) ] , true )
5072+ . await
5073+ . unwrap ( ) ;
5074+
5075+ assert_ne ! (
5076+ revision_v2_off, revision_v2_on,
5077+ "toggling providers_v2_enabled must change provider_env_revision so running sandboxes refresh"
5078+ ) ;
5079+ }
5080+
50295081 #[ tokio:: test]
50305082 async fn sandbox_config_and_provider_env_follow_attached_provider_lifecycle ( ) {
50315083 use crate :: grpc:: sandbox:: {
0 commit comments