acp: Show descriptions for elicitation options (#60527)

<img width="683" height="781" alt="image"
src="https://github.com/user-attachments/assets/bcf1bc75-9bb4-4ddf-88d8-c09a4aa3f34b"
/>


Release Notes:

- N/A
This commit is contained in:
Ben Brandt authored and GitHub committed 2026-07-07 11:45:59 +00:00
1 parent 4293014f8a
commit e9c324f710
3 files changed
+102 -29

No files matched your search

Generated
+6 -6
View File
@@ -322,9 +322,9 @@ dependencies = [
[[package]]
name = "agent-client-protocol"
version = "1.0.1"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16302d16c7531355db16593d99c38c8297db0c4653aa7dd80c3556bb17f4cd8c"
checksum = "ed34c8297a72c9d99fc7493ba6370f45c69fdcd51fdfb469d80f266cf63e5a98"
dependencies = [
"agent-client-protocol-derive",
"agent-client-protocol-schema",
@@ -344,9 +344,9 @@ dependencies = [
[[package]]
name = "agent-client-protocol-derive"
version = "1.0.1"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88b37d552feb6981a0109febda6b71fc723678cd065974c2d76279c19c407095"
checksum = "0de0877cd41073fa0a124edb972df0d02e88c70adde11f49cf2231fd63aae738"
dependencies = [
"quote",
"syn 2.0.117",
@@ -354,9 +354,9 @@ dependencies = [
[[package]]
name = "agent-client-protocol-schema"
version = "1.1.0"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac542aba230234b1591ace7286a47c0514fe3efc3037d43296bde31ba7ee5728"
checksum = "06679e1542356341f4550ccfb16338b64f37f6af70de2105446ef6fbb078234c"
dependencies = [
"anyhow",
"derive_more",
+1 -1
View File
@@ -508,7 +508,7 @@ accesskit = "0.24.0"
accesskit_macos = "0.26.0"
accesskit_unix = "0.21.0"
accesskit_windows = "0.33.1"
agent-client-protocol = { version = "=1.0.1", features = ["unstable"] }
agent-client-protocol = { version = "=1.1.0", features = ["unstable"] }
aho-corasick = "1.1"
alacritty_terminal = { git = "https://github.com/zed-industries/alacritty", rev = "4c129667ce56611becdc82de6e28218c80e2e88f" }
any_vec = "0.14"
@@ -16,6 +16,7 @@ use ui::{
struct ElicitationOption {
value: String,
label: SharedString,
description: Option<SharedString>,
}
enum ElicitationFieldState {
@@ -429,6 +430,51 @@ mod tests {
);
}
#[test]
fn single_select_options_include_titled_descriptions() {
let schema = acp::StringPropertySchema::new().one_of(vec![
acp::EnumOption::new("production", "Production").description("Use live resources"),
]);
let options = single_select_options(&schema);
let [option] = options.as_slice() else {
panic!("expected one option, got {}", options.len());
};
assert_eq!(option.value, "production");
assert_eq!(option.label.to_string(), "Production");
assert_eq!(
option
.description
.as_ref()
.map(|description| description.to_string()),
Some("Use live resources".to_string())
);
}
#[test]
fn multi_select_options_include_titled_descriptions() {
let schema = acp::MultiSelectPropertySchema::titled(vec![
acp::EnumOption::new("repository", "Repository Access")
.description("Read and update repositories"),
]);
let options = multi_select_options(&schema);
let [option] = options.as_slice() else {
panic!("expected one option, got {}", options.len());
};
assert_eq!(option.value, "repository");
assert_eq!(option.label.to_string(), "Repository Access");
assert_eq!(
option
.description
.as_ref()
.map(|description| description.to_string()),
Some("Read and update repositories".to_string())
);
}
#[gpui::test]
fn form_state_preserves_string_whitespace(cx: &mut TestAppContext) {
crate::conversation_view::tests::init_test(cx);
@@ -773,8 +819,10 @@ fn preview_form_schema() -> acp::ElicitationSchema {
.title("Environment")
.description("Select the environment this credential should target.")
.one_of(vec![
acp::EnumOption::new("production", "Production"),
acp::EnumOption::new("staging", "Staging"),
acp::EnumOption::new("production", "Production")
.description("Use the live account and production resources."),
acp::EnumOption::new("staging", "Staging")
.description("Validate changes against staging data first."),
acp::EnumOption::new("development", "Development"),
])
.default_value("staging"),
@@ -783,8 +831,10 @@ fn preview_form_schema() -> acp::ElicitationSchema {
.property(
"scopes",
acp::MultiSelectPropertySchema::titled(vec![
acp::EnumOption::new("profile", "Profile"),
acp::EnumOption::new("repository", "Repository Access"),
acp::EnumOption::new("profile", "Profile")
.description("Read account identity and basic profile details."),
acp::EnumOption::new("repository", "Repository Access")
.description("Read and update repositories connected to this account."),
acp::EnumOption::new("terminal", "Terminal Commands"),
])
.title("Access")
@@ -810,6 +860,7 @@ fn single_select_options(schema: &acp::StringPropertySchema) -> Vec<ElicitationO
.map(|option| ElicitationOption {
value: option.value.clone(),
label: SharedString::from(option.title.clone()),
description: option.description.clone().map(SharedString::from),
})
.collect();
}
@@ -822,6 +873,7 @@ fn single_select_options(schema: &acp::StringPropertySchema) -> Vec<ElicitationO
.map(|value| ElicitationOption {
value: value.clone(),
label: SharedString::from(value.clone()),
description: None,
})
.collect()
}
@@ -843,12 +895,13 @@ fn single_select_default_value(
fn multi_select_options(schema: &acp::MultiSelectPropertySchema) -> Vec<ElicitationOption> {
match &schema.items {
acp::MultiSelectItems::Untitled(items) => items
acp::MultiSelectItems::String(items) => items
.values
.iter()
.map(|value| ElicitationOption {
value: value.clone(),
label: SharedString::from(value.clone()),
description: None,
})
.collect(),
acp::MultiSelectItems::Titled(items) => items
@@ -857,6 +910,7 @@ fn multi_select_options(schema: &acp::MultiSelectPropertySchema) -> Vec<Elicitat
.map(|option| ElicitationOption {
value: option.value.clone(),
label: SharedString::from(option.title.clone()),
description: option.description.clone().map(SharedString::from),
})
.collect(),
_ => Vec::new(),
@@ -1310,11 +1364,7 @@ impl<'a> ElicitationCard<'a> {
cx,
);
})
.child(
div()
.mt_0p5()
.child(Checkbox::new(checkbox_id, checkbox_state)),
)
.child(div().child(Checkbox::new(checkbox_id, checkbox_state)))
.child(
v_flex()
.gap_0p5()
@@ -1412,8 +1462,8 @@ impl<'a> ElicitationCard<'a> {
)))
.w_full()
.min_h(rems_from_px(28.))
.items_center()
.gap_2()
.items_start()
.gap_1p5()
.rounded_sm()
.border_1()
.border_color(field_border_color.opacity(0.5))
@@ -1430,8 +1480,8 @@ impl<'a> ElicitationCard<'a> {
cx,
);
})
.child(Checkbox::new(checkbox_id, checkbox_state))
.child(Label::new(option.label).size(LabelSize::Small).truncate())
.child(div().child(Checkbox::new(checkbox_id, checkbox_state)))
.child(Self::render_option_content(option))
}))
.into_any_element()
}
@@ -1479,8 +1529,8 @@ impl<'a> ElicitationCard<'a> {
.id(option_id)
.w_full()
.min_h(rems_from_px(28.))
.items_center()
.gap_2()
.items_start()
.gap_1p5()
.rounded_sm()
.border_1()
.border_color(border_color.opacity(0.5))
@@ -1496,16 +1546,39 @@ impl<'a> ElicitationCard<'a> {
cx,
);
})
.child(Self::render_radio_indicator(
is_selected,
border_color,
control_background,
))
.child(Label::new(option.label).size(LabelSize::Small).truncate())
.child(
div()
.size(Checkbox::container_size())
.flex_none()
.flex()
.items_center()
.justify_center()
.child(Self::render_radio_indicator(
is_selected,
border_color,
control_background,
)),
)
.child(Self::render_option_content(option))
}))
.into_any_element()
}
fn render_option_content(option: ElicitationOption) -> Div {
v_flex()
.min_w_0()
.flex_1()
.gap_0p5()
.child(Label::new(option.label).size(LabelSize::Small).truncate())
.when_some(option.description, |this, description| {
this.child(
Label::new(description)
.size(LabelSize::Small)
.color(Color::Muted),
)
})
}
fn option_row_background(is_selected: bool, cx: &App) -> Hsla {
let editor_background = cx.theme().colors().editor_background;
if is_selected {