Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions app/src/ai/agent_sdk/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,10 +300,8 @@ impl EnvironmentCommandRunner {
if let Some(desc) = &env.description {
println!("Description: {desc}");
}
match &env.base_image {
BaseImage::DockerImage(img) => {
println!("Docker image: {img}");
}
if let Some(BaseImage::DockerImage(img)) = &env.base_image {
println!("Docker image: {img}");
}
if env.github_repos.is_empty() {
println!("Repositories: None");
Expand Down Expand Up @@ -952,7 +950,7 @@ impl EnvironmentCommandRunner {
}

if let Some(new_docker_image) = docker_image {
updated_env.base_image = BaseImage::DockerImage(new_docker_image);
updated_env.base_image = Some(BaseImage::DockerImage(new_docker_image));
}

for repo in add_repos {
Expand Down Expand Up @@ -1121,7 +1119,8 @@ struct EnvironmentInfo {
name: String,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
base_image: BaseImage,
#[serde(skip_serializing_if = "Option::is_none")]
base_image: Option<BaseImage>,
github_repos: Vec<GithubRepo>,
setup_commands: Vec<String>,
creator_email: String,
Expand Down Expand Up @@ -1161,7 +1160,12 @@ impl TableFormat for EnvironmentInfo {
Cell::new(&self.id),
Cell::new(&self.name),
Cell::new(description_display),
Cell::new(self.base_image.to_string()),
Cell::new(
self.base_image
.as_ref()
.map(ToString::to_string)
.unwrap_or_default(),
),
Cell::new(github_repos_display),
Cell::new(setup_commands_display),
Cell::new(&self.creator_email),
Expand Down
38 changes: 23 additions & 15 deletions app/src/ai/conversation_details_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1635,7 +1635,6 @@ impl ConversationDetailsPanel {
app: &AppContext,
) -> Box<dyn Element> {
let environment_name = &env_model.name;
let docker_image = env_model.base_image.to_string();

let theme = appearance.theme();
let ui_font_size = appearance.ui_font_size();
Expand Down Expand Up @@ -1702,16 +1701,18 @@ impl ConversationDetailsPanel {
.finish(),
);

section.add_child(
Container::new(render_copyable_field(
"Image",
&docker_image,
CopyButtonKind::DockerImage,
ConversationDetailsPanelAction::CopyDockerImage,
))
.with_margin_bottom(LABEL_VALUE_GAP)
.finish(),
);
if let Some(docker_image) = env_model.base_image.as_ref().map(ToString::to_string) {
section.add_child(
Container::new(render_copyable_field(
"Image",
&docker_image,
CopyButtonKind::DockerImage,
ConversationDetailsPanelAction::CopyDockerImage,
))
.with_margin_bottom(LABEL_VALUE_GAP)
.finish(),
);
}

Container::new(section.finish())
.with_margin_bottom(FIELD_SPACING)
Expand Down Expand Up @@ -2263,10 +2264,17 @@ impl TypedActionView for ConversationDetailsPanel {
if let Ok(server_id) = ServerId::try_from(env_id.as_str()) {
let sync_id = SyncId::ServerId(server_id);
if let Some(env) = CloudAmbientAgentEnvironment::get_by_id(&sync_id, ctx) {
let docker_image = env.model().string_model.base_image.to_string();
ctx.clipboard()
.write(ClipboardContent::plain_text(docker_image));
self.record_copy(CopyButtonKind::DockerImage, ctx);
if let Some(docker_image) = env
.model()
.string_model
.base_image
.as_ref()
.map(ToString::to_string)
{
ctx.clipboard()
.write(ClipboardContent::plain_text(docker_image));
self.record_copy(CopyButtonKind::DockerImage, ctx);
}
}
}
}
Expand Down
8 changes: 7 additions & 1 deletion app/src/context_chips/display_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,13 @@ impl DisplayChipMenu {
Some(EnvironmentSidecarData {
name: env.model().string_model.display_name(),
id: env.id.to_string(),
image: env.model().string_model.base_image.to_string(),
image: env
.model()
.string_model
.base_image
.as_ref()
.map(ToString::to_string)
.unwrap_or_default(),
repos_text,
})
}
Expand Down
44 changes: 27 additions & 17 deletions app/src/settings_view/environments_page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ struct EnvironmentDisplayData {
id: SyncId,
name: String,
description: Option<String>,
docker_image: String,
docker_image: Option<String>,
github_repos: Vec<(String, String)>, // (owner, repo)
setup_commands: Vec<String>,
last_edited_ts: Option<ServerTimestamp>,
Expand All @@ -144,7 +144,7 @@ impl EnvironmentDisplayData {
id: env.id,
name: model.name.clone(),
description: model.description.clone(),
docker_image: model.base_image.to_string(),
docker_image: model.base_image.as_ref().map(ToString::to_string),
github_repos: model
.github_repos
.iter()
Expand All @@ -168,7 +168,7 @@ impl EnvironmentDisplayData {
self.id.to_string(),
self.name.clone(),
self.description.clone().unwrap_or_default(),
self.docker_image.clone(),
self.docker_image.clone().unwrap_or_default(),
];

if haystacks
Expand Down Expand Up @@ -270,7 +270,11 @@ impl EnvironmentsPageView {
name: model.name.clone(),
description: model.description.clone().unwrap_or_default(),
selected_repos: model.github_repos.clone(),
docker_image: model.base_image.to_string(),
docker_image: model
.base_image
.as_ref()
.map(ToString::to_string)
.unwrap_or_default(),
setup_commands: model.setup_commands.clone(),
}
});
Expand Down Expand Up @@ -1811,7 +1815,11 @@ impl EnvironmentsPageWidget {
}
}

let mut details_parts = vec![format!("Image: {}", env_docker_image)];
let mut details_parts = Vec::new();

if let Some(docker_image) = &env_docker_image {
details_parts.push(format!("Image: {docker_image}"));
}

if !env_github_repos.is_empty() {
let repos_text = env_github_repos
Expand All @@ -1836,18 +1844,20 @@ impl EnvironmentsPageWidget {
details_section.add_child(env_id_with_copy);

// Add other details on a new line - selectable
let details_text = details_parts.join(" · ");
details_section.add_child(
Text::new(
details_text,
appearance.ui_font_family(),
appearance.ui_font_size() * 0.9,
)
.soft_wrap(true)
.with_color(blended_colors::text_sub(theme, theme.surface_1()))
.with_selectable(true)
.finish(),
);
if !details_parts.is_empty() {
let details_text = details_parts.join(" · ");
details_section.add_child(
Text::new(
details_text,
appearance.ui_font_family(),
appearance.ui_font_size() * 0.9,
)
.soft_wrap(true)
.with_color(blended_colors::text_sub(theme, theme.surface_1()))
.with_selectable(true)
.finish(),
);
}

let timestamp_color = blended_colors::text_sub(theme, theme.surface_1());

Expand Down
2 changes: 1 addition & 1 deletion app/src/settings_view/environments_page_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ fn make_test_environment_with_timestamps(
id: SyncId::ClientId(ClientId::new()),
name: name.to_string(),
description: None,
docker_image: docker_image.to_string(),
docker_image: Some(docker_image.to_string()),
github_repos,
setup_commands,
last_edited_ts,
Expand Down
6 changes: 3 additions & 3 deletions crates/cloud_object_models/src/cloud_environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,8 @@ pub struct AmbientAgentEnvironment {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source_repos: Option<Vec<SourceRepo>>,
/// Base image specification
#[serde(flatten)]
pub base_image: BaseImage,
#[serde(default, skip_serializing_if = "Option::is_none", flatten)]
pub base_image: Option<BaseImage>,
/// List of setup commands to run after cloning
#[serde(default)]
pub setup_commands: Vec<String>,
Expand Down Expand Up @@ -212,7 +212,7 @@ impl AmbientAgentEnvironment {
code_forge: None,
github_repos,
source_repos: None,
base_image: BaseImage::DockerImage(docker_image),
base_image: Some(BaseImage::DockerImage(docker_image)),
setup_commands,
providers: ProvidersConfig::default(),
secrets: None,
Expand Down
43 changes: 42 additions & 1 deletion crates/cloud_object_models/src/cloud_environment_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,47 @@ use super::{
AmbientAgentEnvironment, AwsProviderConfig, BaseImage, CodeForge, EnvironmentSecretRef,
GcpProviderConfig, GithubRepo, ProvidersConfig, SourceRepo,
};
#[test]
fn deserialize_environment_without_docker_image() {
let json = serde_json::json!({
"name": "default-image-env",
"github_repos": [],
"setup_commands": []
});
let env: AmbientAgentEnvironment = serde_json::from_value(json).unwrap();

assert_eq!(env.base_image, None);
}

#[test]
fn absent_docker_image_roundtrip_omits_json_field() {
let mut env =
AmbientAgentEnvironment::new("no-image-env".into(), None, vec![], String::new(), vec![]);
env.base_image = None;

let json = serde_json::to_value(&env).unwrap();
assert!(!json.as_object().unwrap().contains_key("docker_image"));

let roundtripped: AmbientAgentEnvironment = serde_json::from_value(json).unwrap();
assert_eq!(roundtripped, env);
}

#[test]
fn docker_image_roundtrip_preserves_flattened_json_shape() {
let json = serde_json::json!({
"name": "custom-image-env",
"github_repos": [],
"docker_image": "ubuntu:latest",
"setup_commands": []
});

let env: AmbientAgentEnvironment = serde_json::from_value(json.clone()).unwrap();
assert_eq!(
env.base_image,
Some(BaseImage::DockerImage("ubuntu:latest".into()))
);
assert_eq!(serde_json::to_value(env).unwrap(), json);
}

#[test]
fn deserialize_legacy_environment_without_providers() {
Expand All @@ -28,7 +69,7 @@ fn deserialize_legacy_environment_without_providers() {
);
assert_eq!(
env.base_image,
BaseImage::DockerImage("ubuntu:latest".into())
Some(BaseImage::DockerImage("ubuntu:latest".into()))
);
assert_eq!(env.setup_commands, vec!["echo hello"]);
}
Expand Down