Update Agent panel to work with CloudUserStore (#35436)

This PR updates the Agent panel to work with the `CloudUserStore`
instead of the `UserStore`, reducing its reliance on being connected to
Collab to function.

Release Notes:

- N/A

---------

Co-authored-by: Richard Feldman <oss@rtfeldman.com>
This commit is contained in:
Marshall Bowers 2025-07-31 21:44:43 -04:00
parent 6e0999fb4f
commit 5a70f2131c
16 changed files with 212 additions and 108 deletions

View file

@ -3820,6 +3820,7 @@ mod tests {
use super::*;
use agent::{MessageSegment, context::ContextLoadResult, thread_store};
use assistant_tool::{ToolRegistry, ToolWorkingSet};
use client::CloudUserStore;
use editor::EditorSettings;
use fs::FakeFs;
use gpui::{AppContext, TestAppContext, VisualTestContext};
@ -4116,10 +4117,16 @@ mod tests {
let (workspace, cx) =
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
let (client, user_store) =
project.read_with(cx, |project, _cx| (project.client(), project.user_store()));
let cloud_user_store =
cx.new(|cx| CloudUserStore::new(client.cloud_client(), user_store, cx));
let thread_store = cx
.update(|_, cx| {
ThreadStore::load(
project.clone(),
cloud_user_store,
cx.new(|_| ToolWorkingSet::default()),
None,
Arc::new(PromptBuilder::new(None).unwrap()),

View file

@ -1893,6 +1893,7 @@ mod tests {
use agent::thread_store::{self, ThreadStore};
use agent_settings::AgentSettings;
use assistant_tool::ToolWorkingSet;
use client::CloudUserStore;
use editor::EditorSettings;
use gpui::{TestAppContext, UpdateGlobal, VisualTestContext};
use project::{FakeFs, Project};
@ -1932,11 +1933,17 @@ mod tests {
})
.unwrap();
let (client, user_store) =
project.read_with(cx, |project, _cx| (project.client(), project.user_store()));
let cloud_user_store =
cx.new(|cx| CloudUserStore::new(client.cloud_client(), user_store, cx));
let prompt_store = None;
let thread_store = cx
.update(|cx| {
ThreadStore::load(
project.clone(),
cloud_user_store,
cx.new(|_| ToolWorkingSet::default()),
prompt_store,
Arc::new(PromptBuilder::new(None).unwrap()),
@ -2098,11 +2105,17 @@ mod tests {
})
.unwrap();
let (client, user_store) =
project.read_with(cx, |project, _cx| (project.client(), project.user_store()));
let cloud_user_store =
cx.new(|cx| CloudUserStore::new(client.cloud_client(), user_store, cx));
let prompt_store = None;
let thread_store = cx
.update(|cx| {
ThreadStore::load(
project.clone(),
cloud_user_store,
cx.new(|_| ToolWorkingSet::default()),
prompt_store,
Arc::new(PromptBuilder::new(None).unwrap()),

View file

@ -43,7 +43,7 @@ use anyhow::{Result, anyhow};
use assistant_context::{AssistantContext, ContextEvent, ContextSummary};
use assistant_slash_command::SlashCommandWorkingSet;
use assistant_tool::ToolWorkingSet;
use client::{UserStore, zed_urls};
use client::{CloudUserStore, UserStore, zed_urls};
use cloud_llm_client::{CompletionIntent, UsageLimit};
use editor::{Anchor, AnchorRangeExt as _, Editor, EditorEvent, MultiBuffer};
use feature_flags::{self, FeatureFlagAppExt};
@ -427,6 +427,7 @@ impl ActiveView {
pub struct AgentPanel {
workspace: WeakEntity<Workspace>,
user_store: Entity<UserStore>,
cloud_user_store: Entity<CloudUserStore>,
project: Entity<Project>,
fs: Arc<dyn Fs>,
language_registry: Arc<LanguageRegistry>,
@ -486,6 +487,7 @@ impl AgentPanel {
let project = workspace.project().clone();
ThreadStore::load(
project,
workspace.app_state().cloud_user_store.clone(),
tools.clone(),
prompt_store.clone(),
prompt_builder.clone(),
@ -553,6 +555,7 @@ impl AgentPanel {
let thread = thread_store.update(cx, |this, cx| this.create_thread(cx));
let fs = workspace.app_state().fs.clone();
let user_store = workspace.app_state().user_store.clone();
let cloud_user_store = workspace.app_state().cloud_user_store.clone();
let project = workspace.project();
let language_registry = project.read(cx).languages().clone();
let client = workspace.client().clone();
@ -579,7 +582,7 @@ impl AgentPanel {
MessageEditor::new(
fs.clone(),
workspace.clone(),
user_store.clone(),
cloud_user_store.clone(),
message_editor_context_store.clone(),
prompt_store.clone(),
thread_store.downgrade(),
@ -706,6 +709,7 @@ impl AgentPanel {
active_view,
workspace,
user_store,
cloud_user_store,
project: project.clone(),
fs: fs.clone(),
language_registry,
@ -848,7 +852,7 @@ impl AgentPanel {
MessageEditor::new(
self.fs.clone(),
self.workspace.clone(),
self.user_store.clone(),
self.cloud_user_store.clone(),
context_store.clone(),
self.prompt_store.clone(),
self.thread_store.downgrade(),
@ -1122,7 +1126,7 @@ impl AgentPanel {
MessageEditor::new(
self.fs.clone(),
self.workspace.clone(),
self.user_store.clone(),
self.cloud_user_store.clone(),
context_store,
self.prompt_store.clone(),
self.thread_store.downgrade(),
@ -1821,8 +1825,8 @@ impl AgentPanel {
}
fn render_toolbar(&self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let user_store = self.user_store.read(cx);
let usage = user_store.model_request_usage();
let cloud_user_store = self.cloud_user_store.read(cx);
let usage = cloud_user_store.model_request_usage();
let account_url = zed_urls::account_url(cx);

View file

@ -17,7 +17,7 @@ use agent::{
use agent_settings::{AgentSettings, CompletionMode};
use ai_onboarding::ApiKeysWithProviders;
use buffer_diff::BufferDiff;
use client::UserStore;
use client::CloudUserStore;
use cloud_llm_client::CompletionIntent;
use collections::{HashMap, HashSet};
use editor::actions::{MoveUp, Paste};
@ -43,7 +43,6 @@ use language_model::{
use multi_buffer;
use project::Project;
use prompt_store::PromptStore;
use proto::Plan;
use settings::Settings;
use std::time::Duration;
use theme::ThemeSettings;
@ -79,7 +78,7 @@ pub struct MessageEditor {
editor: Entity<Editor>,
workspace: WeakEntity<Workspace>,
project: Entity<Project>,
user_store: Entity<UserStore>,
cloud_user_store: Entity<CloudUserStore>,
context_store: Entity<ContextStore>,
prompt_store: Option<Entity<PromptStore>>,
history_store: Option<WeakEntity<HistoryStore>>,
@ -159,7 +158,7 @@ impl MessageEditor {
pub fn new(
fs: Arc<dyn Fs>,
workspace: WeakEntity<Workspace>,
user_store: Entity<UserStore>,
cloud_user_store: Entity<CloudUserStore>,
context_store: Entity<ContextStore>,
prompt_store: Option<Entity<PromptStore>>,
thread_store: WeakEntity<ThreadStore>,
@ -231,7 +230,7 @@ impl MessageEditor {
Self {
editor: editor.clone(),
project: thread.read(cx).project().clone(),
user_store,
cloud_user_store,
thread,
incompatible_tools_state: incompatible_tools.clone(),
workspace,
@ -1287,26 +1286,16 @@ impl MessageEditor {
return None;
}
let user_store = self.user_store.read(cx);
let ubb_enable = user_store
.usage_based_billing_enabled()
.map_or(false, |enabled| enabled);
if ubb_enable {
let cloud_user_store = self.cloud_user_store.read(cx);
if cloud_user_store.is_usage_based_billing_enabled() {
return None;
}
let plan = user_store
.current_plan()
.map(|plan| match plan {
Plan::Free => cloud_llm_client::Plan::ZedFree,
Plan::ZedPro => cloud_llm_client::Plan::ZedPro,
Plan::ZedProTrial => cloud_llm_client::Plan::ZedProTrial,
})
let plan = cloud_user_store
.plan()
.unwrap_or(cloud_llm_client::Plan::ZedFree);
let usage = user_store.model_request_usage()?;
let usage = cloud_user_store.model_request_usage()?;
Some(
div()
@ -1769,7 +1758,7 @@ impl AgentPreview for MessageEditor {
) -> Option<AnyElement> {
if let Some(workspace) = workspace.upgrade() {
let fs = workspace.read(cx).app_state().fs.clone();
let user_store = workspace.read(cx).app_state().user_store.clone();
let cloud_user_store = workspace.read(cx).app_state().cloud_user_store.clone();
let project = workspace.read(cx).project().clone();
let weak_project = project.downgrade();
let context_store = cx.new(|_cx| ContextStore::new(weak_project, None));
@ -1782,7 +1771,7 @@ impl AgentPreview for MessageEditor {
MessageEditor::new(
fs,
workspace.downgrade(),
user_store,
cloud_user_store,
context_store,
None,
thread_store.downgrade(),