onboarding: Remove accept AI ToS from within Zed (#36612)

Anthony Eid and Mikayla Maki created

Users now accept ToS from Zed's website when they sign in to Zed the
first time. So it's no longer possible that a signed in account could
not have accepted the ToS.


Release Notes:

- N/A

---------

Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>

Change summary

crates/agent_ui/src/active_thread.rs                        |   5 
crates/agent_ui/src/agent_configuration.rs                  |  10 
crates/agent_ui/src/agent_panel.rs                          |  15 
crates/agent_ui/src/message_editor.rs                       |   7 
crates/agent_ui/src/text_thread_editor.rs                   |  16 
crates/ai_onboarding/src/ai_onboarding.rs                   |  77 --
crates/client/src/user.rs                                   |  44 -
crates/cloud_api_client/src/cloud_api_client.rs             |  28 
crates/edit_prediction/src/edit_prediction.rs               |   8 
crates/edit_prediction_button/src/edit_prediction_button.rs |   8 
crates/editor/src/editor.rs                                 |  41 -
crates/language_model/src/language_model.rs                 |  12 
crates/language_model/src/registry.rs                       |  12 
crates/language_models/src/provider/cloud.rs                | 213 ------
crates/zed/src/zed/edit_prediction_registry.rs              |  25 
crates/zeta/src/zeta.rs                                     |  22 
16 files changed, 44 insertions(+), 499 deletions(-)

Detailed changes

crates/agent_ui/src/active_thread.rs πŸ”—

@@ -1595,11 +1595,6 @@ impl ActiveThread {
             return;
         };
 
-        if model.provider.must_accept_terms(cx) {
-            cx.notify();
-            return;
-        }
-
         let edited_text = state.editor.read(cx).text(cx);
 
         let creases = state.editor.update(cx, extract_message_creases);

crates/agent_ui/src/agent_configuration.rs πŸ”—

@@ -93,14 +93,6 @@ impl AgentConfiguration {
         let scroll_handle = ScrollHandle::new();
         let scrollbar_state = ScrollbarState::new(scroll_handle.clone());
 
-        let mut expanded_provider_configurations = HashMap::default();
-        if LanguageModelRegistry::read_global(cx)
-            .provider(&ZED_CLOUD_PROVIDER_ID)
-            .is_some_and(|cloud_provider| cloud_provider.must_accept_terms(cx))
-        {
-            expanded_provider_configurations.insert(ZED_CLOUD_PROVIDER_ID, true);
-        }
-
         let mut this = Self {
             fs,
             language_registry,
@@ -109,7 +101,7 @@ impl AgentConfiguration {
             configuration_views_by_provider: HashMap::default(),
             context_server_store,
             expanded_context_server_tools: HashMap::default(),
-            expanded_provider_configurations,
+            expanded_provider_configurations: HashMap::default(),
             tools,
             _registry_subscription: registry_subscription,
             scroll_handle,

crates/agent_ui/src/agent_panel.rs πŸ”—

@@ -54,9 +54,7 @@ use gpui::{
     Pixels, Subscription, Task, UpdateGlobal, WeakEntity, prelude::*, pulsating_between,
 };
 use language::LanguageRegistry;
-use language_model::{
-    ConfigurationError, ConfiguredModel, LanguageModelProviderTosView, LanguageModelRegistry,
-};
+use language_model::{ConfigurationError, ConfiguredModel, LanguageModelRegistry};
 use project::{DisableAiSettings, Project, ProjectPath, Worktree};
 use prompt_store::{PromptBuilder, PromptStore, UserPromptId};
 use rules_library::{RulesLibrary, open_rules_library};
@@ -3203,17 +3201,6 @@ impl AgentPanel {
             ConfigurationError::ModelNotFound
             | ConfigurationError::ProviderNotAuthenticated(_)
             | ConfigurationError::NoProvider => callout.into_any_element(),
-            ConfigurationError::ProviderPendingTermsAcceptance(provider) => {
-                Banner::new()
-                    .severity(Severity::Warning)
-                    .child(h_flex().w_full().children(
-                        provider.render_accept_terms(
-                            LanguageModelProviderTosView::ThreadEmptyState,
-                            cx,
-                        ),
-                    ))
-                    .into_any_element()
-            }
         }
     }
 

crates/agent_ui/src/message_editor.rs πŸ”—

@@ -378,18 +378,13 @@ impl MessageEditor {
     }
 
     fn send_to_model(&mut self, window: &mut Window, cx: &mut Context<Self>) {
-        let Some(ConfiguredModel { model, provider }) = self
+        let Some(ConfiguredModel { model, .. }) = self
             .thread
             .update(cx, |thread, cx| thread.get_or_init_configured_model(cx))
         else {
             return;
         };
 
-        if provider.must_accept_terms(cx) {
-            cx.notify();
-            return;
-        }
-
         let (user_message, user_message_creases) = self.editor.update(cx, |editor, cx| {
             let creases = extract_message_creases(editor, cx);
             let text = editor.text(cx);

crates/agent_ui/src/text_thread_editor.rs πŸ”—

@@ -190,7 +190,6 @@ pub struct TextThreadEditor {
     invoked_slash_command_creases: HashMap<InvokedSlashCommandId, CreaseId>,
     _subscriptions: Vec<Subscription>,
     last_error: Option<AssistError>,
-    show_accept_terms: bool,
     pub(crate) slash_menu_handle:
         PopoverMenuHandle<Picker<slash_command_picker::SlashCommandDelegate>>,
     // dragged_file_worktrees is used to keep references to worktrees that were added
@@ -289,7 +288,6 @@ impl TextThreadEditor {
             invoked_slash_command_creases: HashMap::default(),
             _subscriptions,
             last_error: None,
-            show_accept_terms: false,
             slash_menu_handle: Default::default(),
             dragged_file_worktrees: Vec::new(),
             language_model_selector: cx.new(|cx| {
@@ -367,20 +365,7 @@ impl TextThreadEditor {
     }
 
     fn send_to_model(&mut self, window: &mut Window, cx: &mut Context<Self>) {
-        let provider = LanguageModelRegistry::read_global(cx)
-            .default_model()
-            .map(|default| default.provider);
-        if provider
-            .as_ref()
-            .is_some_and(|provider| provider.must_accept_terms(cx))
-        {
-            self.show_accept_terms = true;
-            cx.notify();
-            return;
-        }
-
         self.last_error = None;
-
         if let Some(user_message) = self.context.update(cx, |context, cx| context.assist(cx)) {
             let new_selection = {
                 let cursor = user_message
@@ -1930,7 +1915,6 @@ impl TextThreadEditor {
             ConfigurationError::NoProvider
             | ConfigurationError::ModelNotFound
             | ConfigurationError::ProviderNotAuthenticated(_) => true,
-            ConfigurationError::ProviderPendingTermsAcceptance(_) => self.show_accept_terms,
         }
     }
 

crates/ai_onboarding/src/ai_onboarding.rs πŸ”—

@@ -19,7 +19,7 @@ use std::sync::Arc;
 
 use client::{Client, UserStore, zed_urls};
 use gpui::{AnyElement, Entity, IntoElement, ParentElement};
-use ui::{Divider, RegisterComponent, TintColor, Tooltip, prelude::*};
+use ui::{Divider, RegisterComponent, Tooltip, prelude::*};
 
 #[derive(PartialEq)]
 pub enum SignInStatus {
@@ -43,12 +43,10 @@ impl From<client::Status> for SignInStatus {
 #[derive(RegisterComponent, IntoElement)]
 pub struct ZedAiOnboarding {
     pub sign_in_status: SignInStatus,
-    pub has_accepted_terms_of_service: bool,
     pub plan: Option<Plan>,
     pub account_too_young: bool,
     pub continue_with_zed_ai: Arc<dyn Fn(&mut Window, &mut App)>,
     pub sign_in: Arc<dyn Fn(&mut Window, &mut App)>,
-    pub accept_terms_of_service: Arc<dyn Fn(&mut Window, &mut App)>,
     pub dismiss_onboarding: Option<Arc<dyn Fn(&mut Window, &mut App)>>,
 }
 
@@ -64,17 +62,9 @@ impl ZedAiOnboarding {
 
         Self {
             sign_in_status: status.into(),
-            has_accepted_terms_of_service: store.has_accepted_terms_of_service(),
             plan: store.plan(),
             account_too_young: store.account_too_young(),
             continue_with_zed_ai,
-            accept_terms_of_service: Arc::new({
-                let store = user_store.clone();
-                move |_window, cx| {
-                    let task = store.update(cx, |store, cx| store.accept_terms_of_service(cx));
-                    task.detach_and_log_err(cx);
-                }
-            }),
             sign_in: Arc::new(move |_window, cx| {
                 cx.spawn({
                     let client = client.clone();
@@ -94,42 +84,6 @@ impl ZedAiOnboarding {
         self
     }
 
-    fn render_accept_terms_of_service(&self) -> AnyElement {
-        v_flex()
-            .gap_1()
-            .w_full()
-            .child(Headline::new("Accept Terms of Service"))
-            .child(
-                Label::new("We don’t sell your data, track you across the web, or compromise your privacy.")
-                    .color(Color::Muted)
-                    .mb_2(),
-            )
-            .child(
-                Button::new("terms_of_service", "Review Terms of Service")
-                    .full_width()
-                    .style(ButtonStyle::Outlined)
-                    .icon(IconName::ArrowUpRight)
-                    .icon_color(Color::Muted)
-                    .icon_size(IconSize::Small)
-                    .on_click(move |_, _window, cx| {
-                        telemetry::event!("Review Terms of Service Clicked");
-                        cx.open_url(&zed_urls::terms_of_service(cx))
-                    }),
-            )
-            .child(
-                Button::new("accept_terms", "Accept")
-                    .full_width()
-                    .style(ButtonStyle::Tinted(TintColor::Accent))
-                    .on_click({
-                        let callback = self.accept_terms_of_service.clone();
-                        move |_, window, cx| {
-                            telemetry::event!("Terms of Service Accepted");
-                            (callback)(window, cx)}
-                    }),
-            )
-            .into_any_element()
-    }
-
     fn render_sign_in_disclaimer(&self, _cx: &mut App) -> AnyElement {
         let signing_in = matches!(self.sign_in_status, SignInStatus::SigningIn);
         let plan_definitions = PlanDefinitions;
@@ -359,14 +313,10 @@ impl ZedAiOnboarding {
 impl RenderOnce for ZedAiOnboarding {
     fn render(self, _window: &mut ui::Window, cx: &mut App) -> impl IntoElement {
         if matches!(self.sign_in_status, SignInStatus::SignedIn) {
-            if self.has_accepted_terms_of_service {
-                match self.plan {
-                    None | Some(Plan::ZedFree) => self.render_free_plan_state(cx),
-                    Some(Plan::ZedProTrial) => self.render_trial_state(cx),
-                    Some(Plan::ZedPro) => self.render_pro_plan_state(cx),
-                }
-            } else {
-                self.render_accept_terms_of_service()
+            match self.plan {
+                None | Some(Plan::ZedFree) => self.render_free_plan_state(cx),
+                Some(Plan::ZedProTrial) => self.render_trial_state(cx),
+                Some(Plan::ZedPro) => self.render_pro_plan_state(cx),
             }
         } else {
             self.render_sign_in_disclaimer(cx)
@@ -390,18 +340,15 @@ impl Component for ZedAiOnboarding {
     fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
         fn onboarding(
             sign_in_status: SignInStatus,
-            has_accepted_terms_of_service: bool,
             plan: Option<Plan>,
             account_too_young: bool,
         ) -> AnyElement {
             ZedAiOnboarding {
                 sign_in_status,
-                has_accepted_terms_of_service,
                 plan,
                 account_too_young,
                 continue_with_zed_ai: Arc::new(|_, _| {}),
                 sign_in: Arc::new(|_, _| {}),
-                accept_terms_of_service: Arc::new(|_, _| {}),
                 dismiss_onboarding: None,
             }
             .into_any_element()
@@ -415,27 +362,23 @@ impl Component for ZedAiOnboarding {
                 .children(vec![
                     single_example(
                         "Not Signed-in",
-                        onboarding(SignInStatus::SignedOut, false, None, false),
-                    ),
-                    single_example(
-                        "Not Accepted ToS",
-                        onboarding(SignInStatus::SignedIn, false, None, false),
+                        onboarding(SignInStatus::SignedOut, None, false),
                     ),
                     single_example(
                         "Young Account",
-                        onboarding(SignInStatus::SignedIn, true, None, true),
+                        onboarding(SignInStatus::SignedIn, None, true),
                     ),
                     single_example(
                         "Free Plan",
-                        onboarding(SignInStatus::SignedIn, true, Some(Plan::ZedFree), false),
+                        onboarding(SignInStatus::SignedIn, Some(Plan::ZedFree), false),
                     ),
                     single_example(
                         "Pro Trial",
-                        onboarding(SignInStatus::SignedIn, true, Some(Plan::ZedProTrial), false),
+                        onboarding(SignInStatus::SignedIn, Some(Plan::ZedProTrial), false),
                     ),
                     single_example(
                         "Pro Plan",
-                        onboarding(SignInStatus::SignedIn, true, Some(Plan::ZedPro), false),
+                        onboarding(SignInStatus::SignedIn, Some(Plan::ZedPro), false),
                     ),
                 ])
                 .into_any_element(),

crates/client/src/user.rs πŸ”—

@@ -1,5 +1,5 @@
 use super::{Client, Status, TypedEnvelope, proto};
-use anyhow::{Context as _, Result, anyhow};
+use anyhow::{Context as _, Result};
 use chrono::{DateTime, Utc};
 use cloud_api_client::websocket_protocol::MessageToClient;
 use cloud_api_client::{GetAuthenticatedUserResponse, PlanInfo};
@@ -116,7 +116,6 @@ pub struct UserStore {
     edit_prediction_usage: Option<EditPredictionUsage>,
     plan_info: Option<PlanInfo>,
     current_user: watch::Receiver<Option<Arc<User>>>,
-    accepted_tos_at: Option<Option<cloud_api_client::Timestamp>>,
     contacts: Vec<Arc<Contact>>,
     incoming_contact_requests: Vec<Arc<User>>,
     outgoing_contact_requests: Vec<Arc<User>>,
@@ -194,7 +193,6 @@ impl UserStore {
             plan_info: None,
             model_request_usage: None,
             edit_prediction_usage: None,
-            accepted_tos_at: None,
             contacts: Default::default(),
             incoming_contact_requests: Default::default(),
             participant_indices: Default::default(),
@@ -271,7 +269,6 @@ impl UserStore {
                         Status::SignedOut => {
                             current_user_tx.send(None).await.ok();
                             this.update(cx, |this, cx| {
-                                this.accepted_tos_at = None;
                                 cx.emit(Event::PrivateUserInfoUpdated);
                                 cx.notify();
                                 this.clear_contacts()
@@ -791,19 +788,6 @@ impl UserStore {
                 .set_authenticated_user_info(Some(response.user.metrics_id.clone()), staff);
         }
 
-        let accepted_tos_at = {
-            #[cfg(debug_assertions)]
-            if std::env::var("ZED_IGNORE_ACCEPTED_TOS").is_ok() {
-                None
-            } else {
-                response.user.accepted_tos_at
-            }
-
-            #[cfg(not(debug_assertions))]
-            response.user.accepted_tos_at
-        };
-
-        self.accepted_tos_at = Some(accepted_tos_at);
         self.model_request_usage = Some(ModelRequestUsage(RequestUsage {
             limit: response.plan.usage.model_requests.limit,
             amount: response.plan.usage.model_requests.used as i32,
@@ -846,32 +830,6 @@ impl UserStore {
         self.current_user.clone()
     }
 
-    pub fn has_accepted_terms_of_service(&self) -> bool {
-        self.accepted_tos_at
-            .is_some_and(|accepted_tos_at| accepted_tos_at.is_some())
-    }
-
-    pub fn accept_terms_of_service(&self, cx: &Context<Self>) -> Task<Result<()>> {
-        if self.current_user().is_none() {
-            return Task::ready(Err(anyhow!("no current user")));
-        };
-
-        let client = self.client.clone();
-        cx.spawn(async move |this, cx| -> anyhow::Result<()> {
-            let client = client.upgrade().context("client not found")?;
-            let response = client
-                .cloud_client()
-                .accept_terms_of_service()
-                .await
-                .context("error accepting tos")?;
-            this.update(cx, |this, cx| {
-                this.accepted_tos_at = Some(response.user.accepted_tos_at);
-                cx.emit(Event::PrivateUserInfoUpdated);
-            })?;
-            Ok(())
-        })
-    }
-
     fn load_users(
         &self,
         request: impl RequestMessage<Response = UsersResponse>,

crates/cloud_api_client/src/cloud_api_client.rs πŸ”—

@@ -115,34 +115,6 @@ impl CloudApiClient {
         }))
     }
 
-    pub async fn accept_terms_of_service(&self) -> Result<AcceptTermsOfServiceResponse> {
-        let request = self.build_request(
-            Request::builder().method(Method::POST).uri(
-                self.http_client
-                    .build_zed_cloud_url("/client/terms_of_service/accept", &[])?
-                    .as_ref(),
-            ),
-            AsyncBody::default(),
-        )?;
-
-        let mut response = self.http_client.send(request).await?;
-
-        if !response.status().is_success() {
-            let mut body = String::new();
-            response.body_mut().read_to_string(&mut body).await?;
-
-            anyhow::bail!(
-                "Failed to accept terms of service.\nStatus: {:?}\nBody: {body}",
-                response.status()
-            )
-        }
-
-        let mut body = String::new();
-        response.body_mut().read_to_string(&mut body).await?;
-
-        Ok(serde_json::from_str(&body)?)
-    }
-
     pub async fn create_llm_token(
         &self,
         system_id: Option<String>,

crates/edit_prediction/src/edit_prediction.rs πŸ”—

@@ -89,9 +89,6 @@ pub trait EditPredictionProvider: 'static + Sized {
         debounce: bool,
         cx: &mut Context<Self>,
     );
-    fn needs_terms_acceptance(&self, _cx: &App) -> bool {
-        false
-    }
     fn cycle(
         &mut self,
         buffer: Entity<Buffer>,
@@ -124,7 +121,6 @@ pub trait EditPredictionProviderHandle {
     fn data_collection_state(&self, cx: &App) -> DataCollectionState;
     fn usage(&self, cx: &App) -> Option<EditPredictionUsage>;
     fn toggle_data_collection(&self, cx: &mut App);
-    fn needs_terms_acceptance(&self, cx: &App) -> bool;
     fn is_refreshing(&self, cx: &App) -> bool;
     fn refresh(
         &self,
@@ -196,10 +192,6 @@ where
         self.read(cx).is_enabled(buffer, cursor_position, cx)
     }
 
-    fn needs_terms_acceptance(&self, cx: &App) -> bool {
-        self.read(cx).needs_terms_acceptance(cx)
-    }
-
     fn is_refreshing(&self, cx: &App) -> bool {
         self.read(cx).is_refreshing()
     }

crates/edit_prediction_button/src/edit_prediction_button.rs πŸ”—

@@ -242,13 +242,9 @@ impl Render for EditPredictionButton {
                     IconName::ZedPredictDisabled
                 };
 
-                if zeta::should_show_upsell_modal(&self.user_store, cx) {
+                if zeta::should_show_upsell_modal() {
                     let tooltip_meta = if self.user_store.read(cx).current_user().is_some() {
-                        if self.user_store.read(cx).has_accepted_terms_of_service() {
-                            "Choose a Plan"
-                        } else {
-                            "Accept the Terms of Service"
-                        }
+                        "Choose a Plan"
                     } else {
                         "Sign In"
                     };

crates/editor/src/editor.rs πŸ”—

@@ -253,7 +253,6 @@ pub type RenderDiffHunkControlsFn = Arc<
 enum ReportEditorEvent {
     Saved { auto_saved: bool },
     EditorOpened,
-    ZetaTosClicked,
     Closed,
 }
 
@@ -262,7 +261,6 @@ impl ReportEditorEvent {
         match self {
             Self::Saved { .. } => "Editor Saved",
             Self::EditorOpened => "Editor Opened",
-            Self::ZetaTosClicked => "Edit Prediction Provider ToS Clicked",
             Self::Closed => "Editor Closed",
         }
     }
@@ -9180,45 +9178,6 @@ impl Editor {
         let provider = self.edit_prediction_provider.as_ref()?;
         let provider_icon = Self::get_prediction_provider_icon_name(&self.edit_prediction_provider);
 
-        if provider.provider.needs_terms_acceptance(cx) {
-            return Some(
-                h_flex()
-                    .min_w(min_width)
-                    .flex_1()
-                    .px_2()
-                    .py_1()
-                    .gap_3()
-                    .elevation_2(cx)
-                    .hover(|style| style.bg(cx.theme().colors().element_hover))
-                    .id("accept-terms")
-                    .cursor_pointer()
-                    .on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
-                    .on_click(cx.listener(|this, _event, window, cx| {
-                        cx.stop_propagation();
-                        this.report_editor_event(ReportEditorEvent::ZetaTosClicked, None, cx);
-                        window.dispatch_action(
-                            zed_actions::OpenZedPredictOnboarding.boxed_clone(),
-                            cx,
-                        );
-                    }))
-                    .child(
-                        h_flex()
-                            .flex_1()
-                            .gap_2()
-                            .child(Icon::new(provider_icon))
-                            .child(Label::new("Accept Terms of Service"))
-                            .child(div().w_full())
-                            .child(
-                                Icon::new(IconName::ArrowUpRight)
-                                    .color(Color::Muted)
-                                    .size(IconSize::Small),
-                            )
-                            .into_any_element(),
-                    )
-                    .into_any(),
-            );
-        }
-
         let is_refreshing = provider.provider.is_refreshing(cx);
 
         fn pending_completion_container(icon: IconName) -> Div {

crates/language_model/src/language_model.rs πŸ”—

@@ -14,7 +14,7 @@ use client::Client;
 use cloud_llm_client::{CompletionMode, CompletionRequestStatus};
 use futures::FutureExt;
 use futures::{StreamExt, future::BoxFuture, stream::BoxStream};
-use gpui::{AnyElement, AnyView, App, AsyncApp, SharedString, Task, Window};
+use gpui::{AnyView, App, AsyncApp, SharedString, Task, Window};
 use http_client::{StatusCode, http};
 use icons::IconName;
 use parking_lot::Mutex;
@@ -640,16 +640,6 @@ pub trait LanguageModelProvider: 'static {
         window: &mut Window,
         cx: &mut App,
     ) -> AnyView;
-    fn must_accept_terms(&self, _cx: &App) -> bool {
-        false
-    }
-    fn render_accept_terms(
-        &self,
-        _view: LanguageModelProviderTosView,
-        _cx: &mut App,
-    ) -> Option<AnyElement> {
-        None
-    }
     fn reset_credentials(&self, cx: &mut App) -> Task<Result<()>>;
 }
 

crates/language_model/src/registry.rs πŸ”—

@@ -24,9 +24,6 @@ pub enum ConfigurationError {
     ModelNotFound,
     #[error("{} LLM provider is not configured.", .0.name().0)]
     ProviderNotAuthenticated(Arc<dyn LanguageModelProvider>),
-    #[error("Using the {} LLM provider requires accepting the Terms of Service.",
-    .0.name().0)]
-    ProviderPendingTermsAcceptance(Arc<dyn LanguageModelProvider>),
 }
 
 impl std::fmt::Debug for ConfigurationError {
@@ -37,9 +34,6 @@ impl std::fmt::Debug for ConfigurationError {
             Self::ProviderNotAuthenticated(provider) => {
                 write!(f, "ProviderNotAuthenticated({})", provider.id())
             }
-            Self::ProviderPendingTermsAcceptance(provider) => {
-                write!(f, "ProviderPendingTermsAcceptance({})", provider.id())
-            }
         }
     }
 }
@@ -198,12 +192,6 @@ impl LanguageModelRegistry {
             return Some(ConfigurationError::ProviderNotAuthenticated(model.provider));
         }
 
-        if model.provider.must_accept_terms(cx) {
-            return Some(ConfigurationError::ProviderPendingTermsAcceptance(
-                model.provider,
-            ));
-        }
-
         None
     }
 

crates/language_models/src/provider/cloud.rs πŸ”—

@@ -23,9 +23,9 @@ use language_model::{
     AuthenticateError, LanguageModel, LanguageModelCacheConfiguration,
     LanguageModelCompletionError, LanguageModelCompletionEvent, LanguageModelId, LanguageModelName,
     LanguageModelProvider, LanguageModelProviderId, LanguageModelProviderName,
-    LanguageModelProviderState, LanguageModelProviderTosView, LanguageModelRequest,
-    LanguageModelToolChoice, LanguageModelToolSchemaFormat, LlmApiToken,
-    ModelRequestLimitReachedError, PaymentRequiredError, RateLimiter, RefreshLlmTokenListener,
+    LanguageModelProviderState, LanguageModelRequest, LanguageModelToolChoice,
+    LanguageModelToolSchemaFormat, LlmApiToken, ModelRequestLimitReachedError,
+    PaymentRequiredError, RateLimiter, RefreshLlmTokenListener,
 };
 use release_channel::AppVersion;
 use schemars::JsonSchema;
@@ -118,7 +118,6 @@ pub struct State {
     llm_api_token: LlmApiToken,
     user_store: Entity<UserStore>,
     status: client::Status,
-    accept_terms_of_service_task: Option<Task<Result<()>>>,
     models: Vec<Arc<cloud_llm_client::LanguageModel>>,
     default_model: Option<Arc<cloud_llm_client::LanguageModel>>,
     default_fast_model: Option<Arc<cloud_llm_client::LanguageModel>>,
@@ -142,7 +141,6 @@ impl State {
             llm_api_token: LlmApiToken::default(),
             user_store,
             status,
-            accept_terms_of_service_task: None,
             models: Vec::new(),
             default_model: None,
             default_fast_model: None,
@@ -197,24 +195,6 @@ impl State {
             state.update(cx, |_, cx| cx.notify())
         })
     }
-
-    fn has_accepted_terms_of_service(&self, cx: &App) -> bool {
-        self.user_store.read(cx).has_accepted_terms_of_service()
-    }
-
-    fn accept_terms_of_service(&mut self, cx: &mut Context<Self>) {
-        let user_store = self.user_store.clone();
-        self.accept_terms_of_service_task = Some(cx.spawn(async move |this, cx| {
-            let _ = user_store
-                .update(cx, |store, cx| store.accept_terms_of_service(cx))?
-                .await;
-            this.update(cx, |this, cx| {
-                this.accept_terms_of_service_task = None;
-                cx.notify()
-            })
-        }));
-    }
-
     fn update_models(&mut self, response: ListModelsResponse, cx: &mut Context<Self>) {
         let mut models = Vec::new();
 
@@ -384,7 +364,7 @@ impl LanguageModelProvider for CloudLanguageModelProvider {
 
     fn is_authenticated(&self, cx: &App) -> bool {
         let state = self.state.read(cx);
-        !state.is_signed_out(cx) && state.has_accepted_terms_of_service(cx)
+        !state.is_signed_out(cx)
     }
 
     fn authenticate(&self, _cx: &mut App) -> Task<Result<(), AuthenticateError>> {
@@ -401,112 +381,11 @@ impl LanguageModelProvider for CloudLanguageModelProvider {
             .into()
     }
 
-    fn must_accept_terms(&self, cx: &App) -> bool {
-        !self.state.read(cx).has_accepted_terms_of_service(cx)
-    }
-
-    fn render_accept_terms(
-        &self,
-        view: LanguageModelProviderTosView,
-        cx: &mut App,
-    ) -> Option<AnyElement> {
-        let state = self.state.read(cx);
-        if state.has_accepted_terms_of_service(cx) {
-            return None;
-        }
-        Some(
-            render_accept_terms(view, state.accept_terms_of_service_task.is_some(), {
-                let state = self.state.clone();
-                move |_window, cx| {
-                    state.update(cx, |state, cx| state.accept_terms_of_service(cx));
-                }
-            })
-            .into_any_element(),
-        )
-    }
-
     fn reset_credentials(&self, _cx: &mut App) -> Task<Result<()>> {
         Task::ready(Ok(()))
     }
 }
 
-fn render_accept_terms(
-    view_kind: LanguageModelProviderTosView,
-    accept_terms_of_service_in_progress: bool,
-    accept_terms_callback: impl Fn(&mut Window, &mut App) + 'static,
-) -> impl IntoElement {
-    let thread_fresh_start = matches!(view_kind, LanguageModelProviderTosView::ThreadFreshStart);
-    let thread_empty_state = matches!(view_kind, LanguageModelProviderTosView::ThreadEmptyState);
-
-    let terms_button = Button::new("terms_of_service", "Terms of Service")
-        .style(ButtonStyle::Subtle)
-        .icon(IconName::ArrowUpRight)
-        .icon_color(Color::Muted)
-        .icon_size(IconSize::Small)
-        .when(thread_empty_state, |this| this.label_size(LabelSize::Small))
-        .on_click(move |_, _window, cx| cx.open_url("https://zed.dev/terms-of-service"));
-
-    let button_container = h_flex().child(
-        Button::new("accept_terms", "I accept the Terms of Service")
-            .when(!thread_empty_state, |this| {
-                this.full_width()
-                    .style(ButtonStyle::Tinted(TintColor::Accent))
-                    .icon(IconName::Check)
-                    .icon_position(IconPosition::Start)
-                    .icon_size(IconSize::Small)
-            })
-            .when(thread_empty_state, |this| {
-                this.style(ButtonStyle::Tinted(TintColor::Warning))
-                    .label_size(LabelSize::Small)
-            })
-            .disabled(accept_terms_of_service_in_progress)
-            .on_click(move |_, window, cx| (accept_terms_callback)(window, cx)),
-    );
-
-    if thread_empty_state {
-        h_flex()
-            .w_full()
-            .flex_wrap()
-            .justify_between()
-            .child(
-                h_flex()
-                    .child(
-                        Label::new("To start using Zed AI, please read and accept the")
-                            .size(LabelSize::Small),
-                    )
-                    .child(terms_button),
-            )
-            .child(button_container)
-    } else {
-        v_flex()
-            .w_full()
-            .gap_2()
-            .child(
-                h_flex()
-                    .flex_wrap()
-                    .when(thread_fresh_start, |this| this.justify_center())
-                    .child(Label::new(
-                        "To start using Zed AI, please read and accept the",
-                    ))
-                    .child(terms_button),
-            )
-            .child({
-                match view_kind {
-                    LanguageModelProviderTosView::TextThreadPopup => {
-                        button_container.w_full().justify_end()
-                    }
-                    LanguageModelProviderTosView::Configuration => {
-                        button_container.w_full().justify_start()
-                    }
-                    LanguageModelProviderTosView::ThreadFreshStart => {
-                        button_container.w_full().justify_center()
-                    }
-                    LanguageModelProviderTosView::ThreadEmptyState => div().w_0(),
-                }
-            })
-    }
-}
-
 pub struct CloudLanguageModel {
     id: LanguageModelId,
     model: Arc<cloud_llm_client::LanguageModel>,
@@ -1107,10 +986,7 @@ struct ZedAiConfiguration {
     plan: Option<Plan>,
     subscription_period: Option<(DateTime<Utc>, DateTime<Utc>)>,
     eligible_for_trial: bool,
-    has_accepted_terms_of_service: bool,
     account_too_young: bool,
-    accept_terms_of_service_in_progress: bool,
-    accept_terms_of_service_callback: Arc<dyn Fn(&mut Window, &mut App) + Send + Sync>,
     sign_in_callback: Arc<dyn Fn(&mut Window, &mut App) + Send + Sync>,
 }
 
@@ -1176,58 +1052,30 @@ impl RenderOnce for ZedAiConfiguration {
                 );
         }
 
-        v_flex()
-            .gap_2()
-            .w_full()
-            .when(!self.has_accepted_terms_of_service, |this| {
-                this.child(render_accept_terms(
-                    LanguageModelProviderTosView::Configuration,
-                    self.accept_terms_of_service_in_progress,
-                    {
-                        let callback = self.accept_terms_of_service_callback.clone();
-                        move |window, cx| (callback)(window, cx)
-                    },
-                ))
-            })
-            .map(|this| {
-                if self.has_accepted_terms_of_service && self.account_too_young {
-                    this.child(young_account_banner).child(
-                        Button::new("upgrade", "Upgrade to Pro")
-                            .style(ui::ButtonStyle::Tinted(ui::TintColor::Accent))
-                            .full_width()
-                            .on_click(|_, _, cx| {
-                                cx.open_url(&zed_urls::upgrade_to_zed_pro_url(cx))
-                            }),
-                    )
-                } else if self.has_accepted_terms_of_service {
-                    this.text_sm()
-                        .child(subscription_text)
-                        .child(manage_subscription_buttons)
-                } else {
-                    this
-                }
-            })
-            .when(self.has_accepted_terms_of_service, |this| this)
+        v_flex().gap_2().w_full().map(|this| {
+            if self.account_too_young {
+                this.child(young_account_banner).child(
+                    Button::new("upgrade", "Upgrade to Pro")
+                        .style(ui::ButtonStyle::Tinted(ui::TintColor::Accent))
+                        .full_width()
+                        .on_click(|_, _, cx| cx.open_url(&zed_urls::upgrade_to_zed_pro_url(cx))),
+                )
+            } else {
+                this.text_sm()
+                    .child(subscription_text)
+                    .child(manage_subscription_buttons)
+            }
+        })
     }
 }
 
 struct ConfigurationView {
     state: Entity<State>,
-    accept_terms_of_service_callback: Arc<dyn Fn(&mut Window, &mut App) + Send + Sync>,
     sign_in_callback: Arc<dyn Fn(&mut Window, &mut App) + Send + Sync>,
 }
 
 impl ConfigurationView {
     fn new(state: Entity<State>) -> Self {
-        let accept_terms_of_service_callback = Arc::new({
-            let state = state.clone();
-            move |_window: &mut Window, cx: &mut App| {
-                state.update(cx, |state, cx| {
-                    state.accept_terms_of_service(cx);
-                });
-            }
-        });
-
         let sign_in_callback = Arc::new({
             let state = state.clone();
             move |_window: &mut Window, cx: &mut App| {
@@ -1239,7 +1087,6 @@ impl ConfigurationView {
 
         Self {
             state,
-            accept_terms_of_service_callback,
             sign_in_callback,
         }
     }
@@ -1255,10 +1102,7 @@ impl Render for ConfigurationView {
             plan: user_store.plan(),
             subscription_period: user_store.subscription_period(),
             eligible_for_trial: user_store.trial_started_at().is_none(),
-            has_accepted_terms_of_service: state.has_accepted_terms_of_service(cx),
             account_too_young: user_store.account_too_young(),
-            accept_terms_of_service_in_progress: state.accept_terms_of_service_task.is_some(),
-            accept_terms_of_service_callback: self.accept_terms_of_service_callback.clone(),
             sign_in_callback: self.sign_in_callback.clone(),
         }
     }
@@ -1283,7 +1127,6 @@ impl Component for ZedAiConfiguration {
             plan: Option<Plan>,
             eligible_for_trial: bool,
             account_too_young: bool,
-            has_accepted_terms_of_service: bool,
         ) -> AnyElement {
             ZedAiConfiguration {
                 is_connected,
@@ -1292,10 +1135,7 @@ impl Component for ZedAiConfiguration {
                     .is_some()
                     .then(|| (Utc::now(), Utc::now() + chrono::Duration::days(7))),
                 eligible_for_trial,
-                has_accepted_terms_of_service,
                 account_too_young,
-                accept_terms_of_service_in_progress: false,
-                accept_terms_of_service_callback: Arc::new(|_, _| {}),
                 sign_in_callback: Arc::new(|_, _| {}),
             }
             .into_any_element()
@@ -1306,33 +1146,30 @@ impl Component for ZedAiConfiguration {
                 .p_4()
                 .gap_4()
                 .children(vec![
-                    single_example(
-                        "Not connected",
-                        configuration(false, None, false, false, true),
-                    ),
+                    single_example("Not connected", configuration(false, None, false, false)),
                     single_example(
                         "Accept Terms of Service",
-                        configuration(true, None, true, false, false),
+                        configuration(true, None, true, false),
                     ),
                     single_example(
                         "No Plan - Not eligible for trial",
-                        configuration(true, None, false, false, true),
+                        configuration(true, None, false, false),
                     ),
                     single_example(
                         "No Plan - Eligible for trial",
-                        configuration(true, None, true, false, true),
+                        configuration(true, None, true, false),
                     ),
                     single_example(
                         "Free Plan",
-                        configuration(true, Some(Plan::ZedFree), true, false, true),
+                        configuration(true, Some(Plan::ZedFree), true, false),
                     ),
                     single_example(
                         "Zed Pro Trial Plan",
-                        configuration(true, Some(Plan::ZedProTrial), true, false, true),
+                        configuration(true, Some(Plan::ZedProTrial), true, false),
                     ),
                     single_example(
                         "Zed Pro Plan",
-                        configuration(true, Some(Plan::ZedPro), true, false, true),
+                        configuration(true, Some(Plan::ZedPro), true, false),
                     ),
                 ])
                 .into_any_element(),

crates/zed/src/zed/edit_prediction_registry.rs πŸ”—

@@ -75,13 +75,10 @@ pub fn init(client: Arc<Client>, user_store: Entity<UserStore>, cx: &mut App) {
             let new_provider = all_language_settings(None, cx).edit_predictions.provider;
 
             if new_provider != provider {
-                let tos_accepted = user_store.read(cx).has_accepted_terms_of_service();
-
                 telemetry::event!(
                     "Edit Prediction Provider Changed",
                     from = provider,
                     to = new_provider,
-                    zed_ai_tos_accepted = tos_accepted,
                 );
 
                 provider = new_provider;
@@ -92,28 +89,6 @@ pub fn init(client: Arc<Client>, user_store: Entity<UserStore>, cx: &mut App) {
                     user_store.clone(),
                     cx,
                 );
-
-                if !tos_accepted {
-                    match provider {
-                        EditPredictionProvider::Zed => {
-                            let Some(window) = cx.active_window() else {
-                                return;
-                            };
-
-                            window
-                                .update(cx, |_, window, cx| {
-                                    window.dispatch_action(
-                                        Box::new(zed_actions::OpenZedPredictOnboarding),
-                                        cx,
-                                    );
-                                })
-                                .ok();
-                        }
-                        EditPredictionProvider::None
-                        | EditPredictionProvider::Copilot
-                        | EditPredictionProvider::Supermaven => {}
-                    }
-                }
             }
         }
     })

crates/zeta/src/zeta.rs πŸ”—

@@ -118,12 +118,8 @@ impl Dismissable for ZedPredictUpsell {
     }
 }
 
-pub fn should_show_upsell_modal(user_store: &Entity<UserStore>, cx: &App) -> bool {
-    if user_store.read(cx).has_accepted_terms_of_service() {
-        !ZedPredictUpsell::dismissed()
-    } else {
-        true
-    }
+pub fn should_show_upsell_modal() -> bool {
+    !ZedPredictUpsell::dismissed()
 }
 
 #[derive(Clone)]
@@ -1547,16 +1543,6 @@ impl edit_prediction::EditPredictionProvider for ZetaEditPredictionProvider {
     ) -> bool {
         true
     }
-
-    fn needs_terms_acceptance(&self, cx: &App) -> bool {
-        !self
-            .zeta
-            .read(cx)
-            .user_store
-            .read(cx)
-            .has_accepted_terms_of_service()
-    }
-
     fn is_refreshing(&self) -> bool {
         !self.pending_completions.is_empty()
     }
@@ -1569,10 +1555,6 @@ impl edit_prediction::EditPredictionProvider for ZetaEditPredictionProvider {
         _debounce: bool,
         cx: &mut Context<Self>,
     ) {
-        if self.needs_terms_acceptance(cx) {
-            return;
-        }
-
         if self.zeta.read(cx).update_required {
             return;
         }