mirror of
https://github.com/warpdotdev/warp.git
synced 2026-05-06 23:32:51 +08:00
use correct cloud agent icons for 3p conversation transcripts (#10148)
## Description <!-- Please remember to add your design buddy onto the PR for review, if it contains any UI changes! --> We weren't properly checking/respecting 3p agent harness information for cloud conversation transcripts when deciding which icon to display in the conversation list, vertical tabs, and pane header. ## Screenshots / Videos <!-- Attach screenshots or a short video demonstrating the change, where appropriate. Remove this section if it is not relevant to your PR. -->  ## Agent Mode - [x] Warp Agent Mode - This PR was created via Warp's AI Agent Mode
This commit is contained in:
@@ -607,7 +607,7 @@ impl ConversationOrTask<'_> {
|
||||
}
|
||||
|
||||
/// Resolve the effective execution harness for this run.
|
||||
pub fn harness(&self) -> Option<Harness> {
|
||||
pub fn harness(&self, app: &AppContext) -> Option<Harness> {
|
||||
match self {
|
||||
ConversationOrTask::Task(task) => {
|
||||
task.agent_config_snapshot.as_ref().and_then(|config| {
|
||||
@@ -618,7 +618,10 @@ impl ConversationOrTask<'_> {
|
||||
.or(Some(Harness::Oz))
|
||||
})
|
||||
}
|
||||
ConversationOrTask::Conversation(_) => Some(Harness::Oz),
|
||||
ConversationOrTask::Conversation(metadata) => BlocklistAIHistoryModel::as_ref(app)
|
||||
.get_server_conversation_metadata(&metadata.nav_data.id)
|
||||
.map(|m| Harness::from(m.harness))
|
||||
.or(Some(Harness::Oz)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -741,10 +744,10 @@ impl ConversationOrTask<'_> {
|
||||
}
|
||||
|
||||
/// Check if this item matches the harness filter.
|
||||
fn matches_harness(&self, harness_filter: &HarnessFilter) -> bool {
|
||||
fn matches_harness(&self, harness_filter: &HarnessFilter, app: &AppContext) -> bool {
|
||||
match harness_filter {
|
||||
HarnessFilter::All => true,
|
||||
HarnessFilter::Specific(h) => self.harness() == Some(*h),
|
||||
HarnessFilter::Specific(h) => self.harness(app) == Some(*h),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1572,7 +1575,8 @@ impl AgentConversationsModel {
|
||||
};
|
||||
|
||||
let harness_filter_value = filters.harness;
|
||||
let harness_filter = move |t: &ConversationOrTask| t.matches_harness(&harness_filter_value);
|
||||
let harness_filter =
|
||||
move |t: &ConversationOrTask| t.matches_harness(&harness_filter_value, app);
|
||||
|
||||
let tasks_iter = self.tasks.values().map(ConversationOrTask::Task);
|
||||
let conversations_iter = self
|
||||
|
||||
@@ -1837,7 +1837,7 @@ impl AgentManagementView {
|
||||
}
|
||||
|
||||
if FeatureFlag::AgentHarness.is_enabled() {
|
||||
if let Some(harness) = card_data.harness() {
|
||||
if let Some(harness) = card_data.harness(app) {
|
||||
metadata_parts.push(format!(
|
||||
"Harness: {}",
|
||||
harness_display::display_name(harness)
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
use super::ambient_agent::is_cloud_agent_pre_first_exchange;
|
||||
use super::shared_session::adapter::Kind as SharedSessionKind;
|
||||
use super::{Event, PaneConfiguration, TerminalAction, TerminalViewState, Viewer};
|
||||
use crate::ai::agent::conversation::{AIConversation, ConversationStatus};
|
||||
use crate::ai::agent::conversation::{
|
||||
AIConversation, ConversationStatus, ServerAIConversationMetadata,
|
||||
};
|
||||
use crate::ai::blocklist::agent_view::agent_view_bg_fill;
|
||||
use crate::ai::blocklist::agent_view::orchestration_conversation_links::parent_conversation_navigation_card;
|
||||
use crate::ai::blocklist::agent_view::render_orchestration_breadcrumbs;
|
||||
@@ -1020,6 +1022,15 @@ impl TerminalView {
|
||||
})
|
||||
}
|
||||
|
||||
/// Server metadata for the selected conversation, if any.
|
||||
pub fn selected_conversation_server_metadata<'a>(
|
||||
&'a self,
|
||||
ctx: &'a AppContext,
|
||||
) -> Option<&'a ServerAIConversationMetadata> {
|
||||
self.selected_conversation_for_user_facing_chrome(ctx)
|
||||
.and_then(AIConversation::server_metadata)
|
||||
}
|
||||
|
||||
pub fn selected_conversation_latest_user_prompt_for_tab_name(
|
||||
&self,
|
||||
ctx: &AppContext,
|
||||
|
||||
@@ -13,7 +13,8 @@ use warpui::AppContext;
|
||||
use warpui::SingletonEntity;
|
||||
|
||||
use crate::ai::agent::conversation::ConversationStatus;
|
||||
use crate::ai::agent_conversations_model::ConversationOrTask;
|
||||
use crate::ai::agent_conversations_model::{AgentConversationsModel, ConversationOrTask};
|
||||
use crate::ai::blocklist::BlocklistAIHistoryModel;
|
||||
use crate::terminal::cli_agent_sessions::listener::agent_supports_rich_status;
|
||||
use crate::terminal::cli_agent_sessions::CLIAgentSessionsModel;
|
||||
use crate::terminal::view::TerminalView;
|
||||
@@ -24,26 +25,48 @@ use crate::ui_components::icon_with_status::IconWithStatusVariant;
|
||||
/// not an agent surface (plain terminal / shell / empty conversation).
|
||||
///
|
||||
/// Resolution order:
|
||||
/// 1. A [`CLIAgentSessionsModel`] session with a known agent (observed reality) wins.
|
||||
/// Plugin-backed sessions surface rich status; command-detected sessions don't.
|
||||
/// 2. An ambient agent with a selected third-party harness uses the harness's CLI brand
|
||||
/// even before the harness CLI has started running in the sandbox.
|
||||
/// 3. A selected conversation or ambient Oz run falls back to the Oz agent variant.
|
||||
/// 1. A [`CLIAgentSessionsModel`] session with a known agent wins. Plugin-backed sessions
|
||||
/// surface rich status; command-detected sessions don't.
|
||||
/// 2. A task-backed run defers to [`conversation_or_task_agent_icon_variant`] so the
|
||||
/// terminal chrome and the matching conversation list card stay in lockstep.
|
||||
/// 3. Live ambient pre-dispatch or a selected local conversation falls through to the
|
||||
/// no-task waterfall.
|
||||
/// 4. Everything else returns `None` so the caller renders a plain-terminal indicator.
|
||||
pub(crate) fn terminal_view_agent_icon_variant(
|
||||
terminal_view: &TerminalView,
|
||||
app: &AppContext,
|
||||
) -> Option<IconWithStatusVariant> {
|
||||
let cli_agent_session = CLIAgentSessionsModel::as_ref(app).session(terminal_view.id());
|
||||
|
||||
// Resolve the ambient task id from [`TerminalView::ambient_agent_task_id_for_details_panel`],
|
||||
// falling back to the selected conversation's server metadata for restored cloud transcripts.
|
||||
let ambient_task_id = terminal_view
|
||||
.ambient_agent_task_id_for_details_panel(app)
|
||||
.or_else(|| {
|
||||
terminal_view
|
||||
.selected_conversation_server_metadata(app)
|
||||
.and_then(|m| m.ambient_agent_task_id)
|
||||
});
|
||||
let task_data = ambient_task_id
|
||||
.and_then(|task_id| AgentConversationsModel::as_ref(app).get_task_data(&task_id));
|
||||
|
||||
// Defer to the card helper when we have task data and no CLI session takes precedence.
|
||||
if cli_agent_session.is_none() {
|
||||
if let Some(task) = task_data.as_ref() {
|
||||
return conversation_or_task_agent_icon_variant(&ConversationOrTask::Task(task), app);
|
||||
}
|
||||
}
|
||||
|
||||
let is_ambient = terminal_view.is_ambient_agent_session(app) || ambient_task_id.is_some();
|
||||
let inputs = TerminalIconInputs {
|
||||
is_ambient: terminal_view.is_ambient_agent_session(app),
|
||||
is_ambient,
|
||||
cli_session: cli_agent_session.map(|session| CLISessionInputs {
|
||||
agent: session.agent,
|
||||
has_listener: session.listener.is_some(),
|
||||
status: session.status.to_conversation_status(),
|
||||
supports_rich_status: agent_supports_rich_status(&session.agent),
|
||||
}),
|
||||
ambient_selected_third_party_cli_agent: terminal_view
|
||||
selected_third_party_cli_agent: terminal_view
|
||||
.ambient_agent_view_model()
|
||||
.and_then(|model| model.as_ref(app).selected_third_party_cli_agent()),
|
||||
selected_conversation_status: terminal_view.selected_conversation_status_for_display(app),
|
||||
@@ -56,33 +79,30 @@ pub(crate) fn terminal_view_agent_icon_variant(
|
||||
|
||||
/// Returns the agent-icon variant for a [`ConversationOrTask`] card row.
|
||||
///
|
||||
/// Task rows resolve their harness from [`ConversationOrTask::harness`]; conversation
|
||||
/// rows have no harness signal and always render as local Oz per the product spec.
|
||||
/// Both tasks and conversations resolve their harness through [`ConversationOrTask::harness`].
|
||||
pub(crate) fn conversation_or_task_agent_icon_variant(
|
||||
src: &ConversationOrTask<'_>,
|
||||
app: &AppContext,
|
||||
) -> Option<IconWithStatusVariant> {
|
||||
let status = src.status(app);
|
||||
Some(match src {
|
||||
ConversationOrTask::Task(_) => {
|
||||
agent_icon_variant_for_task(src.harness().unwrap_or(Harness::Oz), status)
|
||||
}
|
||||
ConversationOrTask::Conversation(_) => IconWithStatusVariant::OzAgent {
|
||||
status: Some(status),
|
||||
is_ambient: false,
|
||||
},
|
||||
})
|
||||
let harness = src.harness(app).unwrap_or(Harness::Oz);
|
||||
let is_ambient = match src {
|
||||
ConversationOrTask::Task(_) => true,
|
||||
ConversationOrTask::Conversation(metadata) => BlocklistAIHistoryModel::as_ref(app)
|
||||
.get_server_conversation_metadata(&metadata.nav_data.id)
|
||||
.is_some_and(|m| m.ambient_agent_task_id.is_some()),
|
||||
};
|
||||
Some(agent_icon_variant_for_run(harness, status, is_ambient))
|
||||
}
|
||||
|
||||
/// Primitive inputs to the terminal-view waterfall, gathered once from the live
|
||||
/// [`TerminalView`] / [`AppContext`]. Keeping the decision logic in terms of these
|
||||
/// primitives makes it testable without a live app.
|
||||
/// [`TerminalView`] / [`AppContext`].
|
||||
struct TerminalIconInputs {
|
||||
is_ambient: bool,
|
||||
cli_session: Option<CLISessionInputs>,
|
||||
/// The CLI agent corresponding to the currently selected cloud harness, when the selection
|
||||
/// is a third-party (non-Oz) harness. `None` for Oz or when no harness is selected.
|
||||
ambient_selected_third_party_cli_agent: Option<CLIAgent>,
|
||||
/// Third-party CLI agent for a live ambient run before task data is available (e.g.
|
||||
/// Claude pre-dispatch). `None` otherwise; task-derived harnesses are handled upstream.
|
||||
selected_third_party_cli_agent: Option<CLIAgent>,
|
||||
/// The conversation status that the terminal view would surface in its status-icon slot.
|
||||
selected_conversation_status: Option<ConversationStatus>,
|
||||
/// Whether the terminal view currently has a selected conversation (ambient or local).
|
||||
@@ -122,13 +142,12 @@ fn agent_icon_variant_from_terminal_inputs(
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Ambient agent with a selected third-party harness. Render the harness's brand
|
||||
// circle immediately once the user commits, even before the harness CLI starts
|
||||
// running in the sandbox. `Unknown` is filtered to avoid rendering an unbranded
|
||||
// gray circle for a harness this client doesn't recognize.
|
||||
// 2. Live ambient run with a third-party harness selected, before task data is
|
||||
// available (e.g. Claude pre-dispatch). `Unknown` is filtered so an unrecognized
|
||||
// harness doesn't render as an unbranded gray circle.
|
||||
if inputs.is_ambient {
|
||||
if let Some(agent) = inputs
|
||||
.ambient_selected_third_party_cli_agent
|
||||
.selected_third_party_cli_agent
|
||||
.filter(|agent| !matches!(agent, CLIAgent::Unknown))
|
||||
{
|
||||
return Some(IconWithStatusVariant::CLIAgent {
|
||||
@@ -150,13 +169,14 @@ fn agent_icon_variant_from_terminal_inputs(
|
||||
None
|
||||
}
|
||||
|
||||
/// Pure task-card logic: maps a [`Harness`] and the task's current status into an
|
||||
/// [`IconWithStatusVariant`]. Task cards are always ambient. Falls back to the Oz
|
||||
/// variant for [`Harness::Oz`] and [`Harness::Unknown`], the latter so a future-server
|
||||
/// harness this client doesn't recognize doesn't render an unbranded gray circle.
|
||||
fn agent_icon_variant_for_task(
|
||||
/// Pure run-card logic: maps a [`Harness`], status, and ambient flag into an
|
||||
/// [`IconWithStatusVariant`]. Falls back to the Oz variant for [`Harness::Oz`] and
|
||||
/// [`Harness::Unknown`], the latter so a future-server harness this client doesn't
|
||||
/// recognize doesn't render an unbranded gray circle.
|
||||
fn agent_icon_variant_for_run(
|
||||
harness: Harness,
|
||||
status: ConversationStatus,
|
||||
is_ambient: bool,
|
||||
) -> IconWithStatusVariant {
|
||||
let cli_agent =
|
||||
CLIAgent::from_harness(harness).filter(|agent| !matches!(agent, CLIAgent::Unknown));
|
||||
@@ -164,11 +184,11 @@ fn agent_icon_variant_for_task(
|
||||
Some(agent) => IconWithStatusVariant::CLIAgent {
|
||||
agent,
|
||||
status: Some(status),
|
||||
is_ambient: true,
|
||||
is_ambient,
|
||||
},
|
||||
None => IconWithStatusVariant::OzAgent {
|
||||
status: Some(status),
|
||||
is_ambient: true,
|
||||
is_ambient,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
//! the same [`IconWithStatusVariant`]. Surfaces today are:
|
||||
//! - Terminal view (vertical tabs + pane header) via
|
||||
//! [`super::agent_icon_variant_from_terminal_inputs`]
|
||||
//! - Task cards (conversation list) via [`super::agent_icon_variant_for_task`]
|
||||
//! - Run cards (conversation list, agent management view) via
|
||||
//! [`super::agent_icon_variant_for_run`]
|
||||
//! - Notification mailbox — exercised in `notifications/item_tests.rs`
|
||||
//!
|
||||
//! Adding a new canonical state is a one-enum-variant + one `expected` arm + one `*_inputs`
|
||||
@@ -12,7 +13,7 @@
|
||||
use warp_cli::agent::Harness;
|
||||
|
||||
use super::{
|
||||
agent_icon_variant_for_task, agent_icon_variant_from_terminal_inputs, CLISessionInputs,
|
||||
agent_icon_variant_for_run, agent_icon_variant_from_terminal_inputs, CLISessionInputs,
|
||||
TerminalIconInputs,
|
||||
};
|
||||
use crate::ai::agent::conversation::ConversationStatus;
|
||||
@@ -71,6 +72,10 @@ enum CanonicalRunState {
|
||||
CloudClaudePreDispatch,
|
||||
/// Cloud Claude harness selected, dispatch in flight (status = InProgress, no session).
|
||||
CloudClaudeInProgress,
|
||||
/// Viewing a finished cloud Codex transcript whose VM has shut down. No live ambient
|
||||
/// model exists, so the harness comes from the conversation's server metadata; the icon
|
||||
/// must still render as cloud Codex.
|
||||
ViewingCloudCodexTranscript,
|
||||
/// Local Claude CLI session with a plugin listener (rich status), in-progress.
|
||||
LocalClaudePluginInProgress,
|
||||
/// Local Claude CLI session with a plugin listener (rich status), blocked.
|
||||
@@ -88,6 +93,7 @@ impl CanonicalRunState {
|
||||
CloudOzInProgress,
|
||||
CloudClaudePreDispatch,
|
||||
CloudClaudeInProgress,
|
||||
ViewingCloudCodexTranscript,
|
||||
LocalClaudePluginInProgress,
|
||||
LocalClaudePluginBlocked,
|
||||
LocalClaudeCommandDetected,
|
||||
@@ -124,6 +130,12 @@ impl CanonicalRunState {
|
||||
status: Some(ConversationStatus::InProgress),
|
||||
is_ambient: true,
|
||||
}),
|
||||
ViewingCloudCodexTranscript => Some(AgentIconFields {
|
||||
is_cli: true,
|
||||
cli_agent: Some(CLIAgent::Codex),
|
||||
status: Some(ConversationStatus::Success),
|
||||
is_ambient: true,
|
||||
}),
|
||||
LocalClaudePluginInProgress => Some(AgentIconFields {
|
||||
is_cli: true,
|
||||
cli_agent: Some(CLIAgent::Claude),
|
||||
@@ -154,38 +166,47 @@ impl CanonicalRunState {
|
||||
PlainTerminal => TerminalIconInputs {
|
||||
is_ambient: false,
|
||||
cli_session: None,
|
||||
ambient_selected_third_party_cli_agent: None,
|
||||
selected_third_party_cli_agent: None,
|
||||
selected_conversation_status: None,
|
||||
has_selected_conversation: false,
|
||||
},
|
||||
LocalOzInProgress => TerminalIconInputs {
|
||||
is_ambient: false,
|
||||
cli_session: None,
|
||||
ambient_selected_third_party_cli_agent: None,
|
||||
selected_third_party_cli_agent: None,
|
||||
selected_conversation_status: Some(ConversationStatus::InProgress),
|
||||
has_selected_conversation: true,
|
||||
},
|
||||
CloudOzInProgress => TerminalIconInputs {
|
||||
is_ambient: true,
|
||||
cli_session: None,
|
||||
ambient_selected_third_party_cli_agent: None,
|
||||
selected_third_party_cli_agent: None,
|
||||
selected_conversation_status: Some(ConversationStatus::InProgress),
|
||||
has_selected_conversation: false,
|
||||
},
|
||||
CloudClaudePreDispatch => TerminalIconInputs {
|
||||
is_ambient: true,
|
||||
cli_session: None,
|
||||
ambient_selected_third_party_cli_agent: Some(CLIAgent::Claude),
|
||||
selected_third_party_cli_agent: Some(CLIAgent::Claude),
|
||||
selected_conversation_status: None,
|
||||
has_selected_conversation: false,
|
||||
},
|
||||
CloudClaudeInProgress => TerminalIconInputs {
|
||||
is_ambient: true,
|
||||
cli_session: None,
|
||||
ambient_selected_third_party_cli_agent: Some(CLIAgent::Claude),
|
||||
selected_third_party_cli_agent: Some(CLIAgent::Claude),
|
||||
selected_conversation_status: Some(ConversationStatus::InProgress),
|
||||
has_selected_conversation: false,
|
||||
},
|
||||
ViewingCloudCodexTranscript => TerminalIconInputs {
|
||||
// VM has shut down: the caller resolves these fields from the conversation's
|
||||
// server metadata, so the waterfall sees the same shape as a live run.
|
||||
is_ambient: true,
|
||||
cli_session: None,
|
||||
selected_third_party_cli_agent: Some(CLIAgent::Codex),
|
||||
selected_conversation_status: Some(ConversationStatus::Success),
|
||||
has_selected_conversation: true,
|
||||
},
|
||||
LocalClaudePluginInProgress => TerminalIconInputs {
|
||||
is_ambient: false,
|
||||
cli_session: Some(CLISessionInputs {
|
||||
@@ -194,7 +215,7 @@ impl CanonicalRunState {
|
||||
status: ConversationStatus::InProgress,
|
||||
supports_rich_status: true,
|
||||
}),
|
||||
ambient_selected_third_party_cli_agent: None,
|
||||
selected_third_party_cli_agent: None,
|
||||
selected_conversation_status: None,
|
||||
has_selected_conversation: false,
|
||||
},
|
||||
@@ -208,7 +229,7 @@ impl CanonicalRunState {
|
||||
},
|
||||
supports_rich_status: true,
|
||||
}),
|
||||
ambient_selected_third_party_cli_agent: None,
|
||||
selected_third_party_cli_agent: None,
|
||||
selected_conversation_status: None,
|
||||
has_selected_conversation: false,
|
||||
},
|
||||
@@ -220,21 +241,24 @@ impl CanonicalRunState {
|
||||
status: ConversationStatus::InProgress,
|
||||
supports_rich_status: false,
|
||||
}),
|
||||
ambient_selected_third_party_cli_agent: None,
|
||||
selected_third_party_cli_agent: None,
|
||||
selected_conversation_status: None,
|
||||
has_selected_conversation: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Task-card inputs for this state, if it can surface as a task card.
|
||||
/// Run-card inputs for this state, if it can surface as a run card.
|
||||
/// Cards only exist for cloud/ambient runs; local states return `None`.
|
||||
fn task_inputs(&self) -> Option<(Harness, ConversationStatus)> {
|
||||
fn run_inputs(&self) -> Option<(Harness, ConversationStatus, bool)> {
|
||||
use CanonicalRunState::*;
|
||||
match self {
|
||||
CloudOzInProgress => Some((Harness::Oz, ConversationStatus::InProgress)),
|
||||
CloudOzInProgress => Some((Harness::Oz, ConversationStatus::InProgress, true)),
|
||||
CloudClaudePreDispatch | CloudClaudeInProgress => {
|
||||
Some((Harness::Claude, ConversationStatus::InProgress))
|
||||
Some((Harness::Claude, ConversationStatus::InProgress, true))
|
||||
}
|
||||
ViewingCloudCodexTranscript => {
|
||||
Some((Harness::Codex, ConversationStatus::Success, true))
|
||||
}
|
||||
PlainTerminal
|
||||
| LocalOzInProgress
|
||||
@@ -260,17 +284,17 @@ fn every_canonical_state_produces_consistent_icon_across_surfaces() {
|
||||
"terminal surface disagreed for {state:?}"
|
||||
);
|
||||
|
||||
if let Some((harness, status)) = state.task_inputs() {
|
||||
let task_variant = agent_icon_variant_for_task(harness, status.clone());
|
||||
let task_actual = AgentIconFields::from_variant(&task_variant);
|
||||
// Task cards always populate status (they derive it from `ConversationOrTask::status`).
|
||||
let expected_for_task = expected.clone().map(|mut fields| {
|
||||
if let Some((harness, status, is_ambient)) = state.run_inputs() {
|
||||
let run_variant = agent_icon_variant_for_run(harness, status.clone(), is_ambient);
|
||||
let run_actual = AgentIconFields::from_variant(&run_variant);
|
||||
// Run cards always populate status (they derive it from `ConversationOrTask::status`).
|
||||
let expected_for_run = expected.clone().map(|mut fields| {
|
||||
fields.status = Some(status);
|
||||
fields
|
||||
});
|
||||
assert_eq!(
|
||||
task_actual, expected_for_task,
|
||||
"task surface disagreed for {state:?}"
|
||||
run_actual, expected_for_run,
|
||||
"run-card surface disagreed for {state:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -312,16 +336,16 @@ fn cli_agent_from_harness_maps_known_harnesses() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn task_with_oz_or_unknown_harness_renders_as_oz() {
|
||||
fn run_card_with_oz_or_unknown_harness_renders_as_oz() {
|
||||
// Oz harness explicitly: local Oz is the spec-defined fallback.
|
||||
let variant = agent_icon_variant_for_task(Harness::Oz, ConversationStatus::Success);
|
||||
let variant = agent_icon_variant_for_run(Harness::Oz, ConversationStatus::Success, true);
|
||||
let fields = AgentIconFields::from_variant(&variant).unwrap();
|
||||
assert!(!fields.is_cli);
|
||||
assert!(fields.is_ambient);
|
||||
|
||||
// Unknown harness (e.g. server surfaced a future variant): also falls back to Oz so we
|
||||
// don't render an unbranded gray circle.
|
||||
let variant = agent_icon_variant_for_task(Harness::Unknown, ConversationStatus::Success);
|
||||
let variant = agent_icon_variant_for_run(Harness::Unknown, ConversationStatus::Success, true);
|
||||
let fields = AgentIconFields::from_variant(&variant).unwrap();
|
||||
assert!(!fields.is_cli);
|
||||
assert!(fields.is_ambient);
|
||||
|
||||
Reference in New Issue
Block a user