@@ -28,11 +28,11 @@ use warpui::{
2828
2929use settings:: Setting ;
3030use warp_cli:: agent:: Harness ;
31- use warp_core:: channel:: { Channel , ChannelState } ;
3231use warp_core:: features:: FeatureFlag ;
3332use warp_core:: ui:: theme:: Fill ;
3433
3534use crate :: ai:: auth_secret_types:: auth_secret_types_for_harness;
35+ use crate :: ai:: blocklist:: inline_action:: host_picker:: HostPicker ;
3636use crate :: ai:: cloud_agent_settings:: CloudAgentSettings ;
3737use crate :: ai:: cloud_environments:: CloudAmbientAgentEnvironment ;
3838use crate :: ai:: execution_profiles:: model_menu_items:: available_model_menu_items;
@@ -47,8 +47,13 @@ use crate::report_if_error;
4747use crate :: ui_components:: blended_colors;
4848use crate :: view_components:: dropdown:: { Dropdown , DropdownAction , DropdownStyle } ;
4949use crate :: view_components:: FilterableDropdown ;
50+ use crate :: workspaces:: user_workspaces:: UserWorkspaces ;
5051use crate :: LLMPreferences ;
5152
53+ /// Env var override for the workspace default host (developer testing).
54+ /// Mirrors the single-agent ambient flow.
55+ const DEFAULT_HOST_ENV_VAR : & str = "WARP_CLOUD_MODE_DEFAULT_HOST" ;
56+
5257// ── Shared constants ────────────────────────────────────────────────
5358
5459pub const ORCHESTRATION_WARP_WORKER_HOST : & str = "warp" ;
@@ -78,7 +83,6 @@ pub trait OrchestrationControlAction: Clone + Debug + Send + Sync + 'static {
7883 fn model_changed ( model_id : String ) -> Self ;
7984 fn harness_changed ( harness_type : String ) -> Self ;
8085 fn environment_changed ( environment_id : String ) -> Self ;
81- fn worker_host_changed ( worker_host : String ) -> Self ;
8286 /// Fires when the auth secret picker selects a managed secret.
8387 /// `None` means "clear the selection / inherit from environment".
8488 fn auth_secret_changed ( name : Option < String > ) -> Self ;
@@ -291,7 +295,7 @@ pub struct OrchestrationPickerHandles<A: OrchestrationControlAction> {
291295 pub model_picker : Option < ViewHandle < Dropdown < A > > > ,
292296 pub harness_picker : Option < ViewHandle < Dropdown < A > > > ,
293297 pub environment_picker : Option < ViewHandle < FilterableDropdown < A > > > ,
294- pub host_picker : Option < ViewHandle < Dropdown < A > > > ,
298+ pub host_picker : Option < ViewHandle < HostPicker > > ,
295299 /// Picker for the managed auth secret used by non-Oz cloud children.
296300 /// `None` when the picker hasn't been built yet (e.g. harness is Oz or
297301 /// execution mode is Local), or when the harness has no supported
@@ -688,37 +692,72 @@ pub fn create_environment_picker<A: OrchestrationControlAction, V: View>(
688692 dropdown_handle
689693}
690694
691- pub fn populate_host_picker < A : OrchestrationControlAction , V : View > (
692- dropdown : & ViewHandle < Dropdown < A > > ,
695+ /// Repopulates the host picker with the workspace default (if any) and
696+ /// the user's last-selected custom host (if any), then sets the current
697+ /// selection to `initial_host`.
698+ pub fn populate_host_picker < V : View > (
699+ picker : & ViewHandle < HostPicker > ,
693700 initial_host : & str ,
694701 ctx : & mut ViewContext < V > ,
695702) {
696- let initial_host = if initial_host. is_empty ( ) {
703+ let default_host = resolve_default_host_slug ( ctx) ;
704+ let recent_host = resolve_recent_host_slug ( ctx) ;
705+ let initial = if initial_host. trim ( ) . is_empty ( ) {
697706 ORCHESTRATION_WARP_WORKER_HOST . to_string ( )
698707 } else {
699708 initial_host. to_string ( )
700709 } ;
701- dropdown. update ( ctx, |dropdown, ctx_dropdown| {
702- let hosts: & [ & str ] = if matches ! ( ChannelState :: channel( ) , Channel :: Local ) {
703- & [ "warp" , "local-dev" ]
704- } else {
705- & [ "warp" ]
706- } ;
707- let mut items: Vec < MenuItem < DropdownAction < A > > > = Vec :: new ( ) ;
708- let mut selected_idx = None ;
709- for ( idx, & host) in hosts. iter ( ) . enumerate ( ) {
710- let fields = MenuItemFields :: new ( host) . with_on_select_action (
711- DropdownAction :: SelectActionAndClose ( A :: worker_host_changed ( host. to_string ( ) ) ) ,
712- ) ;
713- if host. eq_ignore_ascii_case ( & initial_host) {
714- selected_idx = Some ( idx) ;
715- }
716- items. push ( MenuItem :: Item ( fields) ) ;
717- }
718- dropdown. set_rich_items ( items, ctx_dropdown) ;
719- if let Some ( idx) = selected_idx {
720- dropdown. set_selected_by_index ( idx, ctx_dropdown) ;
710+ picker. update ( ctx, |picker, picker_ctx| {
711+ picker. set_options ( default_host, recent_host, picker_ctx) ;
712+ picker. set_selected ( & initial, picker_ctx) ;
713+ } ) ;
714+ }
715+
716+ /// Resolves the workspace-configured default host slug, honoring the
717+ /// `WARP_CLOUD_MODE_DEFAULT_HOST` env var override for developer
718+ /// testing. Mirrors the single-agent ambient flow.
719+ pub fn resolve_default_host_slug ( ctx : & AppContext ) -> Option < String > {
720+ if let Ok ( slug) = std:: env:: var ( DEFAULT_HOST_ENV_VAR ) {
721+ let trimmed = slug. trim ( ) ;
722+ if !trimmed. is_empty ( ) {
723+ return Some ( trimmed. to_string ( ) ) ;
721724 }
725+ }
726+ UserWorkspaces :: as_ref ( ctx)
727+ . default_host_slug ( )
728+ . map ( str:: to_string)
729+ . filter ( |s| !s. trim ( ) . is_empty ( ) )
730+ }
731+
732+ /// Returns the user's last-selected custom host slug from
733+ /// `CloudAgentSettings.last_selected_host`, excluding `"warp"` and the
734+ /// workspace default (those are surfaced as separate menu rows).
735+ pub fn resolve_recent_host_slug ( ctx : & AppContext ) -> Option < String > {
736+ let last = CloudAgentSettings :: as_ref ( ctx)
737+ . last_selected_host
738+ . value ( )
739+ . clone ( )
740+ . filter ( |s| !s. trim ( ) . is_empty ( ) ) ?;
741+ if last. eq_ignore_ascii_case ( ORCHESTRATION_WARP_WORKER_HOST ) {
742+ return None ;
743+ }
744+ if resolve_default_host_slug ( ctx) . as_deref ( ) == Some ( last. as_str ( ) ) {
745+ return None ;
746+ }
747+ Some ( last)
748+ }
749+
750+ /// Persists the user's most-recent host selection to
751+ /// `CloudAgentSettings.last_selected_host`. Skipped for `"warp"` and
752+ /// empty values (those don't represent a custom slug worth remembering).
753+ pub fn persist_host_selection < V : View > ( worker_host : & str , ctx : & mut ViewContext < V > ) {
754+ let trimmed = worker_host. trim ( ) ;
755+ if trimmed. is_empty ( ) || trimmed. eq_ignore_ascii_case ( ORCHESTRATION_WARP_WORKER_HOST ) {
756+ return ;
757+ }
758+ let value = trimmed. to_string ( ) ;
759+ CloudAgentSettings :: handle ( ctx) . update ( ctx, |settings, ctx| {
760+ report_if_error ! ( settings. last_selected_host. set_value( Some ( value) , ctx) ) ;
722761 } ) ;
723762}
724763
@@ -1188,8 +1227,8 @@ pub fn sync_picker_selections<A: OrchestrationControlAction, V: View>(
11881227 RunAgentsExecutionMode :: Remote { worker_host, .. } => worker_host. clone ( ) ,
11891228 RunAgentsExecutionMode :: Local => ORCHESTRATION_WARP_WORKER_HOST . to_string ( ) ,
11901229 } ;
1191- host_picker. update ( ctx, |dropdown , ctx_dropdown | {
1192- dropdown . set_selected_by_name ( & worker_host, ctx_dropdown ) ;
1230+ host_picker. update ( ctx, |picker , picker_ctx | {
1231+ picker . set_selected ( & worker_host, picker_ctx ) ;
11931232 } ) ;
11941233 }
11951234 if let Some ( auth_secret_picker) = handles. auth_secret_picker . clone ( ) {
0 commit comments